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
@@ -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
|
+
}
|