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