Class: VibeZstd::DCtx

Inherits:
Object
  • Object
show all
Defined in:
ext/vibe_zstd/vibe_zstd.c

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.default_initial_capacityObject

DCtx default_initial_capacity getter (class method)



165
166
167
168
169
170
171
# File 'ext/vibe_zstd/dctx.c', line 165

static VALUE
vibe_zstd_dctx_get_default_initial_capacity(VALUE self) {
    if (default_initial_capacity == 0) {
        return SIZET2NUM(ZSTD_DStreamOutSize());
    }
    return SIZET2NUM(default_initial_capacity);
}

.default_initial_capacity=(value) ⇒ Object

DCtx default_initial_capacity setter (class method)



174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'ext/vibe_zstd/dctx.c', line 174

static VALUE
vibe_zstd_dctx_set_default_initial_capacity(VALUE self, VALUE value) {
    if (NIL_P(value)) {
        default_initial_capacity = 0;  // Reset to default
    } else {
        size_t capacity = NUM2SIZET(value);
        if (capacity == 0) {
            rb_raise(rb_eArgError, "initial_capacity must be positive (or nil to reset to default)");
        }
        default_initial_capacity = capacity;
    }
    return value;
}

.estimate_memoryObject

DCtx.estimate_memory()



41
42
43
44
45
# File 'ext/vibe_zstd/dctx.c', line 41

static VALUE
vibe_zstd_dctx_estimate_memory(VALUE self) {
    size_t estimate = ZSTD_estimateDCtxSize();
    return SIZET2NUM(estimate);
}

.frame_content_size(data) ⇒ Object

DCtx frame_content_size - class method to get frame content size



248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'ext/vibe_zstd/dctx.c', line 248

static VALUE
vibe_zstd_dctx_frame_content_size(VALUE self, VALUE data) {
    StringValue(data);
    unsigned long long contentSize = ZSTD_getFrameContentSize(RSTRING_PTR(data), RSTRING_LEN(data));

    if (contentSize == ZSTD_CONTENTSIZE_ERROR) {
        return Qnil;  // Invalid frame
    }

    if (contentSize == ZSTD_CONTENTSIZE_UNKNOWN) {
        return Qnil;  // Unknown size
    }

    return ULL2NUM(contentSize);
}

.parameter_boundsObject

Instance Method Details

#decompress(*args) ⇒ Object

Skippable frames at the beginning of data are automatically skipped.



276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
# File 'ext/vibe_zstd/dctx.c', line 276

