ruby-lzws 1.0.0 → 1.1.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 +4 -4
- data/README.md +176 -27
- data/ext/extconf.rb +11 -17
- data/ext/lzws_ext/buffer.c +18 -0
- data/ext/lzws_ext/buffer.h +11 -0
- data/ext/lzws_ext/common.h +4 -0
- data/ext/lzws_ext/error.c +33 -3
- data/ext/lzws_ext/error.h +20 -1
- data/ext/lzws_ext/io.c +60 -47
- data/ext/lzws_ext/io.h +2 -0
- data/ext/lzws_ext/macro.h +0 -2
- data/ext/lzws_ext/main.c +8 -29
- data/ext/lzws_ext/option.c +23 -10
- data/ext/lzws_ext/option.h +27 -50
- data/ext/lzws_ext/stream/compressor.c +71 -84
- data/ext/lzws_ext/stream/compressor.h +5 -2
- data/ext/lzws_ext/stream/decompressor.c +67 -84
- data/ext/lzws_ext/stream/decompressor.h +4 -1
- data/ext/lzws_ext/string.c +261 -44
- data/ext/lzws_ext/string.h +2 -0
- data/lib/lzws/error.rb +8 -7
- data/lib/lzws/file.rb +6 -18
- data/lib/lzws/option.rb +10 -7
- data/lib/lzws/stream/abstract.rb +2 -2
- data/lib/lzws/stream/raw/abstract.rb +24 -9
- data/lib/lzws/stream/raw/compressor.rb +7 -31
- data/lib/lzws/stream/raw/decompressor.rb +6 -24
- data/lib/lzws/stream/reader.rb +14 -11
- data/lib/lzws/stream/reader_helpers.rb +3 -3
- data/lib/lzws/stream/writer_helpers.rb +2 -2
- data/lib/lzws/string.rb +4 -2
- data/lib/lzws/version.rb +1 -1
- data/lib/lzws.rb +4 -1
- metadata +5 -3
@@ -5,6 +5,8 @@
|
|
5
5
|
#define LZWS_EXT_STREAM_DECOMPRESSOR_H
|
6
6
|
|
7
7
|
#include <lzws/decompressor/state.h>
|
8
|
+
#include <stdint.h>
|
9
|
+
#include <stdlib.h>
|
8
10
|
|
9
11
|
#include "ruby.h"
|
10
12
|
|
@@ -18,9 +20,10 @@ typedef struct {
|
|
18
20
|
|
19
21
|
VALUE lzws_ext_allocate_decompressor(VALUE klass);
|
20
22
|
VALUE lzws_ext_initialize_decompressor(VALUE self, VALUE options);
|
21
|
-
VALUE lzws_ext_decompressor_read_magic_header(VALUE self, VALUE source);
|
22
23
|
VALUE lzws_ext_decompress(VALUE self, VALUE source);
|
23
24
|
VALUE lzws_ext_decompressor_read_result(VALUE self);
|
24
25
|
VALUE lzws_ext_decompressor_close(VALUE self);
|
25
26
|
|
27
|
+
void lzws_ext_decompressor_exports(VALUE root_module);
|
28
|
+
|
26
29
|
#endif // LZWS_EXT_STREAM_DECOMPRESSOR_H
|
data/ext/lzws_ext/string.c
CHANGED
@@ -1,78 +1,295 @@
|
|
1
1
|
// Ruby bindings for lzws library.
|
2
2
|
// Copyright (c) 2019 AUTHORS, MIT License.
|
3
3
|
|
4
|
-
#include
|
4
|
+
#include "lzws_ext/string.h"
|
5
5
|
|
6
|
-
#include
|
6
|
+
#include <lzws/buffer.h>
|
7
|
+
#include <lzws/compressor/common.h>
|
8
|
+
#include <lzws/compressor/main.h>
|
9
|
+
#include <lzws/compressor/state.h>
|
10
|
+
#include <lzws/decompressor/common.h>
|
11
|
+
#include <lzws/decompressor/main.h>
|
12
|
+
#include <lzws/decompressor/state.h>
|
13
|
+
#include <stdint.h>
|
14
|
+
#include <stdlib.h>
|
7
15
|
|
8
16
|
#include "lzws_ext/error.h"
|
9
17
|
#include "lzws_ext/macro.h"
|
10
18
|
#include "lzws_ext/option.h"
|
11
|
-
#include "
|
19
|
+
#include "ruby.h"
|
20
|
+
|
21
|
+
// -- buffer --
|
22
|
+
|
23
|
+
static inline VALUE create_buffer(VALUE length)
|
24
|
+
{
|
25
|
+
return rb_str_new(NULL, NUM2UINT(length));
|
26
|
+
}
|
27
|
+
|
28
|
+
#define CREATE_BUFFER(buffer, length, exception) \
|
29
|
+
VALUE buffer = rb_protect(create_buffer, UINT2NUM(length), &exception);
|
12
30
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
31
|
+
static inline VALUE resize_buffer(VALUE args)
|
32
|
+
{
|
33
|
+
VALUE buffer = rb_ary_entry(args, 0);
|
34
|
+
VALUE length = rb_ary_entry(args, 1);
|
35
|
+
return rb_str_resize(buffer, NUM2UINT(length));
|
36
|
+
}
|
37
|
+
|
38
|
+
#define RESIZE_BUFFER(buffer, length, exception) \
|
39
|
+
VALUE resize_buffer_args = rb_ary_new_from_args(2, buffer, UINT2NUM(length)); \
|
40
|
+
buffer = rb_protect(resize_buffer, resize_buffer_args, &exception); \
|
41
|
+
RB_GC_GUARD(resize_buffer_args);
|
42
|
+
|
43
|
+
static inline lzws_ext_result_t increase_destination_buffer(
|
44
|
+
VALUE destination_value, size_t destination_length,
|
45
|
+
size_t* remaining_destination_buffer_length_ptr, size_t destination_buffer_length)
|
46
|
+
{
|
47
|
+
if (*remaining_destination_buffer_length_ptr == destination_buffer_length) {
|
48
|
+
// We want to write more data at once, than buffer has.
|
49
|
+
return LZWS_EXT_ERROR_NOT_ENOUGH_DESTINATION_BUFFER;
|
50
|
+
}
|
51
|
+
|
52
|
+
int exception;
|
53
|
+
|
54
|
+
RESIZE_BUFFER(destination_value, destination_length + destination_buffer_length, exception);
|
55
|
+
if (exception != 0) {
|
56
|
+
return LZWS_EXT_ERROR_ALLOCATE_FAILED;
|
57
|
+
}
|
58
|
+
|
59
|
+
*remaining_destination_buffer_length_ptr = destination_buffer_length;
|
60
|
+
|
61
|
+
return 0;
|
62
|
+
}
|
63
|
+
|
64
|
+
// -- utils --
|
65
|
+
|
66
|
+
#define GET_SOURCE_DATA(source_value) \
|
67
|
+
Check_Type(source_value, T_STRING); \
|
68
|
+
\
|
69
|
+
const char* source = RSTRING_PTR(source_value); \
|
70
|
+
size_t source_length = RSTRING_LEN(source_value); \
|
71
|
+
uint8_t* remaining_source = (uint8_t*)source; \
|
72
|
+
size_t remaining_source_length = source_length;
|
73
|
+
|
74
|
+
// -- compress --
|
75
|
+
|
76
|
+
#define BUFFERED_COMPRESS(function, ...) \
|
77
|
+
while (true) { \
|
78
|
+
uint8_t* remaining_destination_buffer = (uint8_t*)RSTRING_PTR(destination_value) + destination_length; \
|
79
|
+
size_t prev_remaining_destination_buffer_length = remaining_destination_buffer_length; \
|
80
|
+
\
|
81
|
+
result = function(__VA_ARGS__, &remaining_destination_buffer, &remaining_destination_buffer_length); \
|
82
|
+
\
|
83
|
+
if ( \
|
84
|
+
result != 0 && \
|
85
|
+
result != LZWS_COMPRESSOR_NEEDS_MORE_DESTINATION) { \
|
86
|
+
return LZWS_EXT_ERROR_UNEXPECTED; \
|
87
|
+
} \
|
88
|
+
\
|
89
|
+
destination_length += prev_remaining_destination_buffer_length - remaining_destination_buffer_length; \
|
90
|
+
\
|
91
|
+
if (result == LZWS_COMPRESSOR_NEEDS_MORE_DESTINATION) { \
|
92
|
+
ext_result = increase_destination_buffer( \
|
93
|
+
destination_value, destination_length, \
|
94
|
+
&remaining_destination_buffer_length, destination_buffer_length); \
|
95
|
+
\
|
96
|
+
if (ext_result != 0) { \
|
97
|
+
return ext_result; \
|
98
|
+
} \
|
99
|
+
\
|
100
|
+
continue; \
|
101
|
+
} \
|
102
|
+
\
|
103
|
+
break; \
|
104
|
+
}
|
105
|
+
|
106
|
+
static inline lzws_ext_result_t compress(
|
107
|
+
lzws_compressor_state_t* state_ptr,
|
108
|
+
uint8_t* remaining_source, size_t remaining_source_length,
|
109
|
+
VALUE destination_value, size_t destination_buffer_length)
|
110
|
+
{
|
111
|
+
lzws_result_t result;
|
112
|
+
lzws_ext_result_t ext_result;
|
113
|
+
|
114
|
+
size_t destination_length = 0;
|
115
|
+
size_t remaining_destination_buffer_length = destination_buffer_length;
|
116
|
+
|
117
|
+
BUFFERED_COMPRESS(lzws_compress, state_ptr, &remaining_source, &remaining_source_length);
|
118
|
+
BUFFERED_COMPRESS(lzws_compressor_finish, state_ptr);
|
119
|
+
|
120
|
+
int exception;
|
121
|
+
|
122
|
+
RESIZE_BUFFER(destination_value, destination_length, exception);
|
123
|
+
if (exception != 0) {
|
124
|
+
return LZWS_EXT_ERROR_ALLOCATE_FAILED;
|
125
|
+
}
|
126
|
+
|
127
|
+
return 0;
|
128
|
+
}
|
18
129
|
|
19
|
-
VALUE lzws_ext_compress_string(VALUE LZWS_EXT_UNUSED(self), VALUE
|
130
|
+
VALUE lzws_ext_compress_string(VALUE LZWS_EXT_UNUSED(self), VALUE source_value, VALUE options)
|
20
131
|
{
|
21
|
-
|
132
|
+
GET_SOURCE_DATA(source_value);
|
133
|
+
Check_Type(options, T_HASH);
|
22
134
|
LZWS_EXT_GET_COMPRESSOR_OPTIONS(options);
|
135
|
+
LZWS_EXT_GET_BUFFER_LENGTH_OPTION(options, destination_buffer_length);
|
23
136
|
|
24
|
-
|
25
|
-
size_t destination_length;
|
137
|
+
lzws_compressor_state_t* state_ptr;
|
26
138
|
|
27
|
-
lzws_result_t result =
|
28
|
-
|
29
|
-
(uint8_t**)&destination, &destination_length, buffer_length,
|
139
|
+
lzws_result_t result = lzws_compressor_get_initial_state(
|
140
|
+
&state_ptr,
|
30
141
|
without_magic_header, max_code_bit_length, block_mode, msb, unaligned_bit_groups, quiet);
|
31
142
|
|
32
|
-
if (result
|
33
|
-
|
143
|
+
if (result != 0) {
|
144
|
+
switch (result) {
|
145
|
+
case LZWS_COMPRESSOR_ALLOCATE_FAILED:
|
146
|
+
lzws_ext_raise_error(LZWS_EXT_ERROR_ALLOCATE_FAILED);
|
147
|
+
case LZWS_COMPRESSOR_INVALID_MAX_CODE_BIT_LENGTH:
|
148
|
+
lzws_ext_raise_error(LZWS_EXT_ERROR_VALIDATE_FAILED);
|
149
|
+
default:
|
150
|
+
lzws_ext_raise_error(LZWS_EXT_ERROR_UNEXPECTED);
|
151
|
+
}
|
34
152
|
}
|
35
|
-
|
36
|
-
|
153
|
+
|
154
|
+
if (destination_buffer_length == 0) {
|
155
|
+
destination_buffer_length = LZWS_DEFAULT_DESTINATION_BUFFER_LENGTH_FOR_COMPRESSOR;
|
156
|
+
}
|
157
|
+
|
158
|
+
int exception;
|
159
|
+
|
160
|
+
CREATE_BUFFER(destination_value, destination_buffer_length, exception);
|
161
|
+
if (exception != 0) {
|
162
|
+
lzws_compressor_free_state(state_ptr);
|
163
|
+
lzws_ext_raise_error(LZWS_EXT_ERROR_ALLOCATE_FAILED);
|
164
|
+
}
|
165
|
+
|
166
|
+
lzws_ext_result_t ext_result = compress(
|
167
|
+
state_ptr,
|
168
|
+
remaining_source, remaining_source_length,
|
169
|
+
destination_value, destination_buffer_length);
|
170
|
+
|
171
|
+
lzws_compressor_free_state(state_ptr);
|
172
|
+
|
173
|
+
if (ext_result != 0) {
|
174
|
+
lzws_ext_raise_error(ext_result);
|
175
|
+
}
|
176
|
+
|
177
|
+
return destination_value;
|
178
|
+
}
|
179
|
+
|
180
|
+
// -- decompress --
|
181
|
+
|
182
|
+
static inline lzws_ext_result_t decompress(
|
183
|
+
lzws_decompressor_state_t* state_ptr,
|
184
|
+
uint8_t* remaining_source, size_t remaining_source_length,
|
185
|
+
VALUE destination_value, size_t destination_buffer_length)
|
186
|
+
{
|
187
|
+
lzws_result_t result;
|
188
|
+
lzws_ext_result_t ext_result;
|
189
|
+
|
190
|
+
size_t destination_length = 0;
|
191
|
+
size_t remaining_destination_buffer_length = destination_buffer_length;
|
192
|
+
|
193
|
+
while (true) {
|
194
|
+
uint8_t* remaining_destination_buffer = (uint8_t*)RSTRING_PTR(destination_value) + destination_length;
|
195
|
+
size_t prev_remaining_destination_buffer_length = remaining_destination_buffer_length;
|
196
|
+
|
197
|
+
result = lzws_decompress(
|
198
|
+
state_ptr,
|
199
|
+
&remaining_source, &remaining_source_length,
|
200
|
+
&remaining_destination_buffer, &remaining_destination_buffer_length);
|
201
|
+
|
202
|
+
if (
|
203
|
+
result != 0 &&
|
204
|
+
result != LZWS_DECOMPRESSOR_NEEDS_MORE_DESTINATION) {
|
205
|
+
switch (result) {
|
206
|
+
case LZWS_DECOMPRESSOR_INVALID_MAGIC_HEADER:
|
207
|
+
case LZWS_DECOMPRESSOR_INVALID_MAX_CODE_BIT_LENGTH:
|
208
|
+
return LZWS_EXT_ERROR_VALIDATE_FAILED;
|
209
|
+
case LZWS_DECOMPRESSOR_CORRUPTED_SOURCE:
|
210
|
+
return LZWS_EXT_ERROR_DECOMPRESSOR_CORRUPTED_SOURCE;
|
211
|
+
default:
|
212
|
+
return LZWS_EXT_ERROR_UNEXPECTED;
|
213
|
+
}
|
214
|
+
}
|
215
|
+
|
216
|
+
destination_length += prev_remaining_destination_buffer_length - remaining_destination_buffer_length;
|
217
|
+
|
218
|
+
if (result == LZWS_DECOMPRESSOR_NEEDS_MORE_DESTINATION) {
|
219
|
+
ext_result = increase_destination_buffer(
|
220
|
+
destination_value, destination_length,
|
221
|
+
&remaining_destination_buffer_length, destination_buffer_length);
|
222
|
+
|
223
|
+
if (ext_result != 0) {
|
224
|
+
return ext_result;
|
225
|
+
}
|
226
|
+
|
227
|
+
continue;
|
228
|
+
}
|
229
|
+
|
230
|
+
break;
|
37
231
|
}
|
38
|
-
|
39
|
-
|
232
|
+
|
233
|
+
int exception;
|
234
|
+
|
235
|
+
RESIZE_BUFFER(destination_value, destination_length, exception);
|
236
|
+
if (exception != 0) {
|
237
|
+
return LZWS_EXT_ERROR_ALLOCATE_FAILED;
|
40
238
|
}
|
41
239
|
|
42
|
-
|
43
|
-
VALUE result_string = rb_str_new(destination, destination_length);
|
44
|
-
free(destination);
|
45
|
-
return result_string;
|
240
|
+
return 0;
|
46
241
|
}
|
47
242
|
|
48
|
-
VALUE lzws_ext_decompress_string(VALUE LZWS_EXT_UNUSED(self), VALUE
|
243
|
+
VALUE lzws_ext_decompress_string(VALUE LZWS_EXT_UNUSED(self), VALUE source_value, VALUE options)
|
49
244
|
{
|
50
|
-
|
245
|
+
GET_SOURCE_DATA(source_value);
|
246
|
+
Check_Type(options, T_HASH);
|
51
247
|
LZWS_EXT_GET_DECOMPRESSOR_OPTIONS(options);
|
248
|
+
LZWS_EXT_GET_BUFFER_LENGTH_OPTION(options, destination_buffer_length);
|
52
249
|
|
53
|
-
|
54
|
-
size_t destination_length;
|
250
|
+
lzws_decompressor_state_t* state_ptr;
|
55
251
|
|
56
|
-
lzws_result_t result =
|
57
|
-
|
58
|
-
(uint8_t**)&destination, &destination_length, buffer_length,
|
252
|
+
lzws_result_t result = lzws_decompressor_get_initial_state(
|
253
|
+
&state_ptr,
|
59
254
|
without_magic_header, msb, unaligned_bit_groups, quiet);
|
60
255
|
|
61
|
-
if (result
|
62
|
-
|
256
|
+
if (result != 0) {
|
257
|
+
switch (result) {
|
258
|
+
case LZWS_DECOMPRESSOR_ALLOCATE_FAILED:
|
259
|
+
lzws_ext_raise_error(LZWS_EXT_ERROR_ALLOCATE_FAILED);
|
260
|
+
default:
|
261
|
+
lzws_ext_raise_error(LZWS_EXT_ERROR_UNEXPECTED);
|
262
|
+
}
|
63
263
|
}
|
64
|
-
|
65
|
-
|
264
|
+
|
265
|
+
if (destination_buffer_length == 0) {
|
266
|
+
destination_buffer_length = LZWS_DEFAULT_DESTINATION_BUFFER_LENGTH_FOR_DECOMPRESSOR;
|
66
267
|
}
|
67
|
-
|
68
|
-
|
268
|
+
|
269
|
+
int exception;
|
270
|
+
|
271
|
+
CREATE_BUFFER(destination_value, destination_buffer_length, exception);
|
272
|
+
if (exception != 0) {
|
273
|
+
lzws_decompressor_free_state(state_ptr);
|
274
|
+
lzws_ext_raise_error(LZWS_EXT_ERROR_ALLOCATE_FAILED);
|
69
275
|
}
|
70
|
-
|
71
|
-
|
276
|
+
|
277
|
+
lzws_ext_result_t ext_result = decompress(
|
278
|
+
state_ptr,
|
279
|
+
remaining_source, remaining_source_length,
|
280
|
+
destination_value, destination_buffer_length);
|
281
|
+
|
282
|
+
lzws_decompressor_free_state(state_ptr);
|
283
|
+
|
284
|
+
if (ext_result != 0) {
|
285
|
+
lzws_ext_raise_error(ext_result);
|
72
286
|
}
|
73
287
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
288
|
+
return destination_value;
|
289
|
+
}
|
290
|
+
|
291
|
+
void lzws_ext_string_exports(VALUE root_module)
|
292
|
+
{
|
293
|
+
rb_define_module_function(root_module, "_native_compress_string", RUBY_METHOD_FUNC(lzws_ext_compress_string), 2);
|
294
|
+
rb_define_module_function(root_module, "_native_decompress_string", RUBY_METHOD_FUNC(lzws_ext_decompress_string), 2);
|
78
295
|
}
|
data/ext/lzws_ext/string.h
CHANGED
data/lib/lzws/error.rb
CHANGED
@@ -4,17 +4,18 @@
|
|
4
4
|
module LZWS
|
5
5
|
class BaseError < ::StandardError; end
|
6
6
|
|
7
|
-
class
|
8
|
-
class
|
9
|
-
class UnexpectedError < BaseError; end
|
10
|
-
|
11
|
-
class NotEnoughDestinationError < BaseError; end
|
12
|
-
class UsedAfterCloseError < BaseError; end
|
7
|
+
class AllocateError < BaseError; end
|
8
|
+
class ValidateError < BaseError; end
|
13
9
|
|
10
|
+
class UsedAfterCloseError < BaseError; end
|
11
|
+
class NotEnoughSourceBufferError < BaseError; end
|
12
|
+
class NotEnoughDestinationBufferError < BaseError; end
|
13
|
+
class NotEnoughDestinationError < BaseError; end
|
14
14
|
class DecompressorCorruptedSourceError < BaseError; end
|
15
15
|
|
16
|
-
class OpenFileError < BaseError; end
|
17
16
|
class AccessIOError < BaseError; end
|
18
17
|
class ReadIOError < BaseError; end
|
19
18
|
class WriteIOError < BaseError; end
|
19
|
+
|
20
|
+
class UnexpectedError < BaseError; end
|
20
21
|
end
|
data/lib/lzws/file.rb
CHANGED
@@ -9,11 +9,13 @@ require_relative "validation"
|
|
9
9
|
|
10
10
|
module LZWS
|
11
11
|
module File
|
12
|
+
BUFFER_LENGTH_NAMES = %i[source_buffer_length destination_buffer_length].freeze
|
13
|
+
|
12
14
|
def self.compress(source, destination, options = {})
|
13
15
|
Validation.validate_string source
|
14
16
|
Validation.validate_string destination
|
15
17
|
|
16
|
-
options = Option.get_compressor_options options
|
18
|
+
options = Option.get_compressor_options options, BUFFER_LENGTH_NAMES
|
17
19
|
|
18
20
|
open_files(source, destination) do |source_io, destination_io|
|
19
21
|
LZWS._native_compress_io source_io, destination_io, options
|
@@ -24,7 +26,7 @@ module LZWS
|
|
24
26
|
Validation.validate_string source
|
25
27
|
Validation.validate_string destination
|
26
28
|
|
27
|
-
options = Option.get_decompressor_options options
|
29
|
+
options = Option.get_decompressor_options options, BUFFER_LENGTH_NAMES
|
28
30
|
|
29
31
|
open_files(source, destination) do |source_io, destination_io|
|
30
32
|
LZWS._native_decompress_io source_io, destination_io, options
|
@@ -32,25 +34,11 @@ module LZWS
|
|
32
34
|
end
|
33
35
|
|
34
36
|
private_class_method def self.open_files(source, destination, &_block)
|
35
|
-
|
36
|
-
|
37
|
+
::File.open source, "rb" do |source_io|
|
38
|
+
::File.open destination, "wb" do |destination_io|
|
37
39
|
yield source_io, destination_io
|
38
40
|
end
|
39
41
|
end
|
40
42
|
end
|
41
|
-
|
42
|
-
private_class_method def self.open_file(path, mode, &_block)
|
43
|
-
begin
|
44
|
-
io = ::File.open path, mode
|
45
|
-
rescue ::StandardError
|
46
|
-
raise OpenFileError, "open file failed"
|
47
|
-
end
|
48
|
-
|
49
|
-
begin
|
50
|
-
yield io
|
51
|
-
ensure
|
52
|
-
io.close
|
53
|
-
end
|
54
|
-
end
|
55
43
|
end
|
56
44
|
end
|
data/lib/lzws/option.rb
CHANGED
@@ -8,11 +8,11 @@ module LZWS
|
|
8
8
|
module Option
|
9
9
|
# Default options will be compatible with UNIX compress.
|
10
10
|
|
11
|
+
DEFAULT_BUFFER_LENGTH = 0
|
11
12
|
LOWEST_MAX_CODE_BIT_LENGTH = 9
|
12
13
|
BIGGEST_MAX_CODE_BIT_LENGTH = 16
|
13
14
|
|
14
15
|
DECOMPRESSOR_DEFAULTS = {
|
15
|
-
:buffer_length => 0,
|
16
16
|
:without_magic_header => false,
|
17
17
|
:msb => false,
|
18
18
|
:unaligned_bit_groups => false,
|
@@ -26,12 +26,13 @@ module LZWS
|
|
26
26
|
)
|
27
27
|
.freeze
|
28
28
|
|
29
|
-
def self.get_compressor_options(options)
|
29
|
+
def self.get_compressor_options(options, buffer_length_names)
|
30
30
|
Validation.validate_hash options
|
31
31
|
|
32
|
-
|
32
|
+
buffer_length_defaults = buffer_length_names.each_with_object({}) { |name, defaults| defaults[name] = DEFAULT_BUFFER_LENGTH }
|
33
|
+
options = COMPRESSOR_DEFAULTS.merge(buffer_length_defaults).merge options
|
33
34
|
|
34
|
-
Validation.validate_not_negative_integer options[
|
35
|
+
buffer_length_names.each { |name| Validation.validate_not_negative_integer options[name] }
|
35
36
|
|
36
37
|
max_code_bit_length = options[:max_code_bit_length]
|
37
38
|
Validation.validate_positive_integer max_code_bit_length
|
@@ -49,12 +50,14 @@ module LZWS
|
|
49
50
|
options
|
50
51
|
end
|
51
52
|
|
52
|
-
def self.get_decompressor_options(options)
|
53
|
+
def self.get_decompressor_options(options, buffer_length_names)
|
53
54
|
Validation.validate_hash options
|
54
55
|
|
55
|
-
|
56
|
+
buffer_length_defaults = buffer_length_names.each_with_object({}) { |name, defaults| defaults[name] = DEFAULT_BUFFER_LENGTH }
|
57
|
+
options = DECOMPRESSOR_DEFAULTS.merge(buffer_length_defaults).merge options
|
58
|
+
|
59
|
+
buffer_length_names.each { |name| Validation.validate_not_negative_integer options[name] }
|
56
60
|
|
57
|
-
Validation.validate_not_negative_integer options[:buffer_length]
|
58
61
|
Validation.validate_bool options[:without_magic_header]
|
59
62
|
Validation.validate_bool options[:msb]
|
60
63
|
Validation.validate_bool options[:unaligned_bit_groups]
|
data/lib/lzws/stream/abstract.rb
CHANGED
@@ -9,7 +9,7 @@ require_relative "../validation"
|
|
9
9
|
module LZWS
|
10
10
|
module Stream
|
11
11
|
class Abstract
|
12
|
-
#
|
12
|
+
# Native stream is not seekable by design.
|
13
13
|
# Related methods like "seek" and "pos=" can't be implemented.
|
14
14
|
|
15
15
|
# It is not possible to maintain correspondance between bytes consumed from source and bytes written to destination by design.
|
@@ -21,6 +21,7 @@ module LZWS
|
|
21
21
|
attr_reader :stat
|
22
22
|
attr_reader :external_encoding
|
23
23
|
attr_reader :internal_encoding
|
24
|
+
attr_reader :transcode_options
|
24
25
|
attr_reader :pos
|
25
26
|
alias tell pos
|
26
27
|
|
@@ -115,7 +116,6 @@ module LZWS
|
|
115
116
|
|
116
117
|
protected def target_encoding
|
117
118
|
return @internal_encoding unless @internal_encoding.nil?
|
118
|
-
|
119
119
|
return @external_encoding unless @external_encoding.nil?
|
120
120
|
|
121
121
|
::Encoding::BINARY
|
@@ -4,6 +4,7 @@
|
|
4
4
|
require "lzws_ext"
|
5
5
|
|
6
6
|
require_relative "../../error"
|
7
|
+
require_relative "../../validation"
|
7
8
|
|
8
9
|
module LZWS
|
9
10
|
module Stream
|
@@ -14,19 +15,16 @@ module LZWS
|
|
14
15
|
@is_closed = false
|
15
16
|
end
|
16
17
|
|
17
|
-
|
18
|
-
return nil if @is_closed
|
18
|
+
# -- write --
|
19
19
|
|
20
|
-
|
20
|
+
def flush(&writer)
|
21
|
+
do_not_use_after_close
|
21
22
|
|
22
|
-
|
23
|
-
@is_closed = true
|
23
|
+
Validation.validate_proc writer
|
24
24
|
|
25
|
-
|
26
|
-
end
|
25
|
+
write_result(&writer)
|
27
26
|
|
28
|
-
|
29
|
-
@is_closed
|
27
|
+
nil
|
30
28
|
end
|
31
29
|
|
32
30
|
protected def flush_destination_buffer(&writer)
|
@@ -40,6 +38,23 @@ module LZWS
|
|
40
38
|
|
41
39
|
result.bytesize
|
42
40
|
end
|
41
|
+
|
42
|
+
# -- close --
|
43
|
+
|
44
|
+
protected def do_not_use_after_close
|
45
|
+
raise UsedAfterCloseError, "used after close" if closed?
|
46
|
+
end
|
47
|
+
|
48
|
+
def close(&writer)
|
49
|
+
write_result(&writer)
|
50
|
+
|
51
|
+
@native_stream.close
|
52
|
+
@is_closed = true
|
53
|
+
end
|
54
|
+
|
55
|
+
def closed?
|
56
|
+
@is_closed
|
57
|
+
end
|
43
58
|
end
|
44
59
|
end
|
45
60
|
end
|
@@ -12,13 +12,13 @@ module LZWS
|
|
12
12
|
module Stream
|
13
13
|
module Raw
|
14
14
|
class Compressor < Abstract
|
15
|
+
BUFFER_LENGTH_NAMES = %i[destination_buffer_length].freeze
|
16
|
+
|
15
17
|
def initialize(options = {})
|
16
|
-
options = Option.get_compressor_options options
|
18
|
+
options = Option.get_compressor_options options, BUFFER_LENGTH_NAMES
|
17
19
|
native_stream = NativeCompressor.new options
|
18
20
|
|
19
21
|
super native_stream
|
20
|
-
|
21
|
-
@need_to_write_magic_header = !options[:without_magic_header]
|
22
22
|
end
|
23
23
|
|
24
24
|
def write(source, &writer)
|
@@ -27,11 +27,6 @@ module LZWS
|
|
27
27
|
Validation.validate_string source
|
28
28
|
Validation.validate_proc writer
|
29
29
|
|
30
|
-
if @need_to_write_magic_header
|
31
|
-
write_magic_header(&writer)
|
32
|
-
@need_to_write_magic_header = false
|
33
|
-
end
|
34
|
-
|
35
30
|
total_bytes_written = 0
|
36
31
|
|
37
32
|
loop do
|
@@ -55,28 +50,13 @@ module LZWS
|
|
55
50
|
total_bytes_written
|
56
51
|
end
|
57
52
|
|
58
|
-
|
59
|
-
|
60
|
-
need_more_destination = @native_stream.write_magic_header
|
61
|
-
|
62
|
-
if need_more_destination
|
63
|
-
flush_destination_buffer(&writer)
|
64
|
-
next
|
65
|
-
end
|
66
|
-
|
67
|
-
break
|
68
|
-
end
|
69
|
-
|
70
|
-
nil
|
71
|
-
end
|
72
|
-
|
73
|
-
def flush(&writer)
|
74
|
-
do_not_use_after_close
|
53
|
+
def close(&writer)
|
54
|
+
return nil if closed?
|
75
55
|
|
76
56
|
Validation.validate_proc writer
|
77
57
|
|
78
58
|
loop do
|
79
|
-
need_more_destination = @native_stream.
|
59
|
+
need_more_destination = @native_stream.finish
|
80
60
|
|
81
61
|
if need_more_destination
|
82
62
|
flush_destination_buffer(&writer)
|
@@ -86,14 +66,10 @@ module LZWS
|
|
86
66
|
break
|
87
67
|
end
|
88
68
|
|
89
|
-
|
69
|
+
super
|
90
70
|
|
91
71
|
nil
|
92
72
|
end
|
93
|
-
|
94
|
-
protected def do_not_use_after_close
|
95
|
-
raise UsedAfterCloseError, "compressor used after close" if @is_closed
|
96
|
-
end
|
97
73
|
end
|
98
74
|
end
|
99
75
|
end
|