ruby-lzws 1.0.0 → 1.1.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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