static VALUE
vibe_zstd_dctx_decompress(int argc, VALUE* argv, VALUE self) {
    VALUE data, options = Qnil;
    rb_scan_args(argc, argv, "1:", &data, &options);
    vibe_zstd_dctx* dctx;
    TypedData_Get_Struct(self, vibe_zstd_dctx, &vibe_zstd_dctx_type, dctx);
    StringValue(data);
    const char* src = RSTRING_PTR(data);
    size_t srcSize = RSTRING_LEN(data);
    size_t offset = 0;

    // Skip any leading skippable frames
    while (offset < srcSize && ZSTD_isSkippableFrame(src + offset, srcSize - offset)) {
        size_t frameSize = ZSTD_findFrameCompressedSize(src + offset, srcSize - offset);
        if (ZSTD_isError(frameSize)) {
            rb_raise(rb_eRuntimeError, "Invalid skippable frame at offset %zu: %s", offset, ZSTD_getErrorName(frameSize));
        }
        offset += frameSize;
    }

    // Now check the actual compressed frame
    if (offset >= srcSize) {
        rb_raise(rb_eRuntimeError, "No compressed frame found in %zu bytes (only skippable frames)", srcSize);
    }

    src += offset;
    srcSize -= offset;

    unsigned long long contentSize = ZSTD_getFrameContentSize(src, srcSize);
    if (contentSize == ZSTD_CONTENTSIZE_ERROR) {
        rb_raise(rb_eRuntimeError, "Invalid compressed data: not a valid zstd frame (size: %zu bytes)", srcSize);
    }

    // Check dictionary requirements from the frame
    unsigned int frame_dict_id = ZSTD_getDictID_fromFrame(src, srcSize);

    // Extract keyword arguments
    ZSTD_DDict* ddict = NULL;
    unsigned int provided_dict_id = 0;
    size_t initial_capacity = 0;  // 0 = not specified in per-call options

    if (!NIL_P(options)) {
        VALUE dict_val = rb_hash_aref(options, ID2SYM(rb_intern("dict")));
        if (!NIL_P(dict_val)) {
            vibe_zstd_ddict* ddict_struct;
            TypedData_Get_Struct(dict_val, vibe_zstd_ddict, &vibe_zstd_ddict_type, ddict_struct);
            ddict = ddict_struct->ddict;
            provided_dict_id = ZSTD_getDictID_fromDDict(ddict);
        }

        VALUE initial_capacity_val = rb_hash_aref(options, ID2SYM(rb_intern("initial_capacity")));
        if (!NIL_P(initial_capacity_val)) {
            initial_capacity = NUM2SIZET(initial_capacity_val);
            if (initial_capacity == 0) {
                rb_raise(rb_eArgError, "initial_capacity must be positive");
            }
        }
    }

    // Validate dictionary matches frame requirements
    if (frame_dict_id != 0 && ddict == NULL) {
        rb_raise(rb_eArgError, "Data requires dictionary (dict_id: %u) but none provided", frame_dict_id);
    }

    if (ddict != NULL && frame_dict_id != 0 && provided_dict_id != frame_dict_id) {
        rb_raise(rb_eArgError, "Dictionary mismatch: frame requires dict_id %u, provided dict_id %u",
                 frame_dict_id, provided_dict_id);
    }

    // Resolve initial_capacity fallback chain: per-call > instance > class default > ZSTD default
    if (initial_capacity == 0) {
        initial_capacity = dctx->initial_capacity;  // Instance default
        if (initial_capacity == 0) {
            initial_capacity = default_initial_capacity;  // Class default
            if (initial_capacity == 0) {
                initial_capacity = ZSTD_DStreamOutSize();  // ZSTD default (~128KB)
            }
        }
    }

    // If content size is unknown, use streaming decompression with exponential growth
    if (contentSize == ZSTD_CONTENTSIZE_UNKNOWN) {
        size_t chunk_size = ZSTD_DStreamOutSize();  // Fixed chunk buffer size
        VALUE tmpBuffer = rb_str_buf_new(chunk_size);

        // Start with configured initial capacity
        size_t result_capacity = initial_capacity;
        size_t result_size = 0;
        VALUE result = rb_str_buf_new(result_capacity);

        ZSTD_inBuffer input = { src, srcSize, 0 };

        while (input.pos < input.size) {
            ZSTD_outBuffer output = { RSTRING_PTR(tmpBuffer), chunk_size, 0 };

            size_t ret = ZSTD_decompressStream(dctx->dctx, &output, &input);
            if (ZSTD_isError(ret)) {
                rb_raise(rb_eRuntimeError, "Decompression failed: %s", ZSTD_getErrorName(ret));
            }

            if (output.pos > 0) {
                // Grow result buffer exponentially if needed
                if (result_size + output.pos > result_capacity) {
                    // Double capacity until it fits
                    while (result_capacity < result_size + output.pos) {
                        result_capacity *= 2;
                    }
                    rb_str_resize(result, result_capacity);
                }

                // Copy directly into result buffer
                memcpy(RSTRING_PTR(result) + result_size, RSTRING_PTR(tmpBuffer), output.pos);
                result_size += output.pos;
            }
        }

        // Trim to actual size
        rb_str_resize(result, result_size);
        return result;
    }
    VALUE result = rb_str_new(NULL, contentSize);
    decompress_args args = {
        .dctx = dctx->dctx,
        .ddict = ddict,
        .src = src,
        .srcSize = srcSize,
        .dst = RSTRING_PTR(result),
        .dstCapacity = contentSize,
        .result = 0
    };
    rb_thread_call_without_gvl(decompress_without_gvl, &args, NULL, NULL);
    if (ZSTD_isError(args.result)) {
        rb_raise(rb_eRuntimeError, "Decompression failed: %s", ZSTD_getErrorName(args.result));
    }
    rb_str_set_len(result, args.result);
    return result;
}

