ruby-bzs 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.
@@ -0,0 +1,98 @@
1
+ // Ruby bindings for bzip2 library.
2
+ // Copyright (c) 2022 AUTHORS, MIT License.
3
+
4
+ #include "bzs_ext/option.h"
5
+
6
+ #include "bzs_ext/error.h"
7
+
8
+ // -- values --
9
+
10
+ static inline VALUE get_raw_value(VALUE options, const char* name)
11
+ {
12
+ return rb_funcall(options, rb_intern("[]"), 1, ID2SYM(rb_intern(name)));
13
+ }
14
+
15
+ // -- getters --
16
+
17
+ static inline bool get_bool_value(VALUE raw_value)
18
+ {
19
+ int raw_type = TYPE(raw_value);
20
+ if (raw_type != T_TRUE && raw_type != T_FALSE) {
21
+ bzs_ext_raise_error(BZS_EXT_ERROR_VALIDATE_FAILED);
22
+ }
23
+
24
+ return raw_type == T_TRUE;
25
+ }
26
+
27
+ static inline int get_int_value(VALUE raw_value)
28
+ {
29
+ Check_Type(raw_value, T_FIXNUM);
30
+
31
+ return NUM2INT(raw_value);
32
+ }
33
+
34
+ static inline size_t get_size_value(VALUE raw_value)
35
+ {
36
+ Check_Type(raw_value, T_FIXNUM);
37
+
38
+ return NUM2SIZET(raw_value);
39
+ }
40
+
41
+ bool bzs_ext_get_bool_option_value(VALUE options, const char* name)
42
+ {
43
+ VALUE raw_value = get_raw_value(options, name);
44
+
45
+ return get_bool_value(raw_value);
46
+ }
47
+
48
+ size_t bzs_ext_get_size_option_value(VALUE options, const char* name)
49
+ {
50
+ VALUE raw_value = get_raw_value(options, name);
51
+
52
+ return get_size_value(raw_value);
53
+ }
54
+
55
+ // -- resolves --
56
+
57
+ bzs_ext_option_t bzs_ext_resolve_bool_option_value(VALUE options, const char* name, bzs_ext_option_t default_value)
58
+ {
59
+ VALUE raw_value = get_raw_value(options, name);
60
+ if (raw_value != Qnil) {
61
+ return get_bool_value(raw_value);
62
+ } else {
63
+ return default_value;
64
+ }
65
+ }
66
+
67
+ bzs_ext_option_t bzs_ext_resolve_int_option_value(VALUE options, const char* name, bzs_ext_option_t default_value)
68
+ {
69
+ VALUE raw_value = get_raw_value(options, name);
70
+ if (raw_value != Qnil) {
71
+ return get_int_value(raw_value);
72
+ } else {
73
+ return default_value;
74
+ }
75
+ }
76
+
77
+ // -- others --
78
+
79
+ void bzs_ext_option_exports(VALUE root_module)
80
+ {
81
+ VALUE module = rb_define_module_under(root_module, "Option");
82
+
83
+ rb_define_const(module, "MIN_BLOCK_SIZE", SIZET2NUM(BZS_MIN_BLOCK_SIZE));
84
+ rb_define_const(module, "MAX_BLOCK_SIZE", SIZET2NUM(BZS_MAX_BLOCK_SIZE));
85
+ rb_define_const(module, "DEFAULT_BLOCK_SIZE", SIZET2NUM(BZS_DEFAULT_BLOCK_SIZE));
86
+
87
+ rb_define_const(module, "MIN_WORK_FACTOR", SIZET2NUM(BZS_MIN_WORK_FACTOR));
88
+ rb_define_const(module, "MAX_WORK_FACTOR", SIZET2NUM(BZS_MAX_WORK_FACTOR));
89
+ rb_define_const(module, "DEFAULT_WORK_FACTOR", SIZET2NUM(BZS_DEFAULT_WORK_FACTOR));
90
+
91
+ rb_define_const(module, "MIN_SMALL", SIZET2NUM(BZS_MIN_SMALL));
92
+ rb_define_const(module, "MAX_SMALL", SIZET2NUM(BZS_MAX_SMALL));
93
+ rb_define_const(module, "DEFAULT_SMALL", SIZET2NUM(BZS_DEFAULT_SMALL));
94
+
95
+ rb_define_const(module, "MIN_VERBOSITY", SIZET2NUM(BZS_MIN_VERBOSITY));
96
+ rb_define_const(module, "MAX_VERBOSITY", SIZET2NUM(BZS_MAX_VERBOSITY));
97
+ rb_define_const(module, "DEFAULT_VERBOSITY", SIZET2NUM(BZS_DEFAULT_VERBOSITY));
98
+ }
@@ -0,0 +1,62 @@
1
+ // Ruby bindings for bzip2 library.
2
+ // Copyright (c) 2022 AUTHORS, MIT License.
3
+
4
+ #if !defined(BZS_EXT_OPTIONS_H)
5
+ #define BZS_EXT_OPTIONS_H
6
+
7
+ #include <stdbool.h>
8
+ #include <stdlib.h>
9
+
10
+ #include "ruby.h"
11
+
12
+ #define BZS_MIN_BLOCK_SIZE 1
13
+ #define BZS_MAX_BLOCK_SIZE 9
14
+ #define BZS_DEFAULT_BLOCK_SIZE BZS_MAX_BLOCK_SIZE
15
+
16
+ #define BZS_MIN_WORK_FACTOR 0
17
+ #define BZS_MAX_WORK_FACTOR 250
18
+ #define BZS_DEFAULT_WORK_FACTOR BZS_MIN_WORK_FACTOR
19
+
20
+ #define BZS_MIN_SMALL 0
21
+ #define BZS_MAX_SMALL 1
22
+ #define BZS_DEFAULT_SMALL BZS_MAX_SMALL
23
+
24
+ #define BZS_MIN_VERBOSITY 0
25
+ #define BZS_MAX_VERBOSITY 4
26
+ #define BZS_DEFAULT_VERBOSITY BZS_MIN_VERBOSITY
27
+
28
+ #define BZS_DEFAULT_QUIET 1
29
+
30
+ // Bzip2 options are integers instead of unsigned integers.
31
+ typedef int bzs_ext_option_t;
32
+
33
+ bool bzs_ext_get_bool_option_value(VALUE options, const char* name);
34
+ size_t bzs_ext_get_size_option_value(VALUE options, const char* name);
35
+
36
+ #define BZS_EXT_GET_BOOL_OPTION(options, name) bool name = bzs_ext_get_bool_option_value(options, #name);
37
+ #define BZS_EXT_GET_SIZE_OPTION(options, name) size_t name = bzs_ext_get_size_option_value(options, #name);
38
+
39
+ bzs_ext_option_t bzs_ext_resolve_bool_option_value(VALUE options, const char* name, bzs_ext_option_t default_value);
40
+ bzs_ext_option_t bzs_ext_resolve_int_option_value(VALUE options, const char* name, bzs_ext_option_t default_value);
41
+
42
+ #define BZS_EXT_RESOLVE_BOOL_OPTION(options, name, default_value) \
43
+ bzs_ext_option_t name = bzs_ext_resolve_bool_option_value(options, #name, default_value);
44
+ #define BZS_EXT_RESOLVE_INT_OPTION(options, name, default_value) \
45
+ bzs_ext_option_t name = bzs_ext_resolve_int_option_value(options, #name, default_value);
46
+
47
+ #define BZS_EXT_RESOLVE_VERBOSITY_OPTION(options) \
48
+ BZS_EXT_RESOLVE_BOOL_OPTION(options, quiet, BZS_DEFAULT_QUIET); \
49
+ bzs_ext_option_t verbosity = quiet ? BZS_MIN_VERBOSITY : BZS_MAX_VERBOSITY;
50
+
51
+ #define BZS_EXT_RESOLVE_COMPRESSOR_OPTIONS(options) \
52
+ BZS_EXT_RESOLVE_INT_OPTION(options, block_size, BZS_DEFAULT_BLOCK_SIZE); \
53
+ BZS_EXT_RESOLVE_INT_OPTION(options, work_factor, BZS_DEFAULT_WORK_FACTOR); \
54
+ BZS_EXT_RESOLVE_VERBOSITY_OPTION(options);
55
+
56
+ #define BZS_EXT_RESOLVE_DECOMPRESSOR_OPTIONS(options) \
57
+ BZS_EXT_RESOLVE_BOOL_OPTION(options, small, BZS_DEFAULT_SMALL); \
58
+ BZS_EXT_RESOLVE_VERBOSITY_OPTION(options);
59
+
60
+ void bzs_ext_option_exports(VALUE root_module);
61
+
62
+ #endif // BZS_EXT_OPTIONS_H
@@ -0,0 +1,285 @@
1
+ // Ruby bindings for bzip2 library.
2
+ // Copyright (c) 2022 AUTHORS, MIT License.
3
+
4
+ #include "bzs_ext/stream/compressor.h"
5
+
6
+ #include <stdlib.h>
7
+
8
+ #include "bzs_ext/buffer.h"
9
+ #include "bzs_ext/error.h"
10
+ #include "bzs_ext/gvl.h"
11
+ #include "bzs_ext/option.h"
12
+ #include "bzs_ext/utils.h"
13
+
14
+ // -- initialization --
15
+
16
+ static void free_compressor(bzs_ext_compressor_t* compressor_ptr)
17
+ {
18
+ bz_stream* stream_ptr = compressor_ptr->stream_ptr;
19
+ if (stream_ptr != NULL) {
20
+ BZ2_bzCompressEnd(stream_ptr);
21
+ }
22
+
23
+ bzs_ext_byte_t* destination_buffer = compressor_ptr->destination_buffer;
24
+ if (destination_buffer != NULL) {
25
+ free(destination_buffer);
26
+ }
27
+
28
+ free(compressor_ptr);
29
+ }
30
+
31
+ VALUE bzs_ext_allocate_compressor(VALUE klass)
32
+ {
33
+ bzs_ext_compressor_t* compressor_ptr;
34
+ VALUE self = Data_Make_Struct(klass, bzs_ext_compressor_t, NULL, free_compressor, compressor_ptr);
35
+
36
+ compressor_ptr->stream_ptr = NULL;
37
+ compressor_ptr->destination_buffer = NULL;
38
+ compressor_ptr->destination_buffer_length = 0;
39
+ compressor_ptr->remaining_destination_buffer = NULL;
40
+ compressor_ptr->remaining_destination_buffer_length = 0;
41
+ compressor_ptr->gvl = false;
42
+
43
+ return self;
44
+ }
45
+
46
+ #define GET_COMPRESSOR(self) \
47
+ bzs_ext_compressor_t* compressor_ptr; \
48
+ Data_Get_Struct(self, bzs_ext_compressor_t, compressor_ptr);
49
+
50
+ VALUE bzs_ext_initialize_compressor(VALUE self, VALUE options)
51
+ {
52
+ GET_COMPRESSOR(self);
53
+ Check_Type(options, T_HASH);
54
+ BZS_EXT_GET_SIZE_OPTION(options, destination_buffer_length);
55
+ BZS_EXT_GET_BOOL_OPTION(options, gvl);
56
+ BZS_EXT_RESOLVE_COMPRESSOR_OPTIONS(options);
57
+
58
+ bz_stream* stream_ptr = malloc(sizeof(bz_stream));
59
+ if (stream_ptr == NULL) {
60
+ bzs_ext_raise_error(BZS_EXT_ERROR_ALLOCATE_FAILED);
61
+ }
62
+
63
+ stream_ptr->bzalloc = NULL;
64
+ stream_ptr->bzfree = NULL;
65
+ stream_ptr->opaque = NULL;
66
+
67
+ bzs_result_t result = BZ2_bzCompressInit(stream_ptr, block_size, verbosity, work_factor);
68
+ if (result != BZ_OK) {
69
+ free(stream_ptr);
70
+ bzs_ext_raise_error(bzs_ext_get_error(result));
71
+ }
72
+
73
+ if (destination_buffer_length == 0) {
74
+ destination_buffer_length = BZS_DEFAULT_DESTINATION_BUFFER_LENGTH_FOR_COMPRESSOR;
75
+ }
76
+
77
+ bzs_ext_byte_t* destination_buffer = malloc(destination_buffer_length);
78
+ if (destination_buffer == NULL) {
79
+ free(stream_ptr);
80
+ bzs_ext_raise_error(BZS_EXT_ERROR_ALLOCATE_FAILED);
81
+ }
82
+
83
+ compressor_ptr->stream_ptr = stream_ptr;
84
+ compressor_ptr->destination_buffer = destination_buffer;
85
+ compressor_ptr->destination_buffer_length = destination_buffer_length;
86
+ compressor_ptr->remaining_destination_buffer = destination_buffer;
87
+ compressor_ptr->remaining_destination_buffer_length = destination_buffer_length;
88
+ compressor_ptr->gvl = gvl;
89
+
90
+ return Qnil;
91
+ }
92
+
93
+ // -- compress --
94
+
95
+ #define DO_NOT_USE_AFTER_CLOSE(compressor_ptr) \
96
+ if (compressor_ptr->stream_ptr == NULL || compressor_ptr->destination_buffer == NULL) { \
97
+ bzs_ext_raise_error(BZS_EXT_ERROR_USED_AFTER_CLOSE); \
98
+ }
99
+
100
+ typedef struct
101
+ {
102
+ bz_stream* stream_ptr;
103
+ int stream_action;
104
+ bzs_ext_byte_t** remaining_source_ptr;
105
+ size_t* remaining_source_length_ptr;
106
+ bzs_ext_byte_t** remaining_destination_buffer_ptr;
107
+ size_t* remaining_destination_buffer_length_ptr;
108
+ bzs_result_t result;
109
+ } compress_args_t;
110
+
111
+ static inline void* compress_wrapper(void* data)
112
+ {
113
+ compress_args_t* args = data;
114
+
115
+ args->stream_ptr->next_in = (char*) *args->remaining_source_ptr;
116
+ args->stream_ptr->avail_in = bzs_consume_size(*args->remaining_source_length_ptr);
117
+ args->stream_ptr->next_out = (char*) *args->remaining_destination_buffer_ptr;
118
+ args->stream_ptr->avail_out = bzs_consume_size(*args->remaining_destination_buffer_length_ptr);
119
+
120
+ args->result = BZ2_bzCompress(args->stream_ptr, args->stream_action);
121
+
122
+ *args->remaining_source_ptr = (bzs_ext_byte_t*) args->stream_ptr->next_in;
123
+ *args->remaining_source_length_ptr = args->stream_ptr->avail_in;
124
+ *args->remaining_destination_buffer_ptr = (bzs_ext_byte_t*) args->stream_ptr->next_out;
125
+ *args->remaining_destination_buffer_length_ptr = args->stream_ptr->avail_out;
126
+
127
+ return NULL;
128
+ }
129
+
130
+ VALUE bzs_ext_compress(VALUE self, VALUE source_value)
131
+ {
132
+ GET_COMPRESSOR(self);
133
+ DO_NOT_USE_AFTER_CLOSE(compressor_ptr);
134
+ Check_Type(source_value, T_STRING);
135
+
136
+ const char* source = RSTRING_PTR(source_value);
137
+ size_t source_length = RSTRING_LEN(source_value);
138
+ bzs_ext_byte_t* remaining_source = (bzs_ext_byte_t*) source;
139
+ size_t remaining_source_length = source_length;
140
+
141
+ compress_args_t args = {
142
+ .stream_ptr = compressor_ptr->stream_ptr,
143
+ .stream_action = BZ_RUN,
144
+ .remaining_source_ptr = &remaining_source,
145
+ .remaining_source_length_ptr = &remaining_source_length,
146
+ .remaining_destination_buffer_ptr = &compressor_ptr->remaining_destination_buffer,
147
+ .remaining_destination_buffer_length_ptr = &compressor_ptr->remaining_destination_buffer_length};
148
+
149
+ BZS_EXT_GVL_WRAP(compressor_ptr->gvl, compress_wrapper, &args);
150
+ if (args.result != BZ_RUN_OK && args.result != BZ_PARAM_ERROR && args.result != BZ_STREAM_END) {
151
+ bzs_ext_raise_error(bzs_ext_get_error(args.result));
152
+ }
153
+
154
+ VALUE bytes_written = SIZET2NUM(source_length - remaining_source_length);
155
+ VALUE needs_more_destination =
156
+ args.result == BZ_RUN_OK &&
157
+ (remaining_source_length != 0 || compressor_ptr->remaining_destination_buffer_length == 0) ?
158
+ Qtrue :
159
+ Qfalse;
160
+
161
+ return rb_ary_new_from_args(2, bytes_written, needs_more_destination);
162
+ }
163
+
164
+ // -- compressor flush --
165
+
166
+ VALUE bzs_ext_flush_compressor(VALUE self)
167
+ {
168
+ GET_COMPRESSOR(self);
169
+ DO_NOT_USE_AFTER_CLOSE(compressor_ptr);
170
+
171
+ bzs_ext_byte_t* remaining_source = NULL;
172
+ size_t remaining_source_length = 0;
173
+
174
+ compress_args_t args = {
175
+ .stream_ptr = compressor_ptr->stream_ptr,
176
+ .stream_action = BZ_FLUSH,
177
+ .remaining_source_ptr = &remaining_source,
178
+ .remaining_source_length_ptr = &remaining_source_length,
179
+ .remaining_destination_buffer_ptr = &compressor_ptr->remaining_destination_buffer,
180
+ .remaining_destination_buffer_length_ptr = &compressor_ptr->remaining_destination_buffer_length};
181
+
182
+ BZS_EXT_GVL_WRAP(compressor_ptr->gvl, compress_wrapper, &args);
183
+ if (args.result != BZ_FLUSH_OK && args.result != BZ_PARAM_ERROR && args.result != BZ_RUN_OK) {
184
+ bzs_ext_raise_error(bzs_ext_get_error(args.result));
185
+ }
186
+
187
+ return args.result == BZ_FLUSH_OK &&
188
+ (remaining_source_length != 0 || compressor_ptr->remaining_destination_buffer_length == 0) ?
189
+ Qtrue :
190
+ Qfalse;
191
+ }
192
+
193
+ // -- compressor finish --
194
+
195
+ VALUE bzs_ext_finish_compressor(VALUE self)
196
+ {
197
+ GET_COMPRESSOR(self);
198
+ DO_NOT_USE_AFTER_CLOSE(compressor_ptr);
199
+
200
+ bzs_ext_byte_t* remaining_source = NULL;
201
+ size_t remaining_source_length = 0;
202
+
203
+ compress_args_t args = {
204
+ .stream_ptr = compressor_ptr->stream_ptr,
205
+ .stream_action = BZ_FINISH,
206
+ .remaining_source_ptr = &remaining_source,
207
+ .remaining_source_length_ptr = &remaining_source_length,
208
+ .remaining_destination_buffer_ptr = &compressor_ptr->remaining_destination_buffer,
209
+ .remaining_destination_buffer_length_ptr = &compressor_ptr->remaining_destination_buffer_length};
210
+
211
+ BZS_EXT_GVL_WRAP(compressor_ptr->gvl, compress_wrapper, &args);
212
+ if (args.result != BZ_FINISH_OK && args.result != BZ_PARAM_ERROR && args.result != BZ_STREAM_END) {
213
+ bzs_ext_raise_error(bzs_ext_get_error(args.result));
214
+ }
215
+
216
+ return args.result == BZ_FINISH_OK &&
217
+ (remaining_source_length != 0 || compressor_ptr->remaining_destination_buffer_length == 0) ?
218
+ Qtrue :
219
+ Qfalse;
220
+ }
221
+
222
+ // -- other --
223
+
224
+ VALUE bzs_ext_compressor_read_result(VALUE self)
225
+ {
226
+ GET_COMPRESSOR(self);
227
+ DO_NOT_USE_AFTER_CLOSE(compressor_ptr);
228
+
229
+ bzs_ext_byte_t* destination_buffer = compressor_ptr->destination_buffer;
230
+ size_t destination_buffer_length = compressor_ptr->destination_buffer_length;
231
+ size_t remaining_destination_buffer_length = compressor_ptr->remaining_destination_buffer_length;
232
+
233
+ const char* result = (const char*) destination_buffer;
234
+ size_t result_length = destination_buffer_length - remaining_destination_buffer_length;
235
+ VALUE result_value = rb_str_new(result, result_length);
236
+
237
+ compressor_ptr->remaining_destination_buffer = destination_buffer;
238
+ compressor_ptr->remaining_destination_buffer_length = destination_buffer_length;
239
+
240
+ return result_value;
241
+ }
242
+
243
+ // -- cleanup --
244
+
245
+ VALUE bzs_ext_compressor_close(VALUE self)
246
+ {
247
+ GET_COMPRESSOR(self);
248
+ DO_NOT_USE_AFTER_CLOSE(compressor_ptr);
249
+
250
+ bz_stream* stream_ptr = compressor_ptr->stream_ptr;
251
+ if (stream_ptr != NULL) {
252
+ BZ2_bzCompressEnd(stream_ptr);
253
+
254
+ compressor_ptr->stream_ptr = NULL;
255
+ }
256
+
257
+ bzs_ext_byte_t* destination_buffer = compressor_ptr->destination_buffer;
258
+ if (destination_buffer != NULL) {
259
+ free(destination_buffer);
260
+
261
+ compressor_ptr->destination_buffer = NULL;
262
+ }
263
+
264
+ // It is possible to keep "destination_buffer_length", "remaining_destination_buffer"
265
+ // and "remaining_destination_buffer_length" as is.
266
+
267
+ return Qnil;
268
+ }
269
+
270
+ // -- exports --
271
+
272
+ void bzs_ext_compressor_exports(VALUE root_module)
273
+ {
274
+ VALUE module = rb_define_module_under(root_module, "Stream");
275
+
276
+ VALUE compressor = rb_define_class_under(module, "NativeCompressor", rb_cObject);
277
+
278
+ rb_define_alloc_func(compressor, bzs_ext_allocate_compressor);
279
+ rb_define_method(compressor, "initialize", bzs_ext_initialize_compressor, 1);
280
+ rb_define_method(compressor, "write", bzs_ext_compress, 1);
281
+ rb_define_method(compressor, "flush", bzs_ext_flush_compressor, 0);
282
+ rb_define_method(compressor, "finish", bzs_ext_finish_compressor, 0);
283
+ rb_define_method(compressor, "read_result", bzs_ext_compressor_read_result, 0);
284
+ rb_define_method(compressor, "close", bzs_ext_compressor_close, 0);
285
+ }
@@ -0,0 +1,33 @@
1
+ // Ruby bindings for bzip2 library.
2
+ // Copyright (c) 2022 AUTHORS, MIT License.
3
+
4
+ #if !defined(BZS_EXT_STREAM_COMPRESSOR_H)
5
+ #define BZS_EXT_STREAM_COMPRESSOR_H
6
+
7
+ #include <bzlib.h>
8
+ #include <stdbool.h>
9
+
10
+ #include "bzs_ext/common.h"
11
+ #include "ruby.h"
12
+
13
+ typedef struct
14
+ {
15
+ bz_stream* stream_ptr;
16
+ bzs_ext_byte_t* destination_buffer;
17
+ size_t destination_buffer_length;
18
+ bzs_ext_byte_t* remaining_destination_buffer;
19
+ size_t remaining_destination_buffer_length;
20
+ bool gvl;
21
+ } bzs_ext_compressor_t;
22
+
23
+ VALUE bzs_ext_allocate_compressor(VALUE klass);
24
+ VALUE bzs_ext_initialize_compressor(VALUE self, VALUE options);
25
+ VALUE bzs_ext_compress(VALUE self, VALUE source);
26
+ VALUE bzs_ext_flush_compressor(VALUE self);
27
+ VALUE bzs_ext_finish_compressor(VALUE self);
28
+ VALUE bzs_ext_compressor_read_result(VALUE self);
29
+ VALUE bzs_ext_compressor_close(VALUE self);
30
+
31
+ void bzs_ext_compressor_exports(VALUE root_module);
32
+
33
+ #endif // BZS_EXT_STREAM_COMPRESSOR_H
@@ -0,0 +1,221 @@
1
+ // Ruby bindings for bzip2 library.
2
+ // Copyright (c) 2022 AUTHORS, MIT License.
3
+
4
+ #include "bzs_ext/stream/decompressor.h"
5
+
6
+ #include <stdlib.h>
7
+
8
+ #include "bzs_ext/buffer.h"
9
+ #include "bzs_ext/error.h"
10
+ #include "bzs_ext/gvl.h"
11
+ #include "bzs_ext/option.h"
12
+ #include "bzs_ext/utils.h"
13
+
14
+ // -- initialization --
15
+
16
+ static void free_decompressor(bzs_ext_decompressor_t* decompressor_ptr)
17
+ {
18
+ bz_stream* stream_ptr = decompressor_ptr->stream_ptr;
19
+ if (stream_ptr != NULL) {
20
+ BZ2_bzDecompressEnd(stream_ptr);
21
+ }
22
+
23
+ bzs_ext_byte_t* destination_buffer = decompressor_ptr->destination_buffer;
24
+ if (destination_buffer != NULL) {
25
+ free(destination_buffer);
26
+ }
27
+
28
+ free(decompressor_ptr);
29
+ }
30
+
31
+ VALUE bzs_ext_allocate_decompressor(VALUE klass)
32
+ {
33
+ bzs_ext_decompressor_t* decompressor_ptr;
34
+ VALUE self = Data_Make_Struct(klass, bzs_ext_decompressor_t, NULL, free_decompressor, decompressor_ptr);
35
+
36
+ decompressor_ptr->stream_ptr = NULL;
37
+ decompressor_ptr->destination_buffer = NULL;
38
+ decompressor_ptr->destination_buffer_length = 0;
39
+ decompressor_ptr->remaining_destination_buffer = NULL;
40
+ decompressor_ptr->remaining_destination_buffer_length = 0;
41
+
42
+ return self;
43
+ }
44
+
45
+ #define GET_DECOMPRESSOR(self) \
46
+ bzs_ext_decompressor_t* decompressor_ptr; \
47
+ Data_Get_Struct(self, bzs_ext_decompressor_t, decompressor_ptr);
48
+
49
+ VALUE bzs_ext_initialize_decompressor(VALUE self, VALUE options)
50
+ {
51
+ GET_DECOMPRESSOR(self);
52
+ Check_Type(options, T_HASH);
53
+ BZS_EXT_GET_SIZE_OPTION(options, destination_buffer_length);
54
+ BZS_EXT_GET_BOOL_OPTION(options, gvl);
55
+ BZS_EXT_RESOLVE_DECOMPRESSOR_OPTIONS(options);
56
+
57
+ bz_stream* stream_ptr = malloc(sizeof(bz_stream));
58
+ if (stream_ptr == NULL) {
59
+ bzs_ext_raise_error(BZS_EXT_ERROR_ALLOCATE_FAILED);
60
+ }
61
+
62
+ stream_ptr->bzalloc = NULL;
63
+ stream_ptr->bzfree = NULL;
64
+ stream_ptr->opaque = NULL;
65
+
66
+ bzs_result_t result = BZ2_bzDecompressInit(stream_ptr, verbosity, small);
67
+ if (result != BZ_OK) {
68
+ free(stream_ptr);
69
+ bzs_ext_raise_error(bzs_ext_get_error(result));
70
+ }
71
+
72
+ if (destination_buffer_length == 0) {
73
+ destination_buffer_length = BZS_DEFAULT_DESTINATION_BUFFER_LENGTH_FOR_DECOMPRESSOR;
74
+ }
75
+
76
+ bzs_ext_byte_t* destination_buffer = malloc(destination_buffer_length);
77
+ if (destination_buffer == NULL) {
78
+ free(stream_ptr);
79
+ bzs_ext_raise_error(BZS_EXT_ERROR_ALLOCATE_FAILED);
80
+ }
81
+
82
+ decompressor_ptr->stream_ptr = stream_ptr;
83
+ decompressor_ptr->destination_buffer = destination_buffer;
84
+ decompressor_ptr->destination_buffer_length = destination_buffer_length;
85
+ decompressor_ptr->remaining_destination_buffer = destination_buffer;
86
+ decompressor_ptr->remaining_destination_buffer_length = destination_buffer_length;
87
+ decompressor_ptr->gvl = gvl;
88
+
89
+ return Qnil;
90
+ }
91
+
92
+ // -- decompress --
93
+
94
+ #define DO_NOT_USE_AFTER_CLOSE(decompressor_ptr) \
95
+ if (decompressor_ptr->stream_ptr == NULL || decompressor_ptr->destination_buffer == NULL) { \
96
+ bzs_ext_raise_error(BZS_EXT_ERROR_USED_AFTER_CLOSE); \
97
+ }
98
+
99
+ typedef struct
100
+ {
101
+ bz_stream* stream_ptr;
102
+ bzs_ext_byte_t** remaining_source_ptr;
103
+ size_t* remaining_source_length_ptr;
104
+ bzs_ext_byte_t** remaining_destination_buffer_ptr;
105
+ size_t* remaining_destination_buffer_length_ptr;
106
+ bzs_result_t result;
107
+ } decompress_args_t;
108
+
109
+ static inline void* decompress_wrapper(void* data)
110
+ {
111
+ decompress_args_t* args = data;
112
+
113
+ args->stream_ptr->next_in = (char*) *args->remaining_source_ptr;
114
+ args->stream_ptr->avail_in = bzs_consume_size(*args->remaining_source_length_ptr);
115
+ args->stream_ptr->next_out = (char*) *args->remaining_destination_buffer_ptr;
116
+ args->stream_ptr->avail_out = bzs_consume_size(*args->remaining_destination_buffer_length_ptr);
117
+
118
+ args->result = BZ2_bzDecompress(args->stream_ptr);
119
+
120
+ *args->remaining_source_ptr = (bzs_ext_byte_t*) args->stream_ptr->next_in;
121
+ *args->remaining_source_length_ptr = args->stream_ptr->avail_in;
122
+ *args->remaining_destination_buffer_ptr = (bzs_ext_byte_t*) args->stream_ptr->next_out;
123
+ *args->remaining_destination_buffer_length_ptr = args->stream_ptr->avail_out;
124
+
125
+ return NULL;
126
+ }
127
+
128
+ VALUE bzs_ext_decompress(VALUE self, VALUE source_value)
129
+ {
130
+ GET_DECOMPRESSOR(self);
131
+ DO_NOT_USE_AFTER_CLOSE(decompressor_ptr);
132
+ Check_Type(source_value, T_STRING);
133
+
134
+ const char* source = RSTRING_PTR(source_value);
135
+ size_t source_length = RSTRING_LEN(source_value);
136
+ bzs_ext_byte_t* remaining_source = (bzs_ext_byte_t*) source;
137
+ size_t remaining_source_length = source_length;
138
+
139
+ decompress_args_t args = {
140
+ .stream_ptr = decompressor_ptr->stream_ptr,
141
+ .remaining_source_ptr = &remaining_source,
142
+ .remaining_source_length_ptr = &remaining_source_length,
143
+ .remaining_destination_buffer_ptr = &decompressor_ptr->remaining_destination_buffer,
144
+ .remaining_destination_buffer_length_ptr = &decompressor_ptr->remaining_destination_buffer_length};
145
+
146
+ BZS_EXT_GVL_WRAP(decompressor_ptr->gvl, decompress_wrapper, &args);
147
+ if (args.result != BZ_OK && args.result != BZ_PARAM_ERROR && args.result != BZ_STREAM_END) {
148
+ bzs_ext_raise_error(bzs_ext_get_error(args.result));
149
+ }
150
+
151
+ VALUE bytes_read = SIZET2NUM(source_length - remaining_source_length);
152
+ VALUE needs_more_destination = args.result == BZ_OK && (remaining_source_length != 0 ||
153
+ decompressor_ptr->remaining_destination_buffer_length == 0) ?
154
+ Qtrue :
155
+ Qfalse;
156
+
157
+ return rb_ary_new_from_args(2, bytes_read, needs_more_destination);
158
+ }
159
+
160
+ // -- other --
161
+
162
+ VALUE bzs_ext_decompressor_read_result(VALUE self)
163
+ {
164
+ GET_DECOMPRESSOR(self);
165
+ DO_NOT_USE_AFTER_CLOSE(decompressor_ptr);
166
+
167
+ bzs_ext_byte_t* destination_buffer = decompressor_ptr->destination_buffer;
168
+ size_t destination_buffer_length = decompressor_ptr->destination_buffer_length;
169
+ size_t remaining_destination_buffer_length = decompressor_ptr->remaining_destination_buffer_length;
170
+
171
+ const char* result = (const char*) destination_buffer;
172
+ size_t result_length = destination_buffer_length - remaining_destination_buffer_length;
173
+ VALUE result_value = rb_str_new(result, result_length);
174
+
175
+ decompressor_ptr->remaining_destination_buffer = destination_buffer;
176
+ decompressor_ptr->remaining_destination_buffer_length = destination_buffer_length;
177
+
178
+ return result_value;
179
+ }
180
+
181
+ // -- cleanup --
182
+
183
+ VALUE bzs_ext_decompressor_close(VALUE self)
184
+ {
185
+ GET_DECOMPRESSOR(self);
186
+ DO_NOT_USE_AFTER_CLOSE(decompressor_ptr);
187
+
188
+ bz_stream* stream_ptr = decompressor_ptr->stream_ptr;
189
+ if (stream_ptr != NULL) {
190
+ BZ2_bzDecompressEnd(stream_ptr);
191
+
192
+ decompressor_ptr->stream_ptr = NULL;
193
+ }
194
+
195
+ bzs_ext_byte_t* destination_buffer = decompressor_ptr->destination_buffer;
196
+ if (destination_buffer != NULL) {
197
+ free(destination_buffer);
198
+
199
+ decompressor_ptr->destination_buffer = NULL;
200
+ }
201
+
202
+ // It is possible to keep "destination_buffer_length", "remaining_destination_buffer"
203
+ // and "remaining_destination_buffer_length" as is.
204
+
205
+ return Qnil;
206
+ }
207
+
208
+ // -- exports --
209
+
210
+ void bzs_ext_decompressor_exports(VALUE root_module)
211
+ {
212
+ VALUE module = rb_define_module_under(root_module, "Stream");
213
+
214
+ VALUE decompressor = rb_define_class_under(module, "NativeDecompressor", rb_cObject);
215
+
216
+ rb_define_alloc_func(decompressor, bzs_ext_allocate_decompressor);
217
+ rb_define_method(decompressor, "initialize", bzs_ext_initialize_decompressor, 1);
218
+ rb_define_method(decompressor, "read", bzs_ext_decompress, 1);
219
+ rb_define_method(decompressor, "read_result", bzs_ext_decompressor_read_result, 0);
220
+ rb_define_method(decompressor, "close", bzs_ext_decompressor_close, 0);
221
+ }