ruby-lzws 1.0.0 → 1.1.4

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,22 +5,25 @@
5
5
  #define LZWS_EXT_STREAM_DECOMPRESSOR_H
6
6
 
7
7
  #include <lzws/decompressor/state.h>
8
+ #include <stdlib.h>
8
9
 
10
+ #include "lzws_ext/common.h"
9
11
  #include "ruby.h"
10
12
 
11
13
  typedef struct {
12
14
  lzws_decompressor_state_t* state_ptr;
13
- uint8_t* destination_buffer;
15
+ lzws_ext_byte_t* destination_buffer;
14
16
  size_t destination_buffer_length;
15
- uint8_t* remaining_destination_buffer;
17
+ lzws_ext_byte_t* remaining_destination_buffer;
16
18
  size_t remaining_destination_buffer_length;
17
19
  } lzws_ext_decompressor_t;
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,280 @@
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/common.h>
8
+ #include <lzws/compressor/common.h>
9
+ #include <lzws/compressor/main.h>
10
+ #include <lzws/compressor/state.h>
11
+ #include <lzws/decompressor/common.h>
12
+ #include <lzws/decompressor/main.h>
13
+ #include <lzws/decompressor/state.h>
14
+ #include <stdlib.h>
7
15
 
16
+ #include "lzws_ext/buffer.h"
8
17
  #include "lzws_ext/error.h"
9
18
  #include "lzws_ext/macro.h"
10
19
  #include "lzws_ext/option.h"
11
- #include "lzws_ext/string.h"
20
+ #include "ruby.h"
21
+
22
+ // -- buffer --
23
+
24
+ static inline lzws_ext_result_t increase_destination_buffer(
25
+ VALUE destination_value, size_t destination_length,
26
+ size_t* remaining_destination_buffer_length_ptr, size_t destination_buffer_length)
27
+ {
28
+ if (*remaining_destination_buffer_length_ptr == destination_buffer_length) {
29
+ // We want to write more data at once, than buffer has.
30
+ return LZWS_EXT_ERROR_NOT_ENOUGH_DESTINATION_BUFFER;
31
+ }
32
+
33
+ int exception;
34
+
35
+ LZWS_EXT_RESIZE_STRING_BUFFER(destination_value, destination_length + destination_buffer_length, exception);
36
+ if (exception != 0) {
37
+ return LZWS_EXT_ERROR_ALLOCATE_FAILED;
38
+ }
39
+
40
+ *remaining_destination_buffer_length_ptr = destination_buffer_length;
41
+
42
+ return 0;
43
+ }
44
+
45
+ // -- utils --
46
+
47
+ #define GET_SOURCE_DATA(source_value) \
48
+ Check_Type(source_value, T_STRING); \
49
+ \
50
+ const char* source = RSTRING_PTR(source_value); \
51
+ size_t source_length = RSTRING_LEN(source_value);
12
52
 
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);
53
+ // -- compress --
18
54
 
