ruby-bzs 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }