vibe_zstd 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.standard.yml +3 -0
- data/CHANGELOG.md +22 -0
- data/LICENSE.txt +21 -0
- data/README.md +978 -0
- data/Rakefile +20 -0
- data/benchmark/README.md +198 -0
- data/benchmark/compression_levels.rb +99 -0
- data/benchmark/context_reuse.rb +174 -0
- data/benchmark/decompression_speed_by_level.rb +65 -0
- data/benchmark/dictionary_training.rb +182 -0
- data/benchmark/dictionary_usage.rb +121 -0
- data/benchmark/for_readme.rb +157 -0
- data/benchmark/generate_fixture.rb +82 -0
- data/benchmark/helpers.rb +237 -0
- data/benchmark/multithreading.rb +105 -0
- data/benchmark/run_all.rb +150 -0
- data/benchmark/streaming.rb +154 -0
- data/ext/vibe_zstd/Makefile +270 -0
- data/ext/vibe_zstd/cctx.c +565 -0
- data/ext/vibe_zstd/dctx.c +493 -0
- data/ext/vibe_zstd/dict.c +587 -0
- data/ext/vibe_zstd/extconf.rb +52 -0
- data/ext/vibe_zstd/frames.c +132 -0
- data/ext/vibe_zstd/libzstd/LICENSE +30 -0
- data/ext/vibe_zstd/libzstd/common/allocations.h +55 -0
- data/ext/vibe_zstd/libzstd/common/bits.h +205 -0
- data/ext/vibe_zstd/libzstd/common/bitstream.h +454 -0
- data/ext/vibe_zstd/libzstd/common/compiler.h +464 -0
- data/ext/vibe_zstd/libzstd/common/cpu.h +249 -0
- data/ext/vibe_zstd/libzstd/common/debug.c +30 -0
- data/ext/vibe_zstd/libzstd/common/debug.h +107 -0
- data/ext/vibe_zstd/libzstd/common/entropy_common.c +340 -0
- data/ext/vibe_zstd/libzstd/common/error_private.c +64 -0
- data/ext/vibe_zstd/libzstd/common/error_private.h +158 -0
- data/ext/vibe_zstd/libzstd/common/fse.h +625 -0
- data/ext/vibe_zstd/libzstd/common/fse_decompress.c +315 -0
- data/ext/vibe_zstd/libzstd/common/huf.h +277 -0
- data/ext/vibe_zstd/libzstd/common/mem.h +422 -0
- data/ext/vibe_zstd/libzstd/common/pool.c +371 -0
- data/ext/vibe_zstd/libzstd/common/pool.h +81 -0
- data/ext/vibe_zstd/libzstd/common/portability_macros.h +171 -0
- data/ext/vibe_zstd/libzstd/common/threading.c +182 -0
- data/ext/vibe_zstd/libzstd/common/threading.h +142 -0
- data/ext/vibe_zstd/libzstd/common/xxhash.c +18 -0
- data/ext/vibe_zstd/libzstd/common/xxhash.h +7094 -0
- data/ext/vibe_zstd/libzstd/common/zstd_common.c +48 -0
- data/ext/vibe_zstd/libzstd/common/zstd_deps.h +123 -0
- data/ext/vibe_zstd/libzstd/common/zstd_internal.h +324 -0
- data/ext/vibe_zstd/libzstd/common/zstd_trace.h +156 -0
- data/ext/vibe_zstd/libzstd/compress/clevels.h +134 -0
- data/ext/vibe_zstd/libzstd/compress/fse_compress.c +625 -0
- data/ext/vibe_zstd/libzstd/compress/hist.c +191 -0
- data/ext/vibe_zstd/libzstd/compress/hist.h +82 -0
- data/ext/vibe_zstd/libzstd/compress/huf_compress.c +1464 -0
- data/ext/vibe_zstd/libzstd/compress/zstd_compress.c +7843 -0
- data/ext/vibe_zstd/libzstd/compress/zstd_compress_internal.h +1636 -0
- data/ext/vibe_zstd/libzstd/compress/zstd_compress_literals.c +235 -0
- data/ext/vibe_zstd/libzstd/compress/zstd_compress_literals.h +39 -0
- data/ext/vibe_zstd/libzstd/compress/zstd_compress_sequences.c +442 -0
- data/ext/vibe_zstd/libzstd/compress/zstd_compress_sequences.h +55 -0
- data/ext/vibe_zstd/libzstd/compress/zstd_compress_superblock.c +688 -0
- data/ext/vibe_zstd/libzstd/compress/zstd_compress_superblock.h +32 -0
- data/ext/vibe_zstd/libzstd/compress/zstd_cwksp.h +765 -0
- data/ext/vibe_zstd/libzstd/compress/zstd_double_fast.c +778 -0
- data/ext/vibe_zstd/libzstd/compress/zstd_double_fast.h +42 -0
- data/ext/vibe_zstd/libzstd/compress/zstd_fast.c +985 -0
- data/ext/vibe_zstd/libzstd/compress/zstd_fast.h +30 -0
- data/ext/vibe_zstd/libzstd/compress/zstd_lazy.c +2199 -0
- data/ext/vibe_zstd/libzstd/compress/zstd_lazy.h +193 -0
- data/ext/vibe_zstd/libzstd/compress/zstd_ldm.c +745 -0
- data/ext/vibe_zstd/libzstd/compress/zstd_ldm.h +109 -0
- data/ext/vibe_zstd/libzstd/compress/zstd_ldm_geartab.h +106 -0
- data/ext/vibe_zstd/libzstd/compress/zstd_opt.c +1580 -0
- data/ext/vibe_zstd/libzstd/compress/zstd_opt.h +72 -0
- data/ext/vibe_zstd/libzstd/compress/zstd_preSplit.c +238 -0
- data/ext/vibe_zstd/libzstd/compress/zstd_preSplit.h +33 -0
- data/ext/vibe_zstd/libzstd/compress/zstdmt_compress.c +1923 -0
- data/ext/vibe_zstd/libzstd/compress/zstdmt_compress.h +102 -0
- data/ext/vibe_zstd/libzstd/decompress/huf_decompress.c +1944 -0
- data/ext/vibe_zstd/libzstd/decompress/huf_decompress_amd64.S +602 -0
- data/ext/vibe_zstd/libzstd/decompress/zstd_ddict.c +244 -0
- data/ext/vibe_zstd/libzstd/decompress/zstd_ddict.h +44 -0
- data/ext/vibe_zstd/libzstd/decompress/zstd_decompress.c +2410 -0
- data/ext/vibe_zstd/libzstd/decompress/zstd_decompress_block.c +2209 -0
- data/ext/vibe_zstd/libzstd/decompress/zstd_decompress_block.h +73 -0
- data/ext/vibe_zstd/libzstd/decompress/zstd_decompress_internal.h +240 -0
- data/ext/vibe_zstd/libzstd/deprecated/zbuff.h +214 -0
- data/ext/vibe_zstd/libzstd/deprecated/zbuff_common.c +26 -0
- data/ext/vibe_zstd/libzstd/deprecated/zbuff_compress.c +167 -0
- data/ext/vibe_zstd/libzstd/deprecated/zbuff_decompress.c +77 -0
- data/ext/vibe_zstd/libzstd/dictBuilder/cover.c +1302 -0
- data/ext/vibe_zstd/libzstd/dictBuilder/cover.h +152 -0
- data/ext/vibe_zstd/libzstd/dictBuilder/divsufsort.c +1913 -0
- data/ext/vibe_zstd/libzstd/dictBuilder/divsufsort.h +57 -0
- data/ext/vibe_zstd/libzstd/dictBuilder/fastcover.c +766 -0
- data/ext/vibe_zstd/libzstd/dictBuilder/zdict.c +1133 -0
- data/ext/vibe_zstd/libzstd/zdict.h +481 -0
- data/ext/vibe_zstd/libzstd/zstd.h +3198 -0
- data/ext/vibe_zstd/libzstd/zstd_errors.h +107 -0
- data/ext/vibe_zstd/streaming.c +410 -0
- data/ext/vibe_zstd/vibe_zstd.c +293 -0
- data/ext/vibe_zstd/vibe_zstd.h +56 -0
- data/ext/vibe_zstd/vibe_zstd_internal.h +27 -0
- data/lib/vibe_zstd/constants.rb +67 -0
- data/lib/vibe_zstd/version.rb +5 -0
- data/lib/vibe_zstd.rb +255 -0
- data/sig/vibe_zstd.rbs +76 -0
- metadata +179 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under both the BSD-style license (found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree) and the GPLv2 (found
|
|
7
|
+
* in the COPYING file in the root directory of this source tree).
|
|
8
|
+
* You may select, at your option, one of the above-listed licenses.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
#ifndef ZSTD_ERRORS_H_398273423
|
|
12
|
+
#define ZSTD_ERRORS_H_398273423
|
|
13
|
+
|
|
14
|
+
#if defined (__cplusplus)
|
|
15
|
+
extern "C" {
|
|
16
|
+
#endif
|
|
17
|
+
|
|
18
|
+
/* ===== ZSTDERRORLIB_API : control library symbols visibility ===== */
|
|
19
|
+
#ifndef ZSTDERRORLIB_VISIBLE
|
|
20
|
+
/* Backwards compatibility with old macro name */
|
|
21
|
+
# ifdef ZSTDERRORLIB_VISIBILITY
|
|
22
|
+
# define ZSTDERRORLIB_VISIBLE ZSTDERRORLIB_VISIBILITY
|
|
23
|
+
# elif defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__)
|
|
24
|
+
# define ZSTDERRORLIB_VISIBLE __attribute__ ((visibility ("default")))
|
|
25
|
+
# else
|
|
26
|
+
# define ZSTDERRORLIB_VISIBLE
|
|
27
|
+
# endif
|
|
28
|
+
#endif
|
|
29
|
+
|
|
30
|
+
#ifndef ZSTDERRORLIB_HIDDEN
|
|
31
|
+
# if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__)
|
|
32
|
+
# define ZSTDERRORLIB_HIDDEN __attribute__ ((visibility ("hidden")))
|
|
33
|
+
# else
|
|
34
|
+
# define ZSTDERRORLIB_HIDDEN
|
|
35
|
+
# endif
|
|
36
|
+
#endif
|
|
37
|
+
|
|
38
|
+
#if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1)
|
|
39
|
+
# define ZSTDERRORLIB_API __declspec(dllexport) ZSTDERRORLIB_VISIBLE
|
|
40
|
+
#elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1)
|
|
41
|
+
# define ZSTDERRORLIB_API __declspec(dllimport) ZSTDERRORLIB_VISIBLE /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/
|
|
42
|
+
#else
|
|
43
|
+
# define ZSTDERRORLIB_API ZSTDERRORLIB_VISIBLE
|
|
44
|
+
#endif
|
|
45
|
+
|
|
46
|
+
/*-*********************************************
|
|
47
|
+
* Error codes list
|
|
48
|
+
*-*********************************************
|
|
49
|
+
* Error codes _values_ are pinned down since v1.3.1 only.
|
|
50
|
+
* Therefore, don't rely on values if you may link to any version < v1.3.1.
|
|
51
|
+
*
|
|
52
|
+
* Only values < 100 are considered stable.
|
|
53
|
+
*
|
|
54
|
+
* note 1 : this API shall be used with static linking only.
|
|
55
|
+
* dynamic linking is not yet officially supported.
|
|
56
|
+
* note 2 : Prefer relying on the enum than on its value whenever possible
|
|
57
|
+
* This is the only supported way to use the error list < v1.3.1
|
|
58
|
+
* note 3 : ZSTD_isError() is always correct, whatever the library version.
|
|
59
|
+
**********************************************/
|
|
60
|
+
typedef enum {
|
|
61
|
+
ZSTD_error_no_error = 0,
|
|
62
|
+
ZSTD_error_GENERIC = 1,
|
|
63
|
+
ZSTD_error_prefix_unknown = 10,
|
|
64
|
+
ZSTD_error_version_unsupported = 12,
|
|
65
|
+
ZSTD_error_frameParameter_unsupported = 14,
|
|
66
|
+
ZSTD_error_frameParameter_windowTooLarge = 16,
|
|
67
|
+
ZSTD_error_corruption_detected = 20,
|
|
68
|
+
ZSTD_error_checksum_wrong = 22,
|
|
69
|
+
ZSTD_error_literals_headerWrong = 24,
|
|
70
|
+
ZSTD_error_dictionary_corrupted = 30,
|
|
71
|
+
ZSTD_error_dictionary_wrong = 32,
|
|
72
|
+
ZSTD_error_dictionaryCreation_failed = 34,
|
|
73
|
+
ZSTD_error_parameter_unsupported = 40,
|
|
74
|
+
ZSTD_error_parameter_combination_unsupported = 41,
|
|
75
|
+
ZSTD_error_parameter_outOfBound = 42,
|
|
76
|
+
ZSTD_error_tableLog_tooLarge = 44,
|
|
77
|
+
ZSTD_error_maxSymbolValue_tooLarge = 46,
|
|
78
|
+
ZSTD_error_maxSymbolValue_tooSmall = 48,
|
|
79
|
+
ZSTD_error_cannotProduce_uncompressedBlock = 49,
|
|
80
|
+
ZSTD_error_stabilityCondition_notRespected = 50,
|
|
81
|
+
ZSTD_error_stage_wrong = 60,
|
|
82
|
+
ZSTD_error_init_missing = 62,
|
|
83
|
+
ZSTD_error_memory_allocation = 64,
|
|
84
|
+
ZSTD_error_workSpace_tooSmall= 66,
|
|
85
|
+
ZSTD_error_dstSize_tooSmall = 70,
|
|
86
|
+
ZSTD_error_srcSize_wrong = 72,
|
|
87
|
+
ZSTD_error_dstBuffer_null = 74,
|
|
88
|
+
ZSTD_error_noForwardProgress_destFull = 80,
|
|
89
|
+
ZSTD_error_noForwardProgress_inputEmpty = 82,
|
|
90
|
+
/* following error codes are __NOT STABLE__, they can be removed or changed in future versions */
|
|
91
|
+
ZSTD_error_frameIndex_tooLarge = 100,
|
|
92
|
+
ZSTD_error_seekableIO = 102,
|
|
93
|
+
ZSTD_error_dstBuffer_wrong = 104,
|
|
94
|
+
ZSTD_error_srcBuffer_wrong = 105,
|
|
95
|
+
ZSTD_error_sequenceProducer_failed = 106,
|
|
96
|
+
ZSTD_error_externalSequences_invalid = 107,
|
|
97
|
+
ZSTD_error_maxCode = 120 /* never EVER use this value directly, it can change in future versions! Use ZSTD_isError() instead */
|
|
98
|
+
} ZSTD_ErrorCode;
|
|
99
|
+
|
|
100
|
+
ZSTDERRORLIB_API const char* ZSTD_getErrorString(ZSTD_ErrorCode code); /**< Same as ZSTD_getErrorName, but using a `ZSTD_ErrorCode` enum argument */
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
#if defined (__cplusplus)
|
|
104
|
+
}
|
|
105
|
+
#endif
|
|
106
|
+
|
|
107
|
+
#endif /* ZSTD_ERRORS_H_398273423 */
|
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
// Streaming implementation for VibeZstd
|
|
2
|
+
#include "vibe_zstd_internal.h"
|
|
3
|
+
|
|
4
|
+
// Forward declarations
|
|
5
|
+
static VALUE vibe_zstd_writer_initialize(int argc, VALUE *argv, VALUE self);
|
|
6
|
+
static VALUE vibe_zstd_writer_write(VALUE self, VALUE data);
|
|
7
|
+
static VALUE vibe_zstd_writer_flush(VALUE self);
|
|
8
|
+
static VALUE vibe_zstd_writer_finish(VALUE self);
|
|
9
|
+
static VALUE vibe_zstd_reader_initialize(int argc, VALUE *argv, VALUE self);
|
|
10
|
+
static VALUE vibe_zstd_reader_read(int argc, VALUE *argv, VALUE self);
|
|
11
|
+
static VALUE vibe_zstd_reader_eof(VALUE self);
|
|
12
|
+
|
|
13
|
+
// TypedData types - defined in vibe_zstd.c
|
|
14
|
+
extern rb_data_type_t vibe_zstd_cstream_type;
|
|
15
|
+
extern rb_data_type_t vibe_zstd_dstream_type;
|
|
16
|
+
|
|
17
|
+
// CompressWriter implementation
|
|
18
|
+
// Wraps ZSTD streaming compression to write compressed data to an IO object
|
|
19
|
+
static VALUE
|
|
20
|
+
vibe_zstd_writer_initialize(int argc, VALUE *argv, VALUE self) {
|
|
21
|
+
VALUE io, options;
|
|
22
|
+
rb_scan_args(argc, argv, "11", &io, &options);
|
|
23
|
+
|
|
24
|
+
vibe_zstd_cstream* cstream;
|
|
25
|
+
TypedData_Get_Struct(self, vibe_zstd_cstream, &vibe_zstd_cstream_type, cstream);
|
|
26
|
+
|
|
27
|
+
// Validate IO object responds to write (duck typing)
|
|
28
|
+
if (!rb_respond_to(io, rb_intern("write"))) {
|
|
29
|
+
rb_raise(rb_eTypeError, "IO object must respond to write");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Store IO object
|
|
33
|
+
cstream->io = io;
|
|
34
|
+
rb_ivar_set(self, rb_intern("@io"), io);
|
|
35
|
+
|
|
36
|
+
// Parse options
|
|
37
|
+
int level = 3; // default compression level
|
|
38
|
+
VALUE dict = Qnil;
|
|
39
|
+
unsigned long long pledged_size = ZSTD_CONTENTSIZE_UNKNOWN;
|
|
40
|
+
|
|
41
|
+
if (!NIL_P(options)) {
|
|
42
|
+
Check_Type(options, T_HASH);
|
|
43
|
+
VALUE v_level = rb_hash_aref(options, ID2SYM(rb_intern("level")));
|
|
44
|
+
if (!NIL_P(v_level)) {
|
|
45
|
+
level = NUM2INT(v_level);
|
|
46
|
+
}
|
|
47
|
+
dict = rb_hash_aref(options, ID2SYM(rb_intern("dict")));
|
|
48
|
+
|
|
49
|
+
VALUE v_pledged = rb_hash_aref(options, ID2SYM(rb_intern("pledged_size")));
|
|
50
|
+
if (!NIL_P(v_pledged)) {
|
|
51
|
+
pledged_size = NUM2ULL(v_pledged);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Create compression context (CStream and CCtx are the same since v1.3.0)
|
|
56
|
+
cstream->cstream = ZSTD_createCStream();
|
|
57
|
+
if (!cstream->cstream) {
|
|
58
|
+
rb_raise(rb_eRuntimeError, "Failed to create compression stream");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Reset context for streaming and set compression level
|
|
62
|
+
size_t result = ZSTD_CCtx_reset((ZSTD_CCtx*)cstream->cstream, ZSTD_reset_session_only);
|
|
63
|
+
if (ZSTD_isError(result)) {
|
|
64
|
+
rb_raise(rb_eRuntimeError, "Failed to reset compression context: %s", ZSTD_getErrorName(result));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
result = ZSTD_CCtx_setParameter((ZSTD_CCtx*)cstream->cstream, ZSTD_c_compressionLevel, level);
|
|
68
|
+
if (ZSTD_isError(result)) {
|
|
69
|
+
rb_raise(rb_eRuntimeError, "Failed to set compression level: %s", ZSTD_getErrorName(result));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Set pledged source size if provided
|
|
73
|
+
if (pledged_size != ZSTD_CONTENTSIZE_UNKNOWN) {
|
|
74
|
+
result = ZSTD_CCtx_setPledgedSrcSize((ZSTD_CCtx*)cstream->cstream, pledged_size);
|
|
75
|
+
if (ZSTD_isError(result)) {
|
|
76
|
+
rb_raise(rb_eRuntimeError, "Failed to set pledged source size: %s", ZSTD_getErrorName(result));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Set dictionary if provided
|
|
81
|
+
if (!NIL_P(dict)) {
|
|
82
|
+
vibe_zstd_cdict* cdict_obj;
|
|
83
|
+
TypedData_Get_Struct(dict, vibe_zstd_cdict, &vibe_zstd_cdict_type, cdict_obj);
|
|
84
|
+
result = ZSTD_CCtx_refCDict((ZSTD_CCtx*)cstream->cstream, cdict_obj->cdict);
|
|
85
|
+
if (ZSTD_isError(result)) {
|
|
86
|
+
rb_raise(rb_eRuntimeError, "Failed to set dictionary: %s", ZSTD_getErrorName(result));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return self;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
static VALUE
|
|
94
|
+
vibe_zstd_writer_write(VALUE self, VALUE data) {
|
|
95
|
+
Check_Type(data, T_STRING);
|
|
96
|
+
|
|
97
|
+
vibe_zstd_cstream* cstream;
|
|
98
|
+
TypedData_Get_Struct(self, vibe_zstd_cstream, &vibe_zstd_cstream_type, cstream);
|
|
99
|
+
|
|
100
|
+
// Input buffer: pos advances as ZSTD consumes data
|
|
101
|
+
ZSTD_inBuffer input = {
|
|
102
|
+
.src = RSTRING_PTR(data),
|
|
103
|
+
.size = RSTRING_LEN(data),
|
|
104
|
+
.pos = 0
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
size_t outBufferSize = ZSTD_CStreamOutSize();
|
|
108
|
+
VALUE outBuffer = rb_str_buf_new(outBufferSize);
|
|
109
|
+
|
|
110
|
+
// Process all input data in chunks
|
|
111
|
+
while (input.pos < input.size) {
|
|
112
|
+
ZSTD_outBuffer output = {
|
|
113
|
+
.dst = RSTRING_PTR(outBuffer),
|
|
114
|
+
.size = outBufferSize,
|
|
115
|
+
.pos = 0
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// ZSTD_e_continue: continue compression without flushing
|
|
119
|
+
// Return value is a hint for preferred input size (can be ignored)
|
|
120
|
+
size_t result = ZSTD_compressStream2((ZSTD_CCtx*)cstream->cstream, &output, &input, ZSTD_e_continue);
|
|
121
|
+
if (ZSTD_isError(result)) {
|
|
122
|
+
rb_raise(rb_eRuntimeError, "Compression failed: %s", ZSTD_getErrorName(result));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Write any compressed output that was produced
|
|
126
|
+
if (output.pos > 0) {
|
|
127
|
+
rb_str_set_len(outBuffer, output.pos);
|
|
128
|
+
rb_funcall(cstream->io, rb_intern("write"), 1, outBuffer);
|
|
129
|
+
// No need to resize - buffer capacity remains at outBufferSize
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return self;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
static VALUE
|
|
137
|
+
vibe_zstd_writer_flush(VALUE self) {
|
|
138
|
+
vibe_zstd_cstream* cstream;
|
|
139
|
+
TypedData_Get_Struct(self, vibe_zstd_cstream, &vibe_zstd_cstream_type, cstream);
|
|
140
|
+
|
|
141
|
+
size_t outBufferSize = ZSTD_CStreamOutSize();
|
|
142
|
+
VALUE outBuffer = rb_str_buf_new(outBufferSize);
|
|
143
|
+
|
|
144
|
+
ZSTD_inBuffer input = { NULL, 0, 0 };
|
|
145
|
+
size_t remaining;
|
|
146
|
+
|
|
147
|
+
// ZSTD_e_flush: flush internal buffers, making all data readable
|
|
148
|
+
// Loop until remaining == 0 (flush complete)
|
|
149
|
+
do {
|
|
150
|
+
ZSTD_outBuffer output = {
|
|
151
|
+
.dst = RSTRING_PTR(outBuffer),
|
|
152
|
+
.size = outBufferSize,
|
|
153
|
+
.pos = 0
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// Return value > 0 means more flushing needed
|
|
157
|
+
remaining = ZSTD_compressStream2((ZSTD_CCtx*)cstream->cstream, &output, &input, ZSTD_e_flush);
|
|
158
|
+
if (ZSTD_isError(remaining)) {
|
|
159
|
+
rb_raise(rb_eRuntimeError, "Flush failed: %s", ZSTD_getErrorName(remaining));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (output.pos > 0) {
|
|
163
|
+
rb_str_set_len(outBuffer, output.pos);
|
|
164
|
+
rb_funcall(cstream->io, rb_intern("write"), 1, outBuffer);
|
|
165
|
+
// No need to resize - buffer capacity remains at outBufferSize
|
|
166
|
+
}
|
|
167
|
+
} while (remaining > 0);
|
|
168
|
+
|
|
169
|
+
return self;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
static VALUE
|
|
173
|
+
vibe_zstd_writer_finish(VALUE self) {
|
|
174
|
+
vibe_zstd_cstream* cstream;
|
|
175
|
+
TypedData_Get_Struct(self, vibe_zstd_cstream, &vibe_zstd_cstream_type, cstream);
|
|
176
|
+
|
|
177
|
+
size_t outBufferSize = ZSTD_CStreamOutSize();
|
|
178
|
+
VALUE outBuffer = rb_str_buf_new(outBufferSize);
|
|
179
|
+
|
|
180
|
+
ZSTD_inBuffer input = { NULL, 0, 0 };
|
|
181
|
+
size_t remaining;
|
|
182
|
+
|
|
183
|
+
// ZSTD_e_end: finalize frame with checksum and epilogue
|
|
184
|
+
// Loop until remaining == 0 (frame complete)
|
|
185
|
+
do {
|
|
186
|
+
ZSTD_outBuffer output = {
|
|
187
|
+
.dst = RSTRING_PTR(outBuffer),
|
|
188
|
+
.size = outBufferSize,
|
|
189
|
+
.pos = 0
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
// Return value > 0 means more epilogue data to write
|
|
193
|
+
remaining = ZSTD_compressStream2((ZSTD_CCtx*)cstream->cstream, &output, &input, ZSTD_e_end);
|
|
194
|
+
if (ZSTD_isError(remaining)) {
|
|
195
|
+
rb_raise(rb_eRuntimeError, "Finish failed: %s", ZSTD_getErrorName(remaining));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (output.pos > 0) {
|
|
199
|
+
rb_str_set_len(outBuffer, output.pos);
|
|
200
|
+
rb_funcall(cstream->io, rb_intern("write"), 1, outBuffer);
|
|
201
|
+
// No need to resize - buffer capacity remains at outBufferSize
|
|
202
|
+
}
|
|
203
|
+
} while (remaining > 0);
|
|
204
|
+
|
|
205
|
+
return self;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// DecompressReader implementation
|
|
209
|
+
// Wraps ZSTD streaming decompression to read from a compressed IO object
|
|
210
|
+
static VALUE
|
|
211
|
+
vibe_zstd_reader_initialize(int argc, VALUE *argv, VALUE self) {
|
|
212
|
+
VALUE io, options;
|
|
213
|
+
rb_scan_args(argc, argv, "11", &io, &options);
|
|
214
|
+
|
|
215
|
+
vibe_zstd_dstream* dstream;
|
|
216
|
+
TypedData_Get_Struct(self, vibe_zstd_dstream, &vibe_zstd_dstream_type, dstream);
|
|
217
|
+
|
|
218
|
+
// Validate IO object responds to read (duck typing)
|
|
219
|
+
if (!rb_respond_to(io, rb_intern("read"))) {
|
|
220
|
+
rb_raise(rb_eTypeError, "IO object must respond to read");
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Store IO object
|
|
224
|
+
dstream->io = io;
|
|
225
|
+
rb_ivar_set(self, rb_intern("@io"), io);
|
|
226
|
+
|
|
227
|
+
// Parse options
|
|
228
|
+
VALUE dict = Qnil;
|
|
229
|
+
size_t initial_chunk_size = 0; // 0 = use default ZSTD_DStreamOutSize()
|
|
230
|
+
if (!NIL_P(options)) {
|
|
231
|
+
Check_Type(options, T_HASH);
|
|
232
|
+
dict = rb_hash_aref(options, ID2SYM(rb_intern("dict")));
|
|
233
|
+
|
|
234
|
+
VALUE v_chunk_size = rb_hash_aref(options, ID2SYM(rb_intern("initial_chunk_size")));
|
|
235
|
+
if (!NIL_P(v_chunk_size)) {
|
|
236
|
+
initial_chunk_size = NUM2SIZET(v_chunk_size);
|
|
237
|
+
if (initial_chunk_size == 0) {
|
|
238
|
+
rb_raise(rb_eArgError, "initial_chunk_size must be greater than 0");
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Create decompression context (DStream and DCtx are the same since v1.3.0)
|
|
244
|
+
dstream->dstream = ZSTD_createDStream();
|
|
245
|
+
if (!dstream->dstream) {
|
|
246
|
+
rb_raise(rb_eRuntimeError, "Failed to create decompression stream");
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Reset context for streaming
|
|
250
|
+
size_t result = ZSTD_DCtx_reset((ZSTD_DCtx*)dstream->dstream, ZSTD_reset_session_only);
|
|
251
|
+
if (ZSTD_isError(result)) {
|
|
252
|
+
rb_raise(rb_eRuntimeError, "Failed to reset decompression context: %s", ZSTD_getErrorName(result));
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Set dictionary if provided
|
|
256
|
+
if (!NIL_P(dict)) {
|
|
257
|
+
vibe_zstd_ddict* ddict_obj;
|
|
258
|
+
TypedData_Get_Struct(dict, vibe_zstd_ddict, &vibe_zstd_ddict_type, ddict_obj);
|
|
259
|
+
result = ZSTD_DCtx_refDDict((ZSTD_DCtx*)dstream->dstream, ddict_obj->ddict);
|
|
260
|
+
if (ZSTD_isError(result)) {
|
|
261
|
+
rb_raise(rb_eRuntimeError, "Failed to set dictionary: %s", ZSTD_getErrorName(result));
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Initialize input buffer management
|
|
266
|
+
dstream->input_data = rb_str_new(NULL, 0);
|
|
267
|
+
dstream->input.src = NULL;
|
|
268
|
+
dstream->input.size = 0;
|
|
269
|
+
dstream->input.pos = 0;
|
|
270
|
+
dstream->eof = 0;
|
|
271
|
+
dstream->initial_chunk_size = initial_chunk_size;
|
|
272
|
+
|
|
273
|
+
return self;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// DecompressReader read - Read decompressed data from stream
|
|
277
|
+
//
|
|
278
|
+
// Handles streaming decompression with buffered input management:
|
|
279
|
+
// - Requested size: Reads up to specified number of bytes
|
|
280
|
+
// - No size (nil): Reads one chunk (default: ZSTD_DStreamOutSize ~128KB)
|
|
281
|
+
//
|
|
282
|
+
// Buffer management:
|
|
283
|
+
// - Maintains internal compressed input buffer that refills from IO as needed
|
|
284
|
+
// - Calls ZSTD_decompressStream incrementally to produce output
|
|
285
|
+
// - Tracks EOF state based on IO exhaustion and frame completion
|
|
286
|
+
//
|
|
287
|
+
// EOF handling:
|
|
288
|
+
// - Returns nil when no more data available
|
|
289
|
+
// - Sets eof flag when: IO returns nil, frame complete (ret==0), or no progress made
|
|
290
|
+
//
|
|
291
|
+
// This implements proper streaming semantics for incremental decompression
|
|
292
|
+
// of arbitrarily large files without loading everything into memory.
|
|
293
|
+
static VALUE
|
|
294
|
+
vibe_zstd_reader_read(int argc, VALUE *argv, VALUE self) {
|
|
295
|
+
VALUE size_arg;
|
|
296
|
+
rb_scan_args(argc, argv, "01", &size_arg);
|
|
297
|
+
|
|
298
|
+
vibe_zstd_dstream* dstream;
|
|
299
|
+
TypedData_Get_Struct(self, vibe_zstd_dstream, &vibe_zstd_dstream_type, dstream);
|
|
300
|
+
|
|
301
|
+
if (dstream->eof) {
|
|
302
|
+
return Qnil;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Unbounded reads use configurable chunk size (defaults to ZSTD_DStreamOutSize() ~128KB)
|
|
306
|
+
// This provides chunked streaming behavior for true streaming use cases
|
|
307
|
+
size_t default_chunk_size = (dstream->initial_chunk_size > 0) ? dstream->initial_chunk_size : ZSTD_DStreamOutSize();
|
|
308
|
+
size_t requested_size = NIL_P(size_arg) ? default_chunk_size : NUM2SIZET(size_arg);
|
|
309
|
+
size_t inBufferSize = ZSTD_DStreamInSize();
|
|
310
|
+
|
|
311
|
+
// Preallocate buffer for requested size
|
|
312
|
+
VALUE result = rb_str_buf_new(requested_size);
|
|
313
|
+
|
|
314
|
+
size_t total_read = 0;
|
|
315
|
+
int made_progress = 0;
|
|
316
|
+
|
|
317
|
+
while (total_read < requested_size) {
|
|
318
|
+
// Refill input buffer when all compressed data consumed
|
|
319
|
+
if (dstream->input.pos >= dstream->input.size) {
|
|
320
|
+
VALUE chunk = rb_funcall(dstream->io, rb_intern("read"), 1, SIZET2NUM(inBufferSize));
|
|
321
|
+
if (NIL_P(chunk)) {
|
|
322
|
+
dstream->eof = 1;
|
|
323
|
+
if (total_read == 0 && !made_progress) {
|
|
324
|
+
return Qnil;
|
|
325
|
+
}
|
|
326
|
+
break;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Reset input buffer with new data
|
|
330
|
+
dstream->input_data = chunk;
|
|
331
|
+
dstream->input.src = RSTRING_PTR(chunk);
|
|
332
|
+
dstream->input.size = RSTRING_LEN(chunk);
|
|
333
|
+
dstream->input.pos = 0;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (dstream->input.size == 0) {
|
|
337
|
+
dstream->eof = 1;
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
size_t space_left = requested_size - total_read;
|
|
342
|
+
|
|
343
|
+
ZSTD_outBuffer output = {
|
|
344
|
+
.dst = RSTRING_PTR(result) + total_read,
|
|
345
|
+
.size = space_left,
|
|
346
|
+
.pos = 0
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
// ZSTD_decompressStream advances input.pos and output.pos
|
|
350
|
+
// Return value: 0 = frame complete, >0 = hint for next input size, error if < 0
|
|
351
|
+
size_t ret = ZSTD_decompressStream(dstream->dstream, &output, &dstream->input);
|
|
352
|
+
if (ZSTD_isError(ret)) {
|
|
353
|
+
rb_raise(rb_eRuntimeError, "Decompression failed: %s", ZSTD_getErrorName(ret));
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (output.pos > 0) {
|
|
357
|
+
total_read += output.pos;
|
|
358
|
+
made_progress = 1;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Exit when we've read enough data
|
|
362
|
+
if (total_read >= requested_size) {
|
|
363
|
+
break;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// ret == 0 signals end of current frame
|
|
367
|
+
if (ret == 0) {
|
|
368
|
+
dstream->eof = 1;
|
|
369
|
+
break;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// No output produced: need more input
|
|
373
|
+
if (output.pos == 0) {
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (total_read == 0) {
|
|
379
|
+
dstream->eof = 1;
|
|
380
|
+
return Qnil;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
rb_str_set_len(result, total_read);
|
|
384
|
+
return result;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
static VALUE
|
|
388
|
+
vibe_zstd_reader_eof(VALUE self) {
|
|
389
|
+
vibe_zstd_dstream* dstream;
|
|
390
|
+
TypedData_Get_Struct(self, vibe_zstd_dstream, &vibe_zstd_dstream_type, dstream);
|
|
391
|
+
return dstream->eof ? Qtrue : Qfalse;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Class initialization function called from main Init_vibe_zstd
|
|
395
|
+
void
|
|
396
|
+
vibe_zstd_streaming_init_classes(VALUE rb_cVibeZstdCompressWriter, VALUE rb_cVibeZstdDecompressReader) {
|
|
397
|
+
// CompressWriter setup
|
|
398
|
+
rb_define_alloc_func(rb_cVibeZstdCompressWriter, vibe_zstd_cstream_alloc);
|
|
399
|
+
rb_define_method(rb_cVibeZstdCompressWriter, "initialize", vibe_zstd_writer_initialize, -1);
|
|
400
|
+
rb_define_method(rb_cVibeZstdCompressWriter, "write", vibe_zstd_writer_write, 1);
|
|
401
|
+
rb_define_method(rb_cVibeZstdCompressWriter, "flush", vibe_zstd_writer_flush, 0);
|
|
402
|
+
rb_define_method(rb_cVibeZstdCompressWriter, "finish", vibe_zstd_writer_finish, 0);
|
|
403
|
+
rb_define_method(rb_cVibeZstdCompressWriter, "close", vibe_zstd_writer_finish, 0); // alias
|
|
404
|
+
|
|
405
|
+
// DecompressReader setup
|
|
406
|
+
rb_define_alloc_func(rb_cVibeZstdDecompressReader, vibe_zstd_dstream_alloc);
|
|
407
|
+
rb_define_method(rb_cVibeZstdDecompressReader, "initialize", vibe_zstd_reader_initialize, -1);
|
|
408
|
+
rb_define_method(rb_cVibeZstdDecompressReader, "read", vibe_zstd_reader_read, -1);
|
|
409
|
+
rb_define_method(rb_cVibeZstdDecompressReader, "eof?", vibe_zstd_reader_eof, 0);
|
|
410
|
+
}
|