ruby-brs 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,80 @@
1
+ // Ruby bindings for brotli library.
2
+ // Copyright (c) 2019 AUTHORS, MIT License.
3
+
4
+ #if !defined(BRS_EXT_OPTIONS_H)
5
+ #define BRS_EXT_OPTIONS_H
6
+
7
+ #include <brotli/decode.h>
8
+ #include <brotli/encode.h>
9
+ #include <stdbool.h>
10
+ #include <stdint.h>
11
+ #include <stdlib.h>
12
+
13
+ #include "brs_ext/common.h"
14
+ #include "ruby.h"
15
+
16
+ // Default option values depends on brotli library.
17
+ // We will set only user defined values.
18
+
19
+ enum {
20
+ BRS_EXT_OPTION_TYPE_BOOL = 1,
21
+ BRS_EXT_OPTION_TYPE_FIXNUM,
22
+ BRS_EXT_OPTION_TYPE_MODE
23
+ };
24
+
25
+ typedef uint_fast8_t brs_ext_option_type_t;
26
+ typedef uint32_t brs_ext_option_value_t;
27
+
28
+ typedef struct {
29
+ bool has_value;
30
+ brs_ext_option_value_t value;
31
+ } brs_ext_option_t;
32
+
33
+ typedef struct {
34
+ brs_ext_option_t mode;
35
+ brs_ext_option_t quality;
36
+ brs_ext_option_t lgwin;
37
+ brs_ext_option_t lgblock;
38
+ brs_ext_option_t disable_literal_context_modeling;
39
+ brs_ext_option_t size_hint;
40
+ brs_ext_option_t large_window;
41
+ } brs_ext_compressor_options_t;
42
+
43
+ typedef struct {
44
+ brs_ext_option_t disable_ring_buffer_reallocation;
45
+ brs_ext_option_t large_window;
46
+ } brs_ext_decompressor_options_t;
47
+
48
+ void brs_ext_get_option(VALUE options, brs_ext_option_t* option, brs_ext_option_type_t type, const char* name);
49
+
50
+ #define BRS_EXT_GET_OPTION(options, target_options, type, name) \
51
+ brs_ext_get_option(options, &target_options.name, type, #name);
52
+
53
+ #define BRS_EXT_GET_COMPRESSOR_OPTIONS(options) \
54
+ brs_ext_compressor_options_t compressor_options; \
55
+ \
56
+ BRS_EXT_GET_OPTION(options, compressor_options, BRS_EXT_OPTION_TYPE_MODE, mode); \
57
+ BRS_EXT_GET_OPTION(options, compressor_options, BRS_EXT_OPTION_TYPE_FIXNUM, quality); \
58
+ BRS_EXT_GET_OPTION(options, compressor_options, BRS_EXT_OPTION_TYPE_FIXNUM, lgwin); \
59
+ BRS_EXT_GET_OPTION(options, compressor_options, BRS_EXT_OPTION_TYPE_FIXNUM, lgblock); \
60
+ BRS_EXT_GET_OPTION(options, compressor_options, BRS_EXT_OPTION_TYPE_BOOL, disable_literal_context_modeling); \
61
+ BRS_EXT_GET_OPTION(options, compressor_options, BRS_EXT_OPTION_TYPE_FIXNUM, size_hint); \
62
+ BRS_EXT_GET_OPTION(options, compressor_options, BRS_EXT_OPTION_TYPE_BOOL, large_window);
63
+
64
+ #define BRS_EXT_GET_DECOMPRESSOR_OPTIONS(options) \
65
+ brs_ext_decompressor_options_t decompressor_options; \
66
+ \
67
+ BRS_EXT_GET_OPTION(options, decompressor_options, BRS_EXT_OPTION_TYPE_BOOL, disable_ring_buffer_reallocation); \
68
+ BRS_EXT_GET_OPTION(options, decompressor_options, BRS_EXT_OPTION_TYPE_BOOL, large_window);
69
+
70
+ unsigned long brs_ext_get_fixnum_option_value(VALUE options, const char* name);
71
+
72
+ #define BRS_EXT_GET_BUFFER_LENGTH_OPTION(options, name) \
73
+ size_t name = brs_ext_get_fixnum_option_value(options, #name);
74
+
75
+ brs_ext_result_t brs_ext_set_compressor_options(BrotliEncoderState* state_ptr, brs_ext_compressor_options_t* options);
76
+ brs_ext_result_t brs_ext_set_decompressor_options(BrotliDecoderState* state_ptr, brs_ext_decompressor_options_t* options);
77
+
78
+ void brs_ext_option_exports(VALUE root_module);
79
+
80
+ #endif // BRS_EXT_OPTIONS_H
@@ -0,0 +1,234 @@
1
+ // Ruby bindings for brotli library.
2
+ // Copyright (c) 2019 AUTHORS, MIT License.
3
+
4
+ #include "brs_ext/stream/compressor.h"
5
+
6
+ #include <brotli/encode.h>
7
+ #include <stdint.h>
8
+ #include <stdlib.h>
9
+
10
+ #include "brs_ext/buffer.h"
11
+ #include "brs_ext/common.h"
12
+ #include "brs_ext/error.h"
13
+ #include "brs_ext/option.h"
14
+ #include "ruby.h"
15
+
16
+ static void free_compressor(brs_ext_compressor_t* compressor_ptr)
17
+ {
18
+ BrotliEncoderState* state_ptr = compressor_ptr->state_ptr;
19
+ if (state_ptr != NULL) {
20
+ BrotliEncoderDestroyInstance(state_ptr);
21
+ }
22
+
23
+ uint8_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 brs_ext_allocate_compressor(VALUE klass)
32
+ {
33
+ brs_ext_compressor_t* compressor_ptr;
34
+
35
+ VALUE self = Data_Make_Struct(klass, brs_ext_compressor_t, NULL, free_compressor, compressor_ptr);
36
+
37
+ compressor_ptr->state_ptr = NULL;
38
+ compressor_ptr->destination_buffer = NULL;
39
+ compressor_ptr->destination_buffer_length = 0;
40
+ compressor_ptr->remaining_destination_buffer = NULL;
41
+ compressor_ptr->remaining_destination_buffer_length = 0;
42
+
43
+ return self;
44
+ }
45
+
46
+ #define GET_COMPRESSOR(self) \
47
+ brs_ext_compressor_t* compressor_ptr; \
48
+ Data_Get_Struct(self, brs_ext_compressor_t, compressor_ptr);
49
+
50
+ VALUE brs_ext_initialize_compressor(VALUE self, VALUE options)
51
+ {
52
+ GET_COMPRESSOR(self);
53
+ Check_Type(options, T_HASH);
54
+ BRS_EXT_GET_COMPRESSOR_OPTIONS(options);
55
+ BRS_EXT_GET_BUFFER_LENGTH_OPTION(options, destination_buffer_length);
56
+
57
+ BrotliEncoderState* state_ptr = BrotliEncoderCreateInstance(NULL, NULL, NULL);
58
+ if (state_ptr == NULL) {
59
+ brs_ext_raise_error(BRS_EXT_ERROR_ALLOCATE_FAILED);
60
+ }
61
+
62
+ brs_ext_result_t ext_result = brs_ext_set_compressor_options(state_ptr, &compressor_options);
63
+ if (ext_result != 0) {
64
+ BrotliEncoderDestroyInstance(state_ptr);
65
+ brs_ext_raise_error(ext_result);
66
+ }
67
+
68
+ if (destination_buffer_length == 0) {
69
+ destination_buffer_length = BRS_DEFAULT_DESTINATION_BUFFER_LENGTH_FOR_COMPRESSOR;
70
+ }
71
+
72
+ uint8_t* destination_buffer = malloc(destination_buffer_length);
73
+ if (destination_buffer == NULL) {
74
+ BrotliEncoderDestroyInstance(state_ptr);
75
+ brs_ext_raise_error(BRS_EXT_ERROR_ALLOCATE_FAILED);
76
+ }
77
+
78
+ compressor_ptr->state_ptr = state_ptr;
79
+ compressor_ptr->destination_buffer = destination_buffer;
80
+ compressor_ptr->destination_buffer_length = destination_buffer_length;
81
+ compressor_ptr->remaining_destination_buffer = destination_buffer;
82
+ compressor_ptr->remaining_destination_buffer_length = destination_buffer_length;
83
+
84
+ return Qnil;
85
+ }
86
+
87
+ #define DO_NOT_USE_AFTER_CLOSE(compressor_ptr) \
88
+ if (compressor_ptr->state_ptr == NULL || compressor_ptr->destination_buffer == NULL) { \
89
+ brs_ext_raise_error(BRS_EXT_ERROR_USED_AFTER_CLOSE); \
90
+ }
91
+
92
+ #define GET_SOURCE_DATA(source_value) \
93
+ Check_Type(source_value, T_STRING); \
94
+ \
95
+ const char* source = RSTRING_PTR(source_value); \
96
+ size_t source_length = RSTRING_LEN(source_value); \
97
+ const uint8_t* remaining_source = (const uint8_t*)source; \
98
+ size_t remaining_source_length = source_length;
99
+
100
+ VALUE brs_ext_compress(VALUE self, VALUE source_value)
101
+ {
102
+ GET_COMPRESSOR(self);
103
+ DO_NOT_USE_AFTER_CLOSE(compressor_ptr);
104
+ GET_SOURCE_DATA(source_value);
105
+
106
+ BrotliEncoderState* state_ptr = compressor_ptr->state_ptr;
107
+
108
+ BROTLI_BOOL result = BrotliEncoderCompressStream(
109
+ state_ptr,
110
+ BROTLI_OPERATION_PROCESS,
111
+ &remaining_source_length, &remaining_source,
112
+ &compressor_ptr->remaining_destination_buffer_length, &compressor_ptr->remaining_destination_buffer,
113
+ NULL);
114
+
115
+ if (!result) {
116
+ brs_ext_raise_error(BRS_EXT_ERROR_UNEXPECTED);
117
+ }
118
+
119
+ VALUE bytes_written = UINT2NUM(source_length - remaining_source_length);
120
+ VALUE needs_more_destination = BrotliEncoderHasMoreOutput(state_ptr) ? Qtrue : Qfalse;
121
+
122
+ return rb_ary_new_from_args(2, bytes_written, needs_more_destination);
123
+ }
124
+
125
+ VALUE brs_ext_flush_compressor(VALUE self)
126
+ {
127
+ GET_COMPRESSOR(self);
128
+ DO_NOT_USE_AFTER_CLOSE(compressor_ptr);
129
+
130
+ BrotliEncoderState* state_ptr = compressor_ptr->state_ptr;
131
+
132
+ const uint8_t* remaining_source = NULL;
133
+ size_t remaining_source_length = 0;
134
+
135
+ BROTLI_BOOL result = BrotliEncoderCompressStream(
136
+ state_ptr,
137
+ BROTLI_OPERATION_FLUSH,
138
+ &remaining_source_length, &remaining_source,
139
+ &compressor_ptr->remaining_destination_buffer_length, &compressor_ptr->remaining_destination_buffer,
140
+ NULL);
141
+
142
+ if (!result) {
143
+ brs_ext_raise_error(BRS_EXT_ERROR_UNEXPECTED);
144
+ }
145
+
146
+ VALUE needs_more_destination = BrotliEncoderHasMoreOutput(state_ptr) ? Qtrue : Qfalse;
147
+
148
+ return needs_more_destination;
149
+ }
150
+
151
+ VALUE brs_ext_finish_compressor(VALUE self)
152
+ {
153
+ GET_COMPRESSOR(self);
154
+ DO_NOT_USE_AFTER_CLOSE(compressor_ptr);
155
+
156
+ BrotliEncoderState* state_ptr = compressor_ptr->state_ptr;
157
+
158
+ const uint8_t* remaining_source = NULL;
159
+ size_t remaining_source_length = 0;
160
+
161
+ BROTLI_BOOL result = BrotliEncoderCompressStream(
162
+ state_ptr,
163
+ BROTLI_OPERATION_FINISH,
164
+ &remaining_source_length, &remaining_source,
165
+ &compressor_ptr->remaining_destination_buffer_length, &compressor_ptr->remaining_destination_buffer,
166
+ NULL);
167
+
168
+ if (!result) {
169
+ brs_ext_raise_error(BRS_EXT_ERROR_UNEXPECTED);
170
+ }
171
+
172
+ VALUE needs_more_destination = (BrotliEncoderHasMoreOutput(state_ptr) || !BrotliEncoderIsFinished(state_ptr)) ? Qtrue : Qfalse;
173
+
174
+ return needs_more_destination;
175
+ }
176
+
177
+ VALUE brs_ext_compressor_read_result(VALUE self)
178
+ {
179
+ GET_COMPRESSOR(self);
180
+ DO_NOT_USE_AFTER_CLOSE(compressor_ptr);
181
+
182
+ uint8_t* destination_buffer = compressor_ptr->destination_buffer;
183
+ size_t destination_buffer_length = compressor_ptr->destination_buffer_length;
184
+ size_t remaining_destination_buffer_length = compressor_ptr->remaining_destination_buffer_length;
185
+
186
+ const char* result = (const char*)destination_buffer;
187
+ size_t result_length = destination_buffer_length - remaining_destination_buffer_length;
188
+
189
+ VALUE result_value = rb_str_new(result, result_length);
190
+
191
+ compressor_ptr->remaining_destination_buffer = destination_buffer;
192
+ compressor_ptr->remaining_destination_buffer_length = destination_buffer_length;
193
+
194
+ return result_value;
195
+ }
196
+
197
+ VALUE brs_ext_compressor_close(VALUE self)
198
+ {
199
+ GET_COMPRESSOR(self);
200
+ DO_NOT_USE_AFTER_CLOSE(compressor_ptr);
201
+
202
+ BrotliEncoderState* state_ptr = compressor_ptr->state_ptr;
203
+ if (state_ptr != NULL) {
204
+ BrotliEncoderDestroyInstance(state_ptr);
205
+
206
+ compressor_ptr->state_ptr = NULL;
207
+ }
208
+
209
+ uint8_t* destination_buffer = compressor_ptr->destination_buffer;
210
+ if (destination_buffer != NULL) {
211
+ free(destination_buffer);
212
+
213
+ compressor_ptr->destination_buffer = NULL;
214
+ }
215
+
216
+ // It is possible to keep "destination_buffer_length", "remaining_destination_buffer"
217
+ // and "remaining_destination_buffer_length" as is.
218
+
219
+ return Qnil;
220
+ }
221
+
222
+ void brs_ext_compressor_exports(VALUE root_module)
223
+ {
224
+ VALUE stream = rb_define_module_under(root_module, "Stream");
225
+
226
+ VALUE compressor = rb_define_class_under(stream, "NativeCompressor", rb_cObject);
227
+ rb_define_alloc_func(compressor, brs_ext_allocate_compressor);
228
+ rb_define_method(compressor, "initialize", brs_ext_initialize_compressor, 1);
229
+ rb_define_method(compressor, "write", brs_ext_compress, 1);
230
+ rb_define_method(compressor, "flush", brs_ext_flush_compressor, 0);
231
+ rb_define_method(compressor, "finish", brs_ext_finish_compressor, 0);
232
+ rb_define_method(compressor, "read_result", brs_ext_compressor_read_result, 0);
233
+ rb_define_method(compressor, "close", brs_ext_compressor_close, 0);
234
+ }
@@ -0,0 +1,31 @@
1
+ // Ruby bindings for brotli library.
2
+ // Copyright (c) 2019 AUTHORS, MIT License.
3
+
4
+ #if !defined(BRS_EXT_STREAM_COMPRESSOR_H)
5
+ #define BRS_EXT_STREAM_COMPRESSOR_H
6
+
7
+ #include <brotli/encode.h>
8
+ #include <stdint.h>
9
+ #include <stdlib.h>
10
+
11
+ #include "ruby.h"
12
+
13
+ typedef struct {
14
+ BrotliEncoderState* state_ptr;
15
+ uint8_t* destination_buffer;
16
+ size_t destination_buffer_length;
17
+ uint8_t* remaining_destination_buffer;
18
+ size_t remaining_destination_buffer_length;
19
+ } brs_ext_compressor_t;
20
+
21
+ VALUE brs_ext_allocate_compressor(VALUE klass);
22
+ VALUE brs_ext_initialize_compressor(VALUE self, VALUE options);
23
+ VALUE brs_ext_compress(VALUE self, VALUE source);
24
+ VALUE brs_ext_flush_compressor(VALUE self);
25
+ VALUE brs_ext_finish_compressor(VALUE self);
26
+ VALUE brs_ext_compressor_read_result(VALUE self);
27
+ VALUE brs_ext_compressor_close(VALUE self);
28
+
29
+ void brs_ext_compressor_exports(VALUE root_module);
30
+
31
+ #endif // BRS_EXT_STREAM_COMPRESSOR_H
@@ -0,0 +1,181 @@
1
+ // Ruby bindings for brotli library.
2
+ // Copyright (c) 2019 AUTHORS, MIT License.
3
+
4
+ #include "brs_ext/stream/decompressor.h"
5
+
6
+ #include <brotli/decode.h>
7
+ #include <stdint.h>
8
+ #include <stdlib.h>
9
+
10
+ #include "brs_ext/buffer.h"
11
+ #include "brs_ext/common.h"
12
+ #include "brs_ext/error.h"
13
+ #include "brs_ext/option.h"
14
+ #include "ruby.h"
15
+
16
+ static void free_decompressor(brs_ext_decompressor_t* decompressor_ptr)
17
+ {
18
+ BrotliDecoderState* state_ptr = decompressor_ptr->state_ptr;
19
+ if (state_ptr != NULL) {
20
+ BrotliDecoderDestroyInstance(state_ptr);
21
+ }
22
+
23
+ uint8_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 brs_ext_allocate_decompressor(VALUE klass)
32
+ {
33
+ brs_ext_decompressor_t* decompressor_ptr;
34
+
35
+ VALUE self = Data_Make_Struct(klass, brs_ext_decompressor_t, NULL, free_decompressor, decompressor_ptr);
36
+
37
+ decompressor_ptr->state_ptr = NULL;
38
+ decompressor_ptr->destination_buffer = NULL;
39
+ decompressor_ptr->destination_buffer_length = 0;
40
+ decompressor_ptr->remaining_destination_buffer = NULL;
41
+ decompressor_ptr->remaining_destination_buffer_length = 0;
42
+
43
+ return self;
44
+ }
45
+
46
+ #define GET_DECOMPRESSOR(self) \
47
+ brs_ext_decompressor_t* decompressor_ptr; \
48
+ Data_Get_Struct(self, brs_ext_decompressor_t, decompressor_ptr);
49
+
50
+ VALUE brs_ext_initialize_decompressor(VALUE self, VALUE options)
51
+ {
52
+ GET_DECOMPRESSOR(self);
53
+ Check_Type(options, T_HASH);
54
+ BRS_EXT_GET_DECOMPRESSOR_OPTIONS(options);
55
+ BRS_EXT_GET_BUFFER_LENGTH_OPTION(options, destination_buffer_length);
56
+
57
+ BrotliDecoderState* state_ptr = BrotliDecoderCreateInstance(NULL, NULL, NULL);
58
+ if (state_ptr == NULL) {
59
+ brs_ext_raise_error(BRS_EXT_ERROR_ALLOCATE_FAILED);
60
+ }
61
+
62
+ brs_ext_result_t ext_result = brs_ext_set_decompressor_options(state_ptr, &decompressor_options);
63
+ if (ext_result != 0) {
64
+ BrotliDecoderDestroyInstance(state_ptr);
65
+ brs_ext_raise_error(ext_result);
66
+ }
67
+
68
+ if (destination_buffer_length == 0) {
69
+ destination_buffer_length = BRS_DEFAULT_DESTINATION_BUFFER_LENGTH_FOR_DECOMPRESSOR;
70
+ }
71
+
72
+ uint8_t* destination_buffer = malloc(destination_buffer_length);
73
+ if (destination_buffer == NULL) {
74
+ BrotliDecoderDestroyInstance(state_ptr);
75
+ brs_ext_raise_error(BRS_EXT_ERROR_ALLOCATE_FAILED);
76
+ }
77
+
78
+ decompressor_ptr->state_ptr = state_ptr;
79
+ decompressor_ptr->destination_buffer = destination_buffer;
80
+ decompressor_ptr->destination_buffer_length = destination_buffer_length;
81
+ decompressor_ptr->remaining_destination_buffer = destination_buffer;
82
+ decompressor_ptr->remaining_destination_buffer_length = destination_buffer_length;
83
+
84
+ return Qnil;
85
+ }
86
+
87
+ #define DO_NOT_USE_AFTER_CLOSE(decompressor_ptr) \
88
+ if (decompressor_ptr->state_ptr == NULL || decompressor_ptr->destination_buffer == NULL) { \
89
+ brs_ext_raise_error(BRS_EXT_ERROR_USED_AFTER_CLOSE); \
90
+ }
91
+
92
+ #define GET_SOURCE_DATA(source_value) \
93
+ Check_Type(source_value, T_STRING); \
94
+ \
95
+ const char* source = RSTRING_PTR(source_value); \
96
+ size_t source_length = RSTRING_LEN(source_value); \
97
+ const uint8_t* remaining_source = (const uint8_t*)source; \
98
+ size_t remaining_source_length = source_length;
99
+
100
+ VALUE brs_ext_decompress(VALUE self, VALUE source_value)
101
+ {
102
+ GET_DECOMPRESSOR(self);
103
+ DO_NOT_USE_AFTER_CLOSE(decompressor_ptr);
104
+ GET_SOURCE_DATA(source_value);
105
+
106
+ BrotliDecoderResult result = BrotliDecoderDecompressStream(
107
+ decompressor_ptr->state_ptr,
108
+ &remaining_source_length, &remaining_source,
109
+ &decompressor_ptr->remaining_destination_buffer_length, &decompressor_ptr->remaining_destination_buffer,
110
+ NULL);
111
+
112
+ if (
113
+ result != BROTLI_DECODER_RESULT_SUCCESS &&
114
+ result != BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT &&
115
+ result != BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
116
+ BrotliDecoderErrorCode error_code = BrotliDecoderGetErrorCode(decompressor_ptr->state_ptr);
117
+ brs_ext_raise_error(brs_ext_get_decompressor_error(error_code));
118
+ }
119
+
120
+ VALUE bytes_written = UINT2NUM(source_length - remaining_source_length);
121
+ VALUE needs_more_destination = result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT ? Qtrue : Qfalse;
122
+
123
+ return rb_ary_new_from_args(2, bytes_written, needs_more_destination);
124
+ }
125
+
126
+ VALUE brs_ext_decompressor_read_result(VALUE self)
127
+ {
128
+ GET_DECOMPRESSOR(self);
129
+ DO_NOT_USE_AFTER_CLOSE(decompressor_ptr);
130
+
131
+ uint8_t* destination_buffer = decompressor_ptr->destination_buffer;
132
+ size_t destination_buffer_length = decompressor_ptr->destination_buffer_length;
133
+ size_t remaining_destination_buffer_length = decompressor_ptr->remaining_destination_buffer_length;
134
+
135
+ const char* result = (const char*)destination_buffer;
136
+ size_t result_length = destination_buffer_length - remaining_destination_buffer_length;
137
+
138
+ VALUE result_value = rb_str_new(result, result_length);
139
+
140
+ decompressor_ptr->remaining_destination_buffer = destination_buffer;
141
+ decompressor_ptr->remaining_destination_buffer_length = destination_buffer_length;
142
+
143
+ return result_value;
144
+ }
145
+
146
+ VALUE brs_ext_decompressor_close(VALUE self)
147
+ {
148
+ GET_DECOMPRESSOR(self);
149
+ DO_NOT_USE_AFTER_CLOSE(decompressor_ptr);
150
+
151
+ BrotliDecoderState* state_ptr = decompressor_ptr->state_ptr;
152
+ if (state_ptr != NULL) {
153
+ BrotliDecoderDestroyInstance(state_ptr);
154
+
155
+ decompressor_ptr->state_ptr = NULL;
156
+ }
157
+
158
+ uint8_t* destination_buffer = decompressor_ptr->destination_buffer;
159
+ if (destination_buffer != NULL) {
160
+ free(destination_buffer);
161
+
162
+ decompressor_ptr->destination_buffer = NULL;
163
+ }
164
+
165
+ // It is possible to keep "destination_buffer_length", "remaining_destination_buffer"
166
+ // and "remaining_destination_buffer_length" as is.
167
+
168
+ return Qnil;
169
+ }
170
+
171
+ void brs_ext_decompressor_exports(VALUE root_module)
172
+ {
173
+ VALUE stream = rb_define_module_under(root_module, "Stream");
174
+
175
+ VALUE decompressor = rb_define_class_under(stream, "NativeDecompressor", rb_cObject);
176
+ rb_define_alloc_func(decompressor, brs_ext_allocate_decompressor);
177
+ rb_define_method(decompressor, "initialize", brs_ext_initialize_decompressor, 1);
178
+ rb_define_method(decompressor, "read", brs_ext_decompress, 1);
179
+ rb_define_method(decompressor, "read_result", brs_ext_decompressor_read_result, 0);
180
+ rb_define_method(decompressor, "close", brs_ext_decompressor_close, 0);
181
+ }
@@ -0,0 +1,29 @@
1
+ // Ruby bindings for brotli library.
2
+ // Copyright (c) 2019 AUTHORS, MIT License.
3
+
4
+ #if !defined(BRS_EXT_STREAM_DECOMPRESSOR_H)
5
+ #define BRS_EXT_STREAM_DECOMPRESSOR_H
6
+
7
+ #include <brotli/decode.h>
8
+ #include <stdint.h>
9
+ #include <stdlib.h>
10
+
11
+ #include "ruby.h"
12
+
13
+ typedef struct {
14
+ BrotliDecoderState* state_ptr;
15
+ uint8_t* destination_buffer;
16
+ size_t destination_buffer_length;
17
+ uint8_t* remaining_destination_buffer;
18
+ size_t remaining_destination_buffer_length;
19
+ } brs_ext_decompressor_t;
20
+
21
+ VALUE brs_ext_allocate_decompressor(VALUE klass);
22
+ VALUE brs_ext_initialize_decompressor(VALUE self, VALUE options);
23
+ VALUE brs_ext_decompress(VALUE self, VALUE source);
24
+ VALUE brs_ext_decompressor_read_result(VALUE self);
25
+ VALUE brs_ext_decompressor_close(VALUE self);
26
+
27
+ void brs_ext_decompressor_exports(VALUE root_module);
28
+
29
+ #endif // BRS_EXT_STREAM_DECOMPRESSOR_H