Class: VibeZstd::CCtx

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

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.estimate_memory(level) ⇒ Object

CCtx.estimate_memory(level)



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

static VALUE
vibe_zstd_cctx_estimate_memory(VALUE self, VALUE level) {
    int lvl = NUM2INT(level);
    size_t estimate = ZSTD_estimateCCtxSize(lvl);
    return SIZET2NUM(estimate);
}

.parameter_boundsObject

Instance Method Details

#block_delimitersObject Also known as: block_delimiters?

#block_delimiters=Object

#chain_logObject

#chain_log=Object

#checksum_flagObject Also known as: checksum, checksum?

#checksum_flag=Object Also known as: checksum=

#compress(*args) ⇒ Object

Releases GVL during compression to allow other Ruby threads to run.



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'ext/vibe_zstd/cctx.c', line 91

static VALUE
vibe_zstd_cctx_compress(int argc, VALUE* argv, VALUE self) {
    VALUE data, options = Qnil;
    rb_scan_args(argc, argv, "1:", &data, &options);
    vibe_zstd_cctx* cctx;
    TypedData_Get_Struct(self, vibe_zstd_cctx, &vibe_zstd_cctx_type, cctx);
    StringValue(data);

    // Extract keyword arguments (all optional, all per-call overrides)
    int has_level = 0;
    int lvl = 0;
    ZSTD_CDict* cdict = NULL;
    int has_pledged = 0;
    unsigned long long pledged_size = ZSTD_CONTENTSIZE_UNKNOWN;

    if (!NIL_P(options)) {
        VALUE level_val = rb_hash_aref(options, ID2SYM(rb_intern("level")));
        if (!NIL_P(level_val)) {
            has_level = 1;
            lvl = NUM2INT(level_val);
        }

        VALUE dict_val = rb_hash_aref(options, ID2SYM(rb_intern("dict")));
        if (!NIL_P(dict_val)) {
            vibe_zstd_cdict* cdict_struct;
            TypedData_Get_Struct(dict_val, vibe_zstd_cdict, &vibe_zstd_cdict_type, cdict_struct);
            cdict = cdict_struct->cdict;
        }

        VALUE pledged_size_val = rb_hash_aref(options, ID2SYM(rb_intern("pledged_size")));
        if (!NIL_P(pledged_size_val)) {
            has_pledged = 1;
            pledged_size = NUM2ULL(pledged_size_val);
        }
    }

    // Apply per-call compression level override without permanently mutating the
    // context's configured level. The previous value is captured and restored.
    int prev_level = 0;
    if (has_level) {
        size_t gp = ZSTD_CCtx_getParameter(cctx->cctx, ZSTD_c_compressionLevel, &prev_level);
        if (ZSTD_isError(gp)) {
            rb_raise(rb_eRuntimeError, "Failed to read compression level: %s", ZSTD_getErrorName(gp));
        }
        size_t sp = ZSTD_CCtx_setParameter(cctx->cctx, ZSTD_c_compressionLevel, lvl);
        if (ZSTD_isError(sp)) {
            rb_raise(rb_eArgError, "Invalid level %d: %s", lvl, ZSTD_getErrorName(sp));
        }
    }

    // Reference a per-call dictionary; un-referenced after compression so the
    // context returns to no-dictionary mode for subsequent calls.
    if (cdict) {
        size_t rc = ZSTD_CCtx_refCDict(cctx->cctx, cdict);
        if (ZSTD_isError(rc)) {
            if (has_level) ZSTD_CCtx_setParameter(cctx->cctx, ZSTD_c_compressionLevel, prev_level);
            rb_raise(rb_eRuntimeError, "Failed to set dictionary: %s", ZSTD_getErrorName(rc));
        }
    }

    // Set pledged size if provided (resets to UNKNOWN automatically after the frame)
    if (has_pledged) {
        size_t sps = ZSTD_CCtx_setPledgedSrcSize(cctx->cctx, pledged_size);
        if (ZSTD_isError(sps)) {
            if (cdict) ZSTD_CCtx_refCDict(cctx->cctx, NULL);
            if (has_level) ZSTD_CCtx_setParameter(cctx->cctx, ZSTD_c_compressionLevel, prev_level);
            rb_raise(rb_eRuntimeError, "Failed to set pledged_size %llu: %s", pledged_size, ZSTD_getErrorName(sps));
        }
    }

    size_t srcSize = RSTRING_LEN(data);
    size_t dstCapacity = ZSTD_compressBound(srcSize);
    VALUE result_str = rb_str_new(NULL, dstCapacity);
    compress_args args = {
        .cctx = cctx->cctx,
        .src = RSTRING_PTR(data),
        .srcSize = srcSize,
        .dst = RSTRING_PTR(result_str),
        .dstCapacity = dstCapacity,
        .result = 0
    };
    // Lock the source string for the duration of the GVL-released compression.
    // Without this, another Ruby thread holding the same String object could
    // modify or reallocate it while compression reads from its buffer, causing
    // a use-after-free read.  The helper unlocks via rb_ensure so the string
    // is never left permanently locked, even if an async exception (e.g.
    // Timeout, Thread#raise) is delivered when the GVL is reacquired.
    vibe_zstd_nogvl_with_str_locked(compress_without_gvl, &args, data);

    // Restore context state so repeated one-shot calls remain independent.
    if (cdict) ZSTD_CCtx_refCDict(cctx->cctx, NULL);
    if (has_level) ZSTD_CCtx_setParameter(cctx->cctx, ZSTD_c_compressionLevel, prev_level);

    if (ZSTD_isError(args.result)) {
        rb_raise(rb_eRuntimeError, "Compression failed: %s", ZSTD_getErrorName(args.result));
    }
    rb_str_set_len(result_str, args.result);
    return result_str;
}

