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.
- checksums.yaml +7 -0
- data/AUTHORS +1 -0
- data/LICENSE +21 -0
- data/README.md +368 -0
- data/ext/bzs_ext/buffer.c +40 -0
- data/ext/bzs_ext/buffer.h +29 -0
- data/ext/bzs_ext/common.h +16 -0
- data/ext/bzs_ext/error.c +62 -0
- data/ext/bzs_ext/error.h +34 -0
- data/ext/bzs_ext/gvl.h +24 -0
- data/ext/bzs_ext/io.c +592 -0
- data/ext/bzs_ext/io.h +14 -0
- data/ext/bzs_ext/macro.h +13 -0
- data/ext/bzs_ext/main.c +27 -0
- data/ext/bzs_ext/option.c +98 -0
- data/ext/bzs_ext/option.h +62 -0
- data/ext/bzs_ext/stream/compressor.c +285 -0
- data/ext/bzs_ext/stream/compressor.h +33 -0
- data/ext/bzs_ext/stream/decompressor.c +221 -0
- data/ext/bzs_ext/stream/decompressor.h +31 -0
- data/ext/bzs_ext/string.c +340 -0
- data/ext/bzs_ext/string.h +14 -0
- data/ext/bzs_ext/utils.c +15 -0
- data/ext/bzs_ext/utils.h +13 -0
- data/ext/extconf.rb +94 -0
- data/lib/bzs/error.rb +26 -0
- data/lib/bzs/file.rb +25 -0
- data/lib/bzs/option.rb +101 -0
- data/lib/bzs/stream/raw/compressor.rb +22 -0
- data/lib/bzs/stream/raw/decompressor.rb +22 -0
- data/lib/bzs/stream/reader.rb +16 -0
- data/lib/bzs/stream/writer.rb +16 -0
- data/lib/bzs/string.rb +25 -0
- data/lib/bzs/validation.rb +14 -0
- data/lib/bzs/version.rb +6 -0
- data/lib/bzs.rb +8 -0
- metadata +289 -0
data/ext/bzs_ext/io.c
ADDED
@@ -0,0 +1,592 @@
|
|
1
|
+
// Ruby bindings for bzip2 library.
|
2
|
+
// Copyright (c) 2022 AUTHORS, MIT License.
|
3
|
+
|
4
|
+
#include "bzs_ext/io.h"
|
5
|
+
|
6
|
+
#include <bzlib.h>
|
7
|
+
#include <stdio.h>
|
8
|
+
#include <string.h>
|
9
|
+
|
10
|
+
#include "bzs_ext/buffer.h"
|
11
|
+
#include "bzs_ext/error.h"
|
12
|
+
#include "bzs_ext/gvl.h"
|
13
|
+
#include "bzs_ext/macro.h"
|
14
|
+
#include "bzs_ext/option.h"
|
15
|
+
#include "bzs_ext/utils.h"
|
16
|
+
#include "ruby/io.h"
|
17
|
+
|
18
|
+
// Additional possible results:
|
19
|
+
enum
|
20
|
+
{
|
21
|
+
BZS_EXT_FILE_READ_FINISHED = 128
|
22
|
+
};
|
23
|
+
|
24
|
+
// -- file --
|
25
|
+
|
26
|
+
static inline bzs_ext_result_t
|
27
|
+
read_file(FILE* source_file, bzs_ext_byte_t* source_buffer, size_t* source_length_ptr, size_t source_buffer_length)
|
28
|
+
{
|
29
|
+
size_t read_length = fread(source_buffer, 1, source_buffer_length, source_file);
|
30
|
+
if (read_length == 0 && feof(source_file)) {
|
31
|
+
return BZS_EXT_FILE_READ_FINISHED;
|
32
|
+
}
|
33
|
+
|
34
|
+
if (read_length != source_buffer_length && ferror(source_file)) {
|
35
|
+
return BZS_EXT_ERROR_READ_IO;
|
36
|
+
}
|
37
|
+
|
38
|
+
*source_length_ptr = read_length;
|
39
|
+
|
40
|
+
return 0;
|
41
|
+
}
|
42
|
+
|
43
|
+
static inline bzs_ext_result_t
|
44
|
+
write_file(FILE* destination_file, bzs_ext_byte_t* destination_buffer, size_t destination_length)
|
45
|
+
{
|
46
|
+
size_t written_length = fwrite(destination_buffer, 1, destination_length, destination_file);
|
47
|
+
if (written_length != destination_length) {
|
48
|
+
return BZS_EXT_ERROR_WRITE_IO;
|
49
|
+
}
|
50
|
+
|
51
|
+
return 0;
|
52
|
+
}
|
53
|
+
|
54
|
+
// -- buffer --
|
55
|
+
|
56
|
+
static inline bzs_ext_result_t create_buffers(
|
57
|
+
bzs_ext_byte_t** source_buffer_ptr,
|
58
|
+
size_t source_buffer_length,
|
59
|
+
bzs_ext_byte_t** destination_buffer_ptr,
|
60
|
+
size_t destination_buffer_length)
|
61
|
+
{
|
62
|
+
bzs_ext_byte_t* source_buffer = malloc(source_buffer_length);
|
63
|
+
if (source_buffer == NULL) {
|
64
|
+
return BZS_EXT_ERROR_ALLOCATE_FAILED;
|
65
|
+
}
|
66
|
+
|
67
|
+
bzs_ext_byte_t* destination_buffer = malloc(destination_buffer_length);
|
68
|
+
if (destination_buffer == NULL) {
|
69
|
+
free(source_buffer);
|
70
|
+
return BZS_EXT_ERROR_ALLOCATE_FAILED;
|
71
|
+
}
|
72
|
+
|
73
|
+
*source_buffer_ptr = source_buffer;
|
74
|
+
*destination_buffer_ptr = destination_buffer;
|
75
|
+
|
76
|
+
return 0;
|
77
|
+
}
|
78
|
+
|
79
|
+
// We have read some source from file into source buffer.
|
80
|
+
// Than algorithm has read part of this source.
|
81
|
+
// We need to move remaining source to the top of source buffer.
|
82
|
+
// Than we can read more source from file.
|
83
|
+
// Algorithm can use same buffer again.
|
84
|
+
|
85
|
+
static inline bzs_ext_result_t read_more_source(
|
86
|
+
FILE* source_file,
|
87
|
+
const bzs_ext_byte_t** source_ptr,
|
88
|
+
size_t* source_length_ptr,
|
89
|
+
bzs_ext_byte_t* source_buffer,
|
90
|
+
size_t source_buffer_length)
|
91
|
+
{
|
92
|
+
const bzs_ext_byte_t* source = *source_ptr;
|
93
|
+
size_t source_length = *source_length_ptr;
|
94
|
+
|
95
|
+
if (source != source_buffer) {
|
96
|
+
if (source_length != 0) {
|
97
|
+
memmove(source_buffer, source, source_length);
|
98
|
+
}
|
99
|
+
|
100
|
+
// Source can be accessed even if next code will fail.
|
101
|
+
*source_ptr = source_buffer;
|
102
|
+
}
|
103
|
+
|
104
|
+
size_t remaining_source_buffer_length = source_buffer_length - source_length;
|
105
|
+
if (remaining_source_buffer_length == 0) {
|
106
|
+
// We want to read more data at once, than buffer has.
|
107
|
+
return BZS_EXT_ERROR_NOT_ENOUGH_SOURCE_BUFFER;
|
108
|
+
}
|
109
|
+
|
110
|
+
bzs_ext_byte_t* remaining_source_buffer = source_buffer + source_length;
|
111
|
+
size_t new_source_length;
|
112
|
+
|
113
|
+
bzs_ext_result_t ext_result =
|
114
|
+
read_file(source_file, remaining_source_buffer, &new_source_length, remaining_source_buffer_length);
|
115
|
+
|
116
|
+
if (ext_result != 0) {
|
117
|
+
return ext_result;
|
118
|
+
}
|
119
|
+
|
120
|
+
*source_length_ptr = source_length + new_source_length;
|
121
|
+
|
122
|
+
return 0;
|
123
|
+
}
|
124
|
+
|
125
|
+
#define BUFFERED_READ_SOURCE(function, ...) \
|
126
|
+
do { \
|
127
|
+
bool is_function_called = false; \
|
128
|
+
\
|
129
|
+
while (true) { \
|
130
|
+
ext_result = read_more_source(source_file, &source, &source_length, source_buffer, source_buffer_length); \
|
131
|
+
if (ext_result == BZS_EXT_FILE_READ_FINISHED) { \
|
132
|
+
break; \
|
133
|
+
} else if (ext_result != 0) { \
|
134
|
+
return ext_result; \
|
135
|
+
} \
|
136
|
+
\
|
137
|
+
ext_result = function(__VA_ARGS__); \
|
138
|
+
if (ext_result != 0) { \
|
139
|
+
return ext_result; \
|
140
|
+
} \
|
141
|
+
\
|
142
|
+
is_function_called = true; \
|
143
|
+
} \
|
144
|
+
\
|
145
|
+
if (!is_function_called) { \
|
146
|
+
/* Function should be called at least once. */ \
|
147
|
+
ext_result = function(__VA_ARGS__); \
|
148
|
+
if (ext_result != 0) { \
|
149
|
+
return ext_result; \
|
150
|
+
} \
|
151
|
+
} \
|
152
|
+
} while (false);
|
153
|
+
|
154
|
+
// Algorithm has written data into destination buffer.
|
155
|
+
// We need to write this data into file.
|
156
|
+
// Than algorithm can use same buffer again.
|
157
|
+
|
158
|
+
static inline bzs_ext_result_t flush_destination_buffer(
|
159
|
+
FILE* destination_file,
|
160
|
+
bzs_ext_byte_t* destination_buffer,
|
161
|
+
size_t* destination_length_ptr,
|
162
|
+
size_t destination_buffer_length)
|
163
|
+
{
|
164
|
+
if (*destination_length_ptr == 0) {
|
165
|
+
// We want to write more data at once, than buffer has.
|
166
|
+
return BZS_EXT_ERROR_NOT_ENOUGH_DESTINATION_BUFFER;
|
167
|
+
}
|
168
|
+
|
169
|
+
bzs_ext_result_t ext_result = write_file(destination_file, destination_buffer, *destination_length_ptr);
|
170
|
+
if (ext_result != 0) {
|
171
|
+
return ext_result;
|
172
|
+
}
|
173
|
+
|
174
|
+
*destination_length_ptr = 0;
|
175
|
+
|
176
|
+
return 0;
|
177
|
+
}
|
178
|
+
|
179
|
+
static inline bzs_ext_result_t
|
180
|
+
write_remaining_destination(FILE* destination_file, bzs_ext_byte_t* destination_buffer, size_t destination_length)
|
181
|
+
{
|
182
|
+
if (destination_length == 0) {
|
183
|
+
return 0;
|
184
|
+
}
|
185
|
+
|
186
|
+
return write_file(destination_file, destination_buffer, destination_length);
|
187
|
+
}
|
188
|
+
|
189
|
+
// -- utils --
|
190
|
+
|
191
|
+
#define GET_FILE(target) \
|
192
|
+
Check_Type(target, T_FILE); \
|
193
|
+
\
|
194
|
+
rb_io_t* target##_io; \
|
195
|
+
GetOpenFile(target, target##_io); \
|
196
|
+
\
|
197
|
+
FILE* target##_file = rb_io_stdio_file(target##_io); \
|
198
|
+
if (target##_file == NULL) { \
|
199
|
+
bzs_ext_raise_error(BZS_EXT_ERROR_ACCESS_IO); \
|
200
|
+
}
|
201
|
+
|
202
|
+
// -- buffered compress --
|
203
|
+
|
204
|
+
typedef struct
|
205
|
+
{
|
206
|
+
bz_stream* stream_ptr;
|
207
|
+
int stream_action;
|
208
|
+
bzs_ext_byte_t** remaining_source_ptr;
|
209
|
+
size_t* remaining_source_length_ptr;
|
210
|
+
bzs_ext_byte_t* remaining_destination_buffer;
|
211
|
+
size_t* remaining_destination_buffer_length_ptr;
|
212
|
+
bzs_result_t result;
|
213
|
+
} compress_args_t;
|
214
|
+
|
215
|
+
static inline void* compress_wrapper(void* data)
|
216
|
+
{
|
217
|
+
compress_args_t* args = data;
|
218
|
+
|
219
|
+
args->stream_ptr->next_in = (char*) *args->remaining_source_ptr;
|
220
|
+
args->stream_ptr->avail_in = bzs_consume_size(*args->remaining_source_length_ptr);
|
221
|
+
args->stream_ptr->next_out = (char*) args->remaining_destination_buffer;
|
222
|
+
args->stream_ptr->avail_out = bzs_consume_size(*args->remaining_destination_buffer_length_ptr);
|
223
|
+
|
224
|
+
args->result = BZ2_bzCompress(args->stream_ptr, args->stream_action);
|
225
|
+
|
226
|
+
*args->remaining_source_ptr = (bzs_ext_byte_t*) args->stream_ptr->next_in;
|
227
|
+
*args->remaining_source_length_ptr = args->stream_ptr->avail_in;
|
228
|
+
*args->remaining_destination_buffer_length_ptr = args->stream_ptr->avail_out;
|
229
|
+
|
230
|
+
return NULL;
|
231
|
+
}
|
232
|
+
|
233
|
+
#define BUFFERED_COMPRESS(gvl, args, RUN_OK) \
|
234
|
+
bzs_ext_result_t ext_result; \
|
235
|
+
\
|
236
|
+
while (true) { \
|
237
|
+
bzs_ext_byte_t* remaining_destination_buffer = destination_buffer + *destination_length_ptr; \
|
238
|
+
size_t remaining_destination_buffer_length = destination_buffer_length - *destination_length_ptr; \
|
239
|
+
size_t prev_remaining_destination_buffer_length = remaining_destination_buffer_length; \
|
240
|
+
\
|
241
|
+
args.remaining_destination_buffer = remaining_destination_buffer; \
|
242
|
+
args.remaining_destination_buffer_length_ptr = &remaining_destination_buffer_length; \
|
243
|
+
\
|
244
|
+
BZS_EXT_GVL_WRAP(gvl, compress_wrapper, &args); \
|
245
|
+
if (args.result != RUN_OK && args.result != BZ_PARAM_ERROR && args.result != BZ_STREAM_END) { \
|
246
|
+
return bzs_ext_get_error(args.result); \
|
247
|
+
} \
|
248
|
+
\
|
249
|
+
*destination_length_ptr += prev_remaining_destination_buffer_length - remaining_destination_buffer_length; \
|
250
|
+
\
|
251
|
+
if (args.result == BZ_STREAM_END) { \
|
252
|
+
break; \
|
253
|
+
} \
|
254
|
+
\
|
255
|
+
if (*args.remaining_source_length_ptr != 0 || remaining_destination_buffer_length == 0) { \
|
256
|
+
ext_result = flush_destination_buffer( \
|
257
|
+
destination_file, destination_buffer, destination_length_ptr, destination_buffer_length); \
|
258
|
+
\
|
259
|
+
if (ext_result != 0) { \
|
260
|
+
return ext_result; \
|
261
|
+
} \
|
262
|
+
\
|
263
|
+
continue; \
|
264
|
+
} \
|
265
|
+
\
|
266
|
+
break; \
|
267
|
+
} \
|
268
|
+
\
|
269
|
+
return 0;
|
270
|
+
|
271
|
+
static inline bzs_ext_result_t buffered_compress(
|
272
|
+
bz_stream* stream_ptr,
|
273
|
+
const bzs_ext_byte_t** source_ptr,
|
274
|
+
size_t* source_length_ptr,
|
275
|
+
FILE* destination_file,
|
276
|
+
bzs_ext_byte_t* destination_buffer,
|
277
|
+
size_t* destination_length_ptr,
|
278
|
+
size_t destination_buffer_length,
|
279
|
+
bool gvl)
|
280
|
+
{
|
281
|
+
compress_args_t run_args = {
|
282
|
+
.stream_ptr = stream_ptr,
|
283
|
+
.stream_action = BZ_RUN,
|
284
|
+
.remaining_source_ptr = (bzs_ext_byte_t**) source_ptr,
|
285
|
+
.remaining_source_length_ptr = source_length_ptr};
|
286
|
+
BUFFERED_COMPRESS(gvl, run_args, BZ_RUN_OK);
|
287
|
+
}
|
288
|
+
|
289
|
+
// -- buffered compressor finish --
|
290
|
+
|
291
|
+
static inline bzs_ext_result_t buffered_compressor_finish(
|
292
|
+
bz_stream* stream_ptr,
|
293
|
+
FILE* destination_file,
|
294
|
+
bzs_ext_byte_t* destination_buffer,
|
295
|
+
size_t* destination_length_ptr,
|
296
|
+
size_t destination_buffer_length,
|
297
|
+
bool gvl)
|
298
|
+
{
|
299
|
+
bzs_ext_byte_t* remaining_source = NULL;
|
300
|
+
size_t remaining_source_length = 0;
|
301
|
+
|
302
|
+
compress_args_t finish_args = {
|
303
|
+
.stream_ptr = stream_ptr,
|
304
|
+
.stream_action = BZ_FINISH,
|
305
|
+
.remaining_source_ptr = &remaining_source,
|
306
|
+
.remaining_source_length_ptr = &remaining_source_length};
|
307
|
+
BUFFERED_COMPRESS(gvl, finish_args, BZ_FINISH_OK);
|
308
|
+
}
|
309
|
+
|
310
|
+
// -- compress --
|
311
|
+
|
312
|
+
static inline bzs_ext_result_t compress(
|
313
|
+
bz_stream* stream_ptr,
|
314
|
+
FILE* source_file,
|
315
|
+
bzs_ext_byte_t* source_buffer,
|
316
|
+
size_t source_buffer_length,
|
317
|
+
FILE* destination_file,
|
318
|
+
bzs_ext_byte_t* destination_buffer,
|
319
|
+
size_t destination_buffer_length,
|
320
|
+
bool gvl)
|
321
|
+
{
|
322
|
+
bzs_ext_result_t ext_result;
|
323
|
+
const bzs_ext_byte_t* source = source_buffer;
|
324
|
+
size_t source_length = 0;
|
325
|
+
size_t destination_length = 0;
|
326
|
+
|
327
|
+
BUFFERED_READ_SOURCE(
|
328
|
+
buffered_compress,
|
329
|
+
stream_ptr,
|
330
|
+
&source,
|
331
|
+
&source_length,
|
332
|
+
destination_file,
|
333
|
+
destination_buffer,
|
334
|
+
&destination_length,
|
335
|
+
destination_buffer_length,
|
336
|
+
gvl);
|
337
|
+
|
338
|
+
ext_result = buffered_compressor_finish(
|
339
|
+
stream_ptr, destination_file, destination_buffer, &destination_length, destination_buffer_length, gvl);
|
340
|
+
|
341
|
+
if (ext_result != 0) {
|
342
|
+
return ext_result;
|
343
|
+
}
|
344
|
+
|
345
|
+
return write_remaining_destination(destination_file, destination_buffer, destination_length);
|
346
|
+
}
|
347
|
+
|
348
|
+
VALUE bzs_ext_compress_io(VALUE BZS_EXT_UNUSED(self), VALUE source, VALUE destination, VALUE options)
|
349
|
+
{
|
350
|
+
GET_FILE(source);
|
351
|
+
GET_FILE(destination);
|
352
|
+
Check_Type(options, T_HASH);
|
353
|
+
BZS_EXT_GET_SIZE_OPTION(options, source_buffer_length);
|
354
|
+
BZS_EXT_GET_SIZE_OPTION(options, destination_buffer_length);
|
355
|
+
BZS_EXT_GET_BOOL_OPTION(options, gvl);
|
356
|
+
BZS_EXT_RESOLVE_COMPRESSOR_OPTIONS(options);
|
357
|
+
|
358
|
+
bz_stream stream = {
|
359
|
+
.bzalloc = NULL,
|
360
|
+
.bzfree = NULL,
|
361
|
+
.opaque = NULL,
|
362
|
+
};
|
363
|
+
|
364
|
+
bzs_result_t result = BZ2_bzCompressInit(&stream, block_size, verbosity, work_factor);
|
365
|
+
if (result != BZ_OK) {
|
366
|
+
bzs_ext_raise_error(bzs_ext_get_error(result));
|
367
|
+
}
|
368
|
+
|
369
|
+
if (source_buffer_length == 0) {
|
370
|
+
source_buffer_length = BZS_DEFAULT_SOURCE_BUFFER_LENGTH_FOR_COMPRESSOR;
|
371
|
+
}
|
372
|
+
if (destination_buffer_length == 0) {
|
373
|
+
destination_buffer_length = BZS_DEFAULT_DESTINATION_BUFFER_LENGTH_FOR_COMPRESSOR;
|
374
|
+
}
|
375
|
+
|
376
|
+
bzs_ext_byte_t* source_buffer;
|
377
|
+
bzs_ext_byte_t* destination_buffer;
|
378
|
+
|
379
|
+
bzs_ext_result_t ext_result =
|
380
|
+
create_buffers(&source_buffer, source_buffer_length, &destination_buffer, destination_buffer_length);
|
381
|
+
if (ext_result != 0) {
|
382
|
+
BZ2_bzCompressEnd(&stream);
|
383
|
+
bzs_ext_raise_error(ext_result);
|
384
|
+
}
|
385
|
+
|
386
|
+
ext_result = compress(
|
387
|
+
&stream,
|
388
|
+
source_file,
|
389
|
+
source_buffer,
|
390
|
+
source_buffer_length,
|
391
|
+
destination_file,
|
392
|
+
destination_buffer,
|
393
|
+
destination_buffer_length,
|
394
|
+
gvl);
|
395
|
+
|
396
|
+
free(source_buffer);
|
397
|
+
free(destination_buffer);
|
398
|
+
BZ2_bzCompressEnd(&stream);
|
399
|
+
|
400
|
+
if (ext_result != 0) {
|
401
|
+
bzs_ext_raise_error(ext_result);
|
402
|
+
}
|
403
|
+
|
404
|
+
// Ruby itself won't flush stdio file before closing fd, flush is required.
|
405
|
+
fflush(destination_file);
|
406
|
+
|
407
|
+
return Qnil;
|
408
|
+
}
|
409
|
+
|
410
|
+
// -- buffered decompress --
|
411
|
+
|
412
|
+
typedef struct
|
413
|
+
{
|
414
|
+
bz_stream* stream_ptr;
|
415
|
+
bzs_ext_byte_t** remaining_source_ptr;
|
416
|
+
size_t* remaining_source_length_ptr;
|
417
|
+
bzs_ext_byte_t* remaining_destination_buffer;
|
418
|
+
size_t* remaining_destination_buffer_length_ptr;
|
419
|
+
bzs_result_t result;
|
420
|
+
} decompress_args_t;
|
421
|
+
|
422
|
+
static inline void* decompress_wrapper(void* data)
|
423
|
+
{
|
424
|
+
decompress_args_t* args = data;
|
425
|
+
|
426
|
+
args->stream_ptr->next_in = (char*) *args->remaining_source_ptr;
|
427
|
+
args->stream_ptr->avail_in = bzs_consume_size(*args->remaining_source_length_ptr);
|
428
|
+
args->stream_ptr->next_out = (char*) args->remaining_destination_buffer;
|
429
|
+
args->stream_ptr->avail_out = bzs_consume_size(*args->remaining_destination_buffer_length_ptr);
|
430
|
+
|
431
|
+
args->result = BZ2_bzDecompress(args->stream_ptr);
|
432
|
+
|
433
|
+
*args->remaining_source_ptr = (bzs_ext_byte_t*) args->stream_ptr->next_in;
|
434
|
+
*args->remaining_source_length_ptr = args->stream_ptr->avail_in;
|
435
|
+
*args->remaining_destination_buffer_length_ptr = args->stream_ptr->avail_out;
|
436
|
+
|
437
|
+
return NULL;
|
438
|
+
}
|
439
|
+
|
440
|
+
static inline bzs_ext_result_t buffered_decompress(
|
441
|
+
bz_stream* stream_ptr,
|
442
|
+
const bzs_ext_byte_t** source_ptr,
|
443
|
+
size_t* source_length_ptr,
|
444
|
+
FILE* destination_file,
|
445
|
+
bzs_ext_byte_t* destination_buffer,
|
446
|
+
size_t* destination_length_ptr,
|
447
|
+
size_t destination_buffer_length,
|
448
|
+
bool gvl)
|
449
|
+
{
|
450
|
+
bzs_ext_result_t ext_result;
|
451
|
+
|
452
|
+
decompress_args_t args = {
|
453
|
+
.stream_ptr = stream_ptr,
|
454
|
+
.remaining_source_ptr = (bzs_ext_byte_t**) source_ptr,
|
455
|
+
.remaining_source_length_ptr = source_length_ptr};
|
456
|
+
|
457
|
+
while (true) {
|
458
|
+
bzs_ext_byte_t* remaining_destination_buffer = destination_buffer + *destination_length_ptr;
|
459
|
+
size_t remaining_destination_buffer_length = destination_buffer_length - *destination_length_ptr;
|
460
|
+
size_t prev_remaining_destination_buffer_length = remaining_destination_buffer_length;
|
461
|
+
|
462
|
+
args.remaining_destination_buffer = remaining_destination_buffer;
|
463
|
+
args.remaining_destination_buffer_length_ptr = &remaining_destination_buffer_length;
|
464
|
+
|
465
|
+
BZS_EXT_GVL_WRAP(gvl, decompress_wrapper, &args);
|
466
|
+
if (args.result != BZ_OK && args.result != BZ_PARAM_ERROR && args.result != BZ_STREAM_END) {
|
467
|
+
return bzs_ext_get_error(args.result);
|
468
|
+
}
|
469
|
+
|
470
|
+
*destination_length_ptr += prev_remaining_destination_buffer_length - remaining_destination_buffer_length;
|
471
|
+
|
472
|
+
if (args.result == BZ_STREAM_END) {
|
473
|
+
break;
|
474
|
+
}
|
475
|
+
|
476
|
+
if (*args.remaining_source_length_ptr != 0 || remaining_destination_buffer_length == 0) {
|
477
|
+
ext_result = flush_destination_buffer(
|
478
|
+
destination_file, destination_buffer, destination_length_ptr, destination_buffer_length);
|
479
|
+
|
480
|
+
if (ext_result != 0) {
|
481
|
+
return ext_result;
|
482
|
+
}
|
483
|
+
|
484
|
+
continue;
|
485
|
+
}
|
486
|
+
|
487
|
+
break;
|
488
|
+
}
|
489
|
+
|
490
|
+
return 0;
|
491
|
+
}
|
492
|
+
|
493
|
+
// -- decompress --
|
494
|
+
|
495
|
+
static inline bzs_ext_result_t decompress(
|
496
|
+
bz_stream* stream_ptr,
|
497
|
+
FILE* source_file,
|
498
|
+
bzs_ext_byte_t* source_buffer,
|
499
|
+
size_t source_buffer_length,
|
500
|
+
FILE* destination_file,
|
501
|
+
bzs_ext_byte_t* destination_buffer,
|
502
|
+
size_t destination_buffer_length,
|
503
|
+
bool gvl)
|
504
|
+
{
|
505
|
+
bzs_ext_result_t ext_result;
|
506
|
+
const bzs_ext_byte_t* source = source_buffer;
|
507
|
+
size_t source_length = 0;
|
508
|
+
size_t destination_length = 0;
|
509
|
+
|
510
|
+
BUFFERED_READ_SOURCE(
|
511
|
+
buffered_decompress,
|
512
|
+
stream_ptr,
|
513
|
+
&source,
|
514
|
+
&source_length,
|
515
|
+
destination_file,
|
516
|
+
destination_buffer,
|
517
|
+
&destination_length,
|
518
|
+
destination_buffer_length,
|
519
|
+
gvl);
|
520
|
+
|
521
|
+
return write_remaining_destination(destination_file, destination_buffer, destination_length);
|
522
|
+
}
|
523
|
+
|
524
|
+
VALUE bzs_ext_decompress_io(VALUE BZS_EXT_UNUSED(self), VALUE source, VALUE destination, VALUE options)
|
525
|
+
{
|
526
|
+
GET_FILE(source);
|
527
|
+
GET_FILE(destination);
|
528
|
+
Check_Type(options, T_HASH);
|
529
|
+
BZS_EXT_GET_SIZE_OPTION(options, source_buffer_length);
|
530
|
+
BZS_EXT_GET_SIZE_OPTION(options, destination_buffer_length);
|
531
|
+
BZS_EXT_GET_BOOL_OPTION(options, gvl);
|
532
|
+
BZS_EXT_RESOLVE_DECOMPRESSOR_OPTIONS(options);
|
533
|
+
|
534
|
+
bz_stream stream = {
|
535
|
+
.bzalloc = NULL,
|
536
|
+
.bzfree = NULL,
|
537
|
+
.opaque = NULL,
|
538
|
+
};
|
539
|
+
|
540
|
+
bzs_result_t result = BZ2_bzDecompressInit(&stream, verbosity, small);
|
541
|
+
if (result != BZ_OK) {
|
542
|
+
bzs_ext_raise_error(bzs_ext_get_error(result));
|
543
|
+
}
|
544
|
+
|
545
|
+
if (source_buffer_length == 0) {
|
546
|
+
source_buffer_length = BZS_DEFAULT_SOURCE_BUFFER_LENGTH_FOR_DECOMPRESSOR;
|
547
|
+
}
|
548
|
+
if (destination_buffer_length == 0) {
|
549
|
+
destination_buffer_length = BZS_DEFAULT_DESTINATION_BUFFER_LENGTH_FOR_DECOMPRESSOR;
|
550
|
+
}
|
551
|
+
|
552
|
+
bzs_ext_byte_t* source_buffer;
|
553
|
+
bzs_ext_byte_t* destination_buffer;
|
554
|
+
|
555
|
+
bzs_ext_result_t ext_result =
|
556
|
+
create_buffers(&source_buffer, source_buffer_length, &destination_buffer, destination_buffer_length);
|
557
|
+
if (ext_result != 0) {
|
558
|
+
BZ2_bzDecompressEnd(&stream);
|
559
|
+
bzs_ext_raise_error(ext_result);
|
560
|
+
}
|
561
|
+
|
562
|
+
ext_result = decompress(
|
563
|
+
&stream,
|
564
|
+
source_file,
|
565
|
+
source_buffer,
|
566
|
+
source_buffer_length,
|
567
|
+
destination_file,
|
568
|
+
destination_buffer,
|
569
|
+
destination_buffer_length,
|
570
|
+
gvl);
|
571
|
+
|
572
|
+
free(source_buffer);
|
573
|
+
free(destination_buffer);
|
574
|
+
BZ2_bzDecompressEnd(&stream);
|
575
|
+
|
576
|
+
if (ext_result != 0) {
|
577
|
+
bzs_ext_raise_error(ext_result);
|
578
|
+
}
|
579
|
+
|
580
|
+
// Ruby itself won't flush stdio file before closing fd, flush is required.
|
581
|
+
fflush(destination_file);
|
582
|
+
|
583
|
+
return Qnil;
|
584
|
+
}
|
585
|
+
|
586
|
+
// -- exports --
|
587
|
+
|
588
|
+
void bzs_ext_io_exports(VALUE root_module)
|
589
|
+
{
|
590
|
+
rb_define_module_function(root_module, "_native_compress_io", RUBY_METHOD_FUNC(bzs_ext_compress_io), 3);
|
591
|
+
rb_define_module_function(root_module, "_native_decompress_io", RUBY_METHOD_FUNC(bzs_ext_decompress_io), 3);
|
592
|
+
}
|
data/ext/bzs_ext/io.h
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
// Ruby bindings for bzip2 library.
|
2
|
+
// Copyright (c) 2022 AUTHORS, MIT License.
|
3
|
+
|
4
|
+
#if !defined(BZS_EXT_IO_H)
|
5
|
+
#define BZS_EXT_IO_H
|
6
|
+
|
7
|
+
#include "ruby.h"
|
8
|
+
|
9
|
+
VALUE bzs_ext_compress_io(VALUE self, VALUE source, VALUE destination, VALUE options);
|
10
|
+
VALUE bzs_ext_decompress_io(VALUE self, VALUE source, VALUE destination, VALUE options);
|
11
|
+
|
12
|
+
void bzs_ext_io_exports(VALUE root_module);
|
13
|
+
|
14
|
+
#endif // BZS_EXT_IO_H
|
data/ext/bzs_ext/macro.h
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
// Ruby bindings for bzip2 library.
|
2
|
+
// Copyright (c) 2022 AUTHORS, MIT License.
|
3
|
+
|
4
|
+
#if !defined(BZS_EXT_MACRO_H)
|
5
|
+
#define BZS_EXT_MACRO_H
|
6
|
+
|
7
|
+
#if defined(__GNUC__)
|
8
|
+
#define BZS_EXT_UNUSED(x) x __attribute__((__unused__))
|
9
|
+
#else
|
10
|
+
#define BZS_EXT_UNUSED(x) x
|
11
|
+
#endif // __GNUC__
|
12
|
+
|
13
|
+
#endif // BZS_EXT_MACRO_H
|
data/ext/bzs_ext/main.c
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
// Ruby bindings for bzip2 library.
|
2
|
+
// Copyright (c) 2022 AUTHORS, MIT License.
|
3
|
+
|
4
|
+
#include <bzlib.h>
|
5
|
+
|
6
|
+
#include "bzs_ext/buffer.h"
|
7
|
+
#include "bzs_ext/common.h"
|
8
|
+
#include "bzs_ext/io.h"
|
9
|
+
#include "bzs_ext/option.h"
|
10
|
+
#include "bzs_ext/stream/compressor.h"
|
11
|
+
#include "bzs_ext/stream/decompressor.h"
|
12
|
+
#include "bzs_ext/string.h"
|
13
|
+
|
14
|
+
void Init_bzs_ext()
|
15
|
+
{
|
16
|
+
VALUE root_module = rb_define_module(BZS_EXT_MODULE_NAME);
|
17
|
+
|
18
|
+
bzs_ext_buffer_exports(root_module);
|
19
|
+
bzs_ext_io_exports(root_module);
|
20
|
+
bzs_ext_option_exports(root_module);
|
21
|
+
bzs_ext_compressor_exports(root_module);
|
22
|
+
bzs_ext_decompressor_exports(root_module);
|
23
|
+
bzs_ext_string_exports(root_module);
|
24
|
+
|
25
|
+
VALUE version = rb_str_new2(BZ2_bzlibVersion());
|
26
|
+
rb_define_const(root_module, "LIBRARY_VERSION", version);
|
27
|
+
}
|