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.
@@ -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
@@ -1,78 +1,295 @@
1
1
  // Ruby bindings for lzws library.
2
2
  // Copyright (c) 2019 AUTHORS, MIT License.
3
3
 
4
- #include <lzws/string.h>
4
+ #include "lzws_ext/string.h"
5
5
 
6
- #include "ruby.h"
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 "lzws_ext/string.h"
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
- #define GET_STRING(source) \
14
- Check_Type(source, T_STRING); \
15
- \
16
- const char* source_data = RSTRING_PTR(source); \
17
- size_t source_length = RSTRING_LEN(source);
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 source, VALUE options)
130
+ VALUE lzws_ext_compress_string(VALUE LZWS_EXT_UNUSED(self), VALUE source_value, VALUE options)
20
131
  {
21
- GET_STRING(source);
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
- char* destination;
25
- size_t destination_length;
137
+ lzws_compressor_state_t* state_ptr;
26
138
 
27
- lzws_result_t result = lzws_compress_string(
28
- (uint8_t*)source_data, source_length,
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 == LZWS_STRING_ALLOCATE_FAILED) {
33
- lzws_ext_raise_error("AllocateError", "allocate error");
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
- else if (result == LZWS_STRING_VALIDATE_FAILED) {
36
- lzws_ext_raise_error("ValidateError", "validate error");
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
- else if (result != 0) {
39
- lzws_ext_raise_error("UnexpectedError", "unexpected error");
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
- // Ruby copies string on initialization.
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 source, VALUE options)
243
+ VALUE lzws_ext_decompress_string(VALUE LZWS_EXT_UNUSED(self), VALUE source_value, VALUE options)
49
244
  {
50
- GET_STRING(source);
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
- char* destination;
54
- size_t destination_length;
250
+ lzws_decompressor_state_t* state_ptr;
55
251
 
56
- lzws_result_t result = lzws_decompress_string(
57
- (uint8_t*)source_data, source_length,
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 == LZWS_STRING_ALLOCATE_FAILED) {
62
- lzws_ext_raise_error("AllocateError", "allocate error");
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
- else if (result == LZWS_STRING_VALIDATE_FAILED) {
65
- lzws_ext_raise_error("ValidateError", "validate error");
264
+
265
+ if (destination_buffer_length == 0) {
266
+ destination_buffer_length = LZWS_DEFAULT_DESTINATION_BUFFER_LENGTH_FOR_DECOMPRESSOR;
66
267
  }
67
- else if (result == LZWS_STRING_DECOMPRESSOR_CORRUPTED_SOURCE) {
68
- lzws_ext_raise_error("DecompressorCorruptedSourceError", "decompressor received corrupted source");
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
- else if (result != 0) {
71
- lzws_ext_raise_error("UnexpectedError", "unexpected error");
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
- // Ruby copies string on initialization.
75
- VALUE result_string = rb_str_new(destination, destination_length);
76
- free(destination);
77
- return result_string;
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
  }
@@ -9,4 +9,6 @@
9
9
  VALUE lzws_ext_compress_string(VALUE self, VALUE source, VALUE options);
10
10
  VALUE lzws_ext_decompress_string(VALUE self, VALUE source, VALUE options);
11
11
 
12
+ void lzws_ext_string_exports(VALUE root_module);
13
+
12
14
  #endif // LZWS_EXT_STRING_H
data/lib/lzws/error.rb CHANGED
@@ -4,17 +4,18 @@
4
4
  module LZWS
5
5
  class BaseError < ::StandardError; end
6
6
 
7
- class ValidateError < BaseError; end
8
- class AllocateError < BaseError; end
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
- open_file(source, "rb") do |source_io|
36
- open_file(destination, "wb") do |destination_io|
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
- options = COMPRESSOR_DEFAULTS.merge options
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[:buffer_length]
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
- options = DECOMPRESSOR_DEFAULTS.merge options
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]
@@ -9,7 +9,7 @@ require_relative "../validation"
9
9
  module LZWS
10
10
  module Stream
11
11
  class Abstract
12
- # LZWS native stream is not seekable by design.
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
- def close(&writer)
18
- return nil if @is_closed
18
+ # -- write --
19
19
 
20
- flush(&writer)
20
+ def flush(&writer)
21
+ do_not_use_after_close
21
22
 
22
- @native_stream.close
23
- @is_closed = true
23
+ Validation.validate_proc writer
24
24
 
25
- nil
26
- end
25
+ write_result(&writer)
27
26
 
28
- def closed?
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
- protected def write_magic_header(&writer)
59
- loop do
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.flush
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
- write_result(&writer)
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