#compression_levelObject Also known as: level

#compression_level=Object Also known as: level=

CCtx parameter accessors

#content_size_flagObject Also known as: content_size, content_size?

#content_size_flag=Object Also known as: content_size=

#deterministic_ref_prefixObject Also known as: deterministic_ref_prefix?

#deterministic_ref_prefix=Object

#dict_id_flagObject Also known as: dict_id, dict_id?

#dict_id_flag=Object Also known as: dict_id=

#enable_dedicated_dict_searchObject Also known as: enable_dedicated_dict_search?

#enable_dedicated_dict_search=Object

#enable_long_distance_matchingObject Also known as: long_distance_matching, long_distance_matching?

#enable_long_distance_matching=Object Also known as: long_distance_matching=

#enable_seq_producer_fallbackObject Also known as: enable_seq_producer_fallback?

#enable_seq_producer_fallback=Object

#force_attach_dictObject

#force_attach_dict=Object

#force_max_windowObject Also known as: force_max_window?

#force_max_window=Object

#formatObject

#format=Object

#hash_logObject

#hash_log=Object

#job_sizeObject

#job_size=Object

#ldm_bucket_size_logObject

#ldm_bucket_size_log=Object

#ldm_hash_logObject

#ldm_hash_log=Object

#ldm_hash_rate_logObject

#ldm_hash_rate_log=Object

#ldm_min_matchObject

#ldm_min_match=Object

#literal_compression_modeObject

#literal_compression_mode=Object

#max_block_sizeObject

#max_block_size=Object

#min_matchObject

#min_match=Object

#overlap_logObject

#overlap_log=Object

#prefetch_cdict_tablesObject

#prefetch_cdict_tables=Object

#reset(*args) ⇒ Object

CCtx reset - reset context to clean state



461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
# File 'ext/vibe_zstd/cctx.c', line 461

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

    vibe_zstd_cctx* cctx;
    TypedData_Get_Struct(self, vibe_zstd_cctx, &vibe_zstd_cctx_type, cctx);

    // 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_CCtx_reset(cctx->cctx, directive);

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

    return self;
}

#rsyncableObject Also known as: rsyncable?

#rsyncable=Object

#search_for_external_repcodesObject

#search_for_external_repcodes=Object

#search_logObject

#search_log=Object

#src_size_hintObject

#src_size_hint=Object

#stable_in_bufferObject Also known as: stable_in_buffer?

#stable_in_buffer=Object

#stable_out_bufferObject Also known as: stable_out_buffer?

#stable_out_buffer=Object

#strategyObject

#strategy=Object

#target_cblock_sizeObject

#target_cblock_size=Object

#target_lengthObject

#target_length=Object

#use_prefix(prefix_data) ⇒ Object

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



444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
# File 'ext/vibe_zstd/cctx.c', line 444

static VALUE
vibe_zstd_cctx_use_prefix(VALUE self, VALUE prefix_data) {
    vibe_zstd_cctx* cctx;
    TypedData_Get_Struct(self, vibe_zstd_cctx, &vibe_zstd_cctx_type, cctx);

    StringValue(prefix_data);

    size_t result = ZSTD_CCtx_refPrefix(cctx->cctx, 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;
}

#use_row_match_finderObject

#use_row_match_finder=Object

#validate_sequencesObject Also known as: validate_sequences?

#validate_sequences=Object

#window_logObject

#window_log=Object

#workersObject

#workers=Object