#initial_capacityObject

DCtx initial_capacity getter (instance method)



189
190
191
192
193
194
195
196
197
198
199
# File 'ext/vibe_zstd/dctx.c', line 189

static VALUE
vibe_zstd_dctx_get_initial_capacity(VALUE self) {
    vibe_zstd_dctx* dctx;
    TypedData_Get_Struct(self, vibe_zstd_dctx, &vibe_zstd_dctx_type, dctx);

    if (dctx->initial_capacity == 0) {
        // Return the class default
        return vibe_zstd_dctx_get_default_initial_capacity(Qnil);
    }
    return SIZET2NUM(dctx->initial_capacity);
}

#initial_capacity=(value) ⇒ Object

DCtx initial_capacity setter (instance method)



202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'ext/vibe_zstd/dctx.c', line 202

static VALUE
vibe_zstd_dctx_set_initial_capacity(VALUE self, VALUE value) {
    vibe_zstd_dctx* dctx;
    TypedData_Get_Struct(self, vibe_zstd_dctx, &vibe_zstd_dctx_type, dctx);

    if (NIL_P(value)) {
        dctx->initial_capacity = 0;  // Use class default
    } else {
        size_t capacity = NUM2SIZET(value);
        if (capacity == 0) {
            rb_raise(rb_eArgError, "initial_capacity must be positive (or nil to use class default)");
        }
        dctx->initial_capacity = capacity;
    }
    return value;
}

#reset(*args) ⇒ Object

DCtx reset - reset context to clean state



432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
# File 'ext/vibe_zstd/dctx.c', line 432

static VALUE
vibe_zstd_dctx_reset(int argc, VALUE* argv, VALUE self) {
    VALUE reset_mode;
    rb_scan_args(argc, argv, "01", &reset_mode);

    vibe_zstd_dctx* dctx;
    TypedData_Get_Struct(self, vibe_zstd_dctx, &vibe_zstd_dctx_type, dctx);

    // Default to SESSION_AND_PARAMETERS if no argument provided
    ZSTD_ResetDirective directive = ZSTD_reset_session_and_parameters;

    if (!NIL_P(reset_mode)) {
        int mode = NUM2INT(reset_mode);
        if (mode == ZSTD_reset_session_only) {
            directive = ZSTD_reset_session_only;
        } else if (mode == ZSTD_reset_parameters) {
            directive = ZSTD_reset_parameters;
        } else if (mode == ZSTD_reset_session_and_parameters) {
            directive = ZSTD_reset_session_and_parameters;
        } else {
            rb_raise(rb_eArgError, "Invalid reset_mode %d: must be ResetDirective::SESSION (1), PARAMETERS (2), or BOTH (3)", mode);
        }
    }

    size_t result = ZSTD_DCtx_reset(dctx->dctx, directive);

    if (ZSTD_isError(result)) {
        rb_raise(rb_eRuntimeError, "Failed to reset decompression context: %s", ZSTD_getErrorName(result));
    }

    return self;
}

#use_prefix(prefix_data) ⇒ Object

DCtx use_prefix - use raw data as prefix (lightweight dictionary)



415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
# File 'ext/vibe_zstd/dctx.c', line 415

static VALUE
vibe_zstd_dctx_use_prefix(VALUE self, VALUE prefix_data) {
    vibe_zstd_dctx* dctx;
    TypedData_Get_Struct(self, vibe_zstd_dctx, &vibe_zstd_dctx_type, dctx);

    StringValue(prefix_data);

    size_t result = ZSTD_DCtx_refPrefix(dctx->dctx, RSTRING_PTR(prefix_data), RSTRING_LEN(prefix_data));

    if (ZSTD_isError(result)) {
        rb_raise(rb_eRuntimeError, "Failed to set prefix: %s", ZSTD_getErrorName(result));
    }

    return self;
}

#window_log_maxObject Also known as: max_window_log

#window_log_max=Object Also known as: max_window_log=

DCtx parameter accessors