19
- VALUE lzws_ext_compress_string(VALUE LZWS_EXT_UNUSED(self), VALUE source, VALUE options)
55
+ #define BUFFERED_COMPRESS(function, ...) \
56
+ while (true) { \
57
+ lzws_ext_byte_t* remaining_destination_buffer = (lzws_ext_byte_t*)RSTRING_PTR(destination_value) + destination_length; \
58
+ size_t prev_remaining_destination_buffer_length = remaining_destination_buffer_length; \
59
+ \
60
+ result = function(__VA_ARGS__, &remaining_destination_buffer, &remaining_destination_buffer_length); \
61
+ \
62
+ if ( \
63
+ result != 0 && \
64
+ result != LZWS_COMPRESSOR_NEEDS_MORE_DESTINATION) { \
65
+ return LZWS_EXT_ERROR_UNEXPECTED; \
66
+ } \
67
+ \
68
+ destination_length += prev_remaining_destination_buffer_length - remaining_destination_buffer_length; \
69
+ \
70
+ if (result == LZWS_COMPRESSOR_NEEDS_MORE_DESTINATION) { \
71
+ ext_result = increase_destination_buffer( \
72
+ destination_value, destination_length, \
73
+ &remaining_destination_buffer_length, destination_buffer_length); \
74
+ \
75
+ if (ext_result != 0) { \
76
+ return ext_result; \
77
+ } \
78
+ \
79
+ continue; \
80
+ } \
81
+ \
82
+ break; \
83
+ }
84
+
85
+ static inline lzws_ext_result_t compress(
86
+ lzws_compressor_state_t* state_ptr,
87
+ const char* source, size_t source_length,
88
+ VALUE destination_value, size_t destination_buffer_length)
89
+ {
90
+ lzws_result_t result;
91
+ lzws_ext_result_t ext_result;
92
+
93
+ lzws_ext_byte_t* remaining_source = (lzws_ext_byte_t*)source;
94
+ size_t remaining_source_length = source_length;
95
+
96
+ size_t destination_length = 0;
97
+ size_t remaining_destination_buffer_length = destination_buffer_length;
98
+
99
+ BUFFERED_COMPRESS(lzws_compress, state_ptr, &remaining_source, &remaining_source_length);
100
+ BUFFERED_COMPRESS(lzws_compressor_finish, state_ptr);
101
+
102
+ int exception;
103
+
104
+ LZWS_EXT_RESIZE_STRING_BUFFER(destination_value, destination_length, exception);
105
+ if (exception != 0) {
106
+ return LZWS_EXT_ERROR_ALLOCATE_FAILED;
107
+ }
108
+
109
+ return 0;
110
+ }
111
+
112
+ VALUE lzws_ext_compress_string(VALUE LZWS_EXT_UNUSED(self), VALUE source_value, VALUE options)
20
113
  {
21
- GET_STRING(source);
114
+ GET_SOURCE_DATA(source_value);
115
+ Check_Type(options, T_HASH);
22
116
  LZWS_EXT_GET_COMPRESSOR_OPTIONS(options);
117
+ LZWS_EXT_GET_BUFFER_LENGTH_OPTION(options, destination_buffer_length);
23
118
 
24
- char* destination;
25
- size_t destination_length;
119
+ lzws_compressor_state_t* state_ptr;
26
120
 
27
- lzws_result_t result = lzws_compress_string(
28
- (uint8_t*)source_data, source_length,
29
- (uint8_t**)&destination, &destination_length, buffer_length,
121
+ lzws_result_t result = lzws_compressor_get_initial_state(
122
+ &state_ptr,
30
123
  without_magic_header, max_code_bit_length, block_mode, msb, unaligned_bit_groups, quiet);
31
124
 
32
- if (result == LZWS_STRING_ALLOCATE_FAILED) {
33
- lzws_ext_raise_error("AllocateError", "allocate error");
125
+ if (result != 0) {
126
+ switch (result) {
127
+ case LZWS_COMPRESSOR_ALLOCATE_FAILED:
128
+ lzws_ext_raise_error(LZWS_EXT_ERROR_ALLOCATE_FAILED);
129
+ case LZWS_COMPRESSOR_INVALID_MAX_CODE_BIT_LENGTH:
130
+ lzws_ext_raise_error(LZWS_EXT_ERROR_VALIDATE_FAILED);
131
+ default:
132
+ lzws_ext_raise_error(LZWS_EXT_ERROR_UNEXPECTED);
133
+ }
34
134
  }
35
- else if (result == LZWS_STRING_VALIDATE_FAILED) {
36
- lzws_ext_raise_error("ValidateError", "validate error");
135
+
136
+ if (destination_buffer_length == 0) {
137
+ destination_buffer_length = LZWS_DEFAULT_DESTINATION_BUFFER_LENGTH_FOR_COMPRESSOR;
37
138
  }
38
- else if (result != 0) {
39
- lzws_ext_raise_error("UnexpectedError", "unexpected error");
139
+
140
+ int exception;
141
+
142
+ LZWS_EXT_CREATE_STRING_BUFFER(destination_value, destination_buffer_length, exception);
143
+ if (exception != 0) {
144
+ lzws_compressor_free_state(state_ptr);
145
+ lzws_ext_raise_error(LZWS_EXT_ERROR_ALLOCATE_FAILED);
40
146
  }
41
147
 
42
- // Ruby copies string on initialization.
43
- VALUE result_string = rb_str_new(destination, destination_length);
44
- free(destination);
45
- return result_string;
148
+ lzws_ext_result_t ext_result = compress(
149
+ state_ptr,
150
+ source, source_length,
151
+ destination_value, destination_buffer_length);
152
+
153
+ lzws_compressor_free_state(state_ptr);
154
+
155
+ if (ext_result != 0) {
156
+ lzws_ext_raise_error(ext_result);
157
+ }
158
+
159
+ return destination_value;
46
160
  }
47
161
 
48
- VALUE lzws_ext_decompress_string(VALUE LZWS_EXT_UNUSED(self), VALUE source, VALUE options)
162
+ // -- decompress --
163
+
164
+ static inline lzws_ext_result_t decompress(
165
+ lzws_decompressor_state_t* state_ptr,
166
+ const char* source, size_t source_length,
167
+ VALUE destination_value, size_t destination_buffer_length)
49
168
  {
50
- GET_STRING(source);
169
+ lzws_result_t result;
170
+ lzws_ext_result_t ext_result;
171
+
172
+ lzws_ext_byte_t* remaining_source = (lzws_ext_byte_t*)source;
173
+ size_t remaining_source_length = source_length;
174
+
175
+ size_t destination_length = 0;
176
+ size_t remaining_destination_buffer_length = destination_buffer_length;
177
+
178
+ while (true) {
179
+ lzws_ext_byte_t* remaining_destination_buffer = (lzws_ext_byte_t*)RSTRING_PTR(destination_value) + destination_length;
180
+ size_t prev_remaining_destination_buffer_length = remaining_destination_buffer_length;
181
+
182
+ result = lzws_decompress(
183
+ state_ptr,
184
+ &remaining_source, &remaining_source_length,
185
+ &remaining_destination_buffer, &remaining_destination_buffer_length);
186
+
187
+ if (
188
+ result != 0 &&
189
+ result != LZWS_DECOMPRESSOR_NEEDS_MORE_DESTINATION) {
190
+ switch (result) {
191
+ case LZWS_DECOMPRESSOR_INVALID_MAGIC_HEADER:
192
+ case LZWS_DECOMPRESSOR_INVALID_MAX_CODE_BIT_LENGTH:
193
+ return LZWS_EXT_ERROR_VALIDATE_FAILED;
194
+ case LZWS_DECOMPRESSOR_CORRUPTED_SOURCE:
195
+ return LZWS_EXT_ERROR_DECOMPRESSOR_CORRUPTED_SOURCE;
196
+ default:
197
+ return LZWS_EXT_ERROR_UNEXPECTED;
198
+ }
199
+ }
200
+
201
+ destination_length += prev_remaining_destination_buffer_length - remaining_destination_buffer_length;
202
+
203
+ if (result == LZWS_DECOMPRESSOR_NEEDS_MORE_DESTINATION) {
204
+ ext_result = increase_destination_buffer(
205
+ destination_value, destination_length,
206
+ &remaining_destination_buffer_length, destination_buffer_length);
207
+
208
+ if (ext_result != 0) {
209
+ return ext_result;
210
+ }
211
+
212
+ continue;
213
+ }
214
+
215
+ break;
216
+ }
217
+
218
+ int exception;
219
+
220
+ LZWS_EXT_RESIZE_STRING_BUFFER(destination_value, destination_length, exception);
221
+ if (exception != 0) {
222
+ return LZWS_EXT_ERROR_ALLOCATE_FAILED;
223
+ }
224
+
225
+ return 0;
226
+ }
227
+
228
+ VALUE lzws_ext_decompress_string(VALUE LZWS_EXT_UNUSED(self), VALUE source_value, VALUE options)
229
+ {
230
+ GET_SOURCE_DATA(source_value);
231
+ Check_Type(options, T_HASH);
51
232
  LZWS_EXT_GET_DECOMPRESSOR_OPTIONS(options);
233
+ LZWS_EXT_GET_BUFFER_LENGTH_OPTION(options, destination_buffer_length);
52
234
 
53
- char* destination;
54
- size_t destination_length;
235
+ lzws_decompressor_state_t* state_ptr;
55
236
 
56
- lzws_result_t result = lzws_decompress_string(
57
- (uint8_t*)source_data, source_length,
58
- (uint8_t**)&destination, &destination_length, buffer_length,
237
+ lzws_result_t result = lzws_decompressor_get_initial_state(
238
+ &state_ptr,
59
239
  without_magic_header, msb, unaligned_bit_groups, quiet);
60
240
 
61
- if (result == LZWS_STRING_ALLOCATE_FAILED) {
62
- lzws_ext_raise_error("AllocateError", "allocate error");
241
+ if (result != 0) {
242
+ switch (result) {
243
+ case LZWS_DECOMPRESSOR_ALLOCATE_FAILED:
244
+ lzws_ext_raise_error(LZWS_EXT_ERROR_ALLOCATE_FAILED);
245
+ default:
246
+ lzws_ext_raise_error(LZWS_EXT_ERROR_UNEXPECTED);
247
+ }
63
248
  }
64
- else if (result == LZWS_STRING_VALIDATE_FAILED) {
65
- lzws_ext_raise_error("ValidateError", "validate error");
249
+
250
+ if (destination_buffer_length == 0) {
251
+ destination_buffer_length = LZWS_DEFAULT_DESTINATION_BUFFER_LENGTH_FOR_DECOMPRESSOR;
66
252
  }
67
- else if (result == LZWS_STRING_DECOMPRESSOR_CORRUPTED_SOURCE) {
68
- lzws_ext_raise_error("DecompressorCorruptedSourceError", "decompressor received corrupted source");
253
+
254
+ int exception;
255
+
256
+ LZWS_EXT_CREATE_STRING_BUFFER(destination_value, destination_buffer_length, exception);
257
+ if (exception != 0) {
258
+ lzws_decompressor_free_state(state_ptr);
259
+ lzws_ext_raise_error(LZWS_EXT_ERROR_ALLOCATE_FAILED);
69
260
  }
70
- else if (result != 0) {
71
- lzws_ext_raise_error("UnexpectedError", "unexpected error");
261
+
262
+ lzws_ext_result_t ext_result = decompress(
263
+ state_ptr,
264
+ source, source_length,
265
+ destination_value, destination_buffer_length);
266
+
267
+ lzws_decompressor_free_state(state_ptr);
268
+
269
+ if (ext_result != 0) {
270
+ lzws_ext_raise_error(ext_result);
72
271
  }
73
272
 
74
- // Ruby copies string on initialization.
75
- VALUE result_string = rb_str_new(destination, destination_length);
76
- free(destination);
77
- return result_string;
273
+ return destination_value;
274
+ }
275
+
276
+ void lzws_ext_string_exports(VALUE root_module)
277
+ {
278
+ rb_define_module_function(root_module, "_native_compress_string", RUBY_METHOD_FUNC(lzws_ext_compress_string), 2);
279
+ rb_define_module_function(root_module, "_native_decompress_string", RUBY_METHOD_FUNC(lzws_ext_decompress_string), 2);
78
280
  }
@@ -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
@@ -1,4 +1,8 @@
1
- require_relative "lzws/file"
1
+ # Ruby bindings for lzws library.
2
+ # Copyright (c) 2019 AUTHORS, MIT License.
3
+
2
4
  require_relative "lzws/stream/reader"
3
5
  require_relative "lzws/stream/writer"
6
+ require_relative "lzws/file"
4
7
  require_relative "lzws/string"
8
+ require_relative "lzws/version"
@@ -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
@@ -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