ruby-lzws 1.2.0 → 1.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 62d885a3a75f291ea4b808da4ef4e47a68574e74dae8fe39aad61d228a62643c
4
- data.tar.gz: 90e5fc29ea554392dcf93bb391a38361707c14306099b6654765e399d111ea9d
3
+ metadata.gz: 0cd332331b0c0eac3a21a008ec7796ffbee88d7b79039108f9e4857f5ed2a793
4
+ data.tar.gz: 42f29271be355fe9e48a877a640391d21e8820fd34ca96975b07d063dc3e1abb
5
5
  SHA512:
6
- metadata.gz: 66c07c08d7094be0e9f2d802c01796529e0c14bcde067752aa629d7e3337218e0d6a08e7cfe08725659097fb0e3be90e7565a872a3c79dc96813afa6b235e4ab
7
- data.tar.gz: a1d74a3408881c23f816818931685ec084bd2a0e9c114be212f8a059e37af8da882e9235f3780524e5e67557b3c9945f33b3ecab8535768e4737e972fc724d87
6
+ metadata.gz: 9545c057aee9de7e1528905d215c45fe15aa798d1f81dc03e418e1a9ae0b0669d17282d82997f33844563612f731b1ee87b744639b3d04b173d37b28bf492344
7
+ data.tar.gz: a8eec9b071600eb5ff98efb53aefbf0c48779aa3e468b3c538916bf3335171eefe0e1a73b3d6de8385823e439bd939c6c033cafd5a7c281111f1b4a51b012645
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Ruby bindings for lzws library
2
2
 
3
- | Travis | AppVeyor | Circle | Codecov |
4
- | :---: | :---: | :---: | :---: |
5
- | [![Travis test status](https://travis-ci.com/andrew-aladev/ruby-lzws.svg?branch=master)](https://travis-ci.com/andrew-aladev/ruby-lzws) | [![AppVeyor test status](https://ci.appveyor.com/api/projects/status/github/andrew-aladev/ruby-lzws?branch=master&svg=true)](https://ci.appveyor.com/project/andrew-aladev/ruby-lzws/branch/master) | [![Circle test status](https://circleci.com/gh/andrew-aladev/ruby-lzws/tree/master.svg?style=shield)](https://circleci.com/gh/andrew-aladev/ruby-lzws/tree/master) | [![Codecov](https://codecov.io/gh/andrew-aladev/ruby-lzws/branch/master/graph/badge.svg)](https://codecov.io/gh/andrew-aladev/ruby-lzws) |
3
+ | AppVeyor | Circle | Github actions | Codecov | Gem |
4
+ | :------: | :----: | :------------: | :-----: | :--: |
5
+ | [![AppVeyor test status](https://ci.appveyor.com/api/projects/status/github/andrew-aladev/ruby-lzws?branch=master&svg=true)](https://ci.appveyor.com/project/andrew-aladev/ruby-lzws/branch/master) | [![Circle test status](https://circleci.com/gh/andrew-aladev/ruby-lzws/tree/master.svg?style=shield)](https://circleci.com/gh/andrew-aladev/ruby-lzws/tree/master) | [![Github Actions test status](https://github.com/andrew-aladev/ruby-lzws/workflows/test/badge.svg?branch=master)](https://github.com/andrew-aladev/ruby-lzws/actions) | [![Codecov](https://codecov.io/gh/andrew-aladev/ruby-lzws/branch/master/graph/badge.svg)](https://codecov.io/gh/andrew-aladev/ruby-lzws) | [![Gem](https://img.shields.io/gem/v/ruby-lzws.svg)](https://rubygems.org/gems/ruby-lzws) |
6
6
 
7
7
  See [lzws library](https://github.com/andrew-aladev/lzws).
8
8
 
@@ -61,8 +61,8 @@ ensure
61
61
  end
62
62
  ```
63
63
 
64
- You can create and read `tar.Z` archives with `minitar` for example.
65
- LZWS is compatible with UNIX compress (with default options).
64
+ You can create and read `tar.Z` archives with [minitar](https://github.com/halostatue/minitar).
65
+ LZWS is compatible with [UNIX compress](https://en.wikipedia.org/wiki/Compress) (with default options).
66
66
 
67
67
  ```ruby
68
68
  require "lzws"
@@ -84,12 +84,37 @@ LZWS::Stream::Reader.open "file.tar.Z" do |reader|
84
84
  end
85
85
  ```
86
86
 
87
+ You can also use `Content-Encoding: compress` with [sinatra](http://sinatrarb.com):
88
+
89
+ ```ruby
90
+ require "lzws"
91
+ require "sinatra"
92
+
93
+ get "/" do
94
+ headers["Content-Encoding"] = "compress"
95
+ LZWS::String.compress "TOBEORNOTTOBEORTOBEORNOT"
96
+ end
97
+ ```
98
+
99
+ All functionality (including streaming) can be used inside multiple threads with [parallel](https://github.com/grosser/parallel).
100
+ This code will provide heavy load for your CPU.
101
+
102
+ ```ruby
103
+ require "lzws"
104
+ require "parallel"
105
+
106
+ Parallel.each large_datas do |large_data|
107
+ LZWS::String.compress large_data
108
+ end
109
+ ```
110
+
87
111
  ## Options
88
112
 
89
113
  | Option | Values | Default | Description |
90
114
  |-----------------------------|------------|----------|-------------|
91
115
  | `source_buffer_length` | 0, 2 - inf | 0 (auto) | internal buffer length for source data |
92
116
  | `destination_buffer_length` | 0, 2 - inf | 0 (auto) | internal buffer length for description data |
117
+ | `gvl` | true/false | false | enables global VM lock where possible |
93
118
  | `max_code_bit_length` | 9 - 16 | 16 | max code bit length |
94
119
  | `block_mode` | true/false | true | enables block mode |
95
120
  | `without_magic_header` | true/false | false | disables magic header |
@@ -101,6 +126,10 @@ There are internal buffers for compressed and decompressed data.
101
126
  For example you want to use 1 KB as `source_buffer_length` for compressor - please use 256 B as `destination_buffer_length`.
102
127
  You want to use 256 B as `source_buffer_length` for decompressor - please use 1 KB as `destination_buffer_length`.
103
128
 
129
+ `gvl` is disabled by default, this mode allows running multiple compressors/decompressors in different threads simultaneously.
130
+ Please consider enabling `gvl` if you don't want to launch processors in separate threads.
131
+ If `gvl` is enabled ruby won't waste time on acquiring/releasing VM lock.
132
+
104
133
  You can also read lzws docs for more info about options.
105
134
 
106
135
  | Option | Related constants |
@@ -111,6 +140,7 @@ Possible compressor options:
111
140
  ```
112
141
  :source_buffer_length
113
142
  :destination_buffer_length
143
+ :gvl
114
144
  :max_code_bit_length
115
145
  :block_mode
116
146
  :without_magic_header
@@ -123,6 +153,7 @@ Possible decompressor options:
123
153
  ```
124
154
  :source_buffer_length
125
155
  :destination_buffer_length
156
+ :gvl
126
157
  :without_magic_header
127
158
  :msb
128
159
  :unaligned_bit_groups
@@ -138,18 +169,6 @@ data = LZWS::String.compress "TOBEORNOTTOBEORTOBEORNOT", :msb => true
138
169
  puts LZWS::String.decompress(data, :msb => true)
139
170
  ```
140
171
 
141
- Default options are compatible with UNIX compress (`Content-Encoding: compress`):
142
-
143
- ```ruby
144
- require "lzws"
145
- require "sinatra"
146
-
147
- get "/" do
148
- headers["Content-Encoding"] = "compress"
149
- LZWS::String.compress "TOBEORNOTTOBEORTOBEORNOT"
150
- end
151
- ```
152
-
153
172
  Please read more about compatibility in lzws docs.
154
173
 
155
174
  ## String
@@ -176,7 +195,7 @@ File maintains both source and destination buffers, it accepts both `source_buff
176
195
 
177
196
  ## Stream::Writer
178
197
 
179
- Its behaviour is similar to builtin [`Zlib::GzipWriter`](https://ruby-doc.org/stdlib-2.7.0/libdoc/zlib/rdoc/Zlib/GzipWriter.html).
198
+ Its behaviour is similar to builtin [`Zlib::GzipWriter`](https://ruby-doc.org/stdlib/libdoc/zlib/rdoc/Zlib/GzipWriter.html).
180
199
 
181
200
  Writer maintains destination buffer only, so it accepts `destination_buffer_length` option only.
182
201
 
@@ -210,7 +229,7 @@ Set another encodings, `nil` is just for compatibility with `IO`.
210
229
  #tell
211
230
  ```
212
231
 
213
- See [`IO`](https://ruby-doc.org/core-2.7.0/IO.html) docs.
232
+ See [`IO`](https://ruby-doc.org/core/IO.html) docs.
214
233
 
215
234
  ```
216
235
  #write(*objects)
@@ -220,7 +239,7 @@ See [`IO`](https://ruby-doc.org/core-2.7.0/IO.html) docs.
220
239
  #closed?
221
240
  ```
222
241
 
223
- See [`Zlib::GzipWriter`](https://ruby-doc.org/stdlib-2.7.0/libdoc/zlib/rdoc/Zlib/GzipWriter.html) docs.
242
+ See [`Zlib::GzipWriter`](https://ruby-doc.org/stdlib/libdoc/zlib/rdoc/Zlib/GzipWriter.html) docs.
224
243
 
225
244
  ```
226
245
  #write_nonblock(object, *options)
@@ -234,6 +253,9 @@ Special asynchronous methods missing in `Zlib::GzipWriter`.
234
253
  So it is possible to have asynchronous variants for these synchronous methods.
235
254
  Behaviour is the same as `IO#write_nonblock` method.
236
255
 
256
+ All nonblock operations for file will raise `EBADF` error on Windows.
257
+ Setting file into nonblocking mode is [not available on Windows](https://github.com/ruby/ruby/blob/master/win32/win32.c#L4388).
258
+
237
259
  ```
238
260
  #<<(object)
239
261
  #print(*objects)
@@ -242,11 +264,11 @@ Behaviour is the same as `IO#write_nonblock` method.
242
264
  #puts(*objects)
243
265
  ```
244
266
 
245
- Typical helpers, see [`Zlib::GzipWriter`](https://ruby-doc.org/stdlib-2.7.0/libdoc/zlib/rdoc/Zlib/GzipWriter.html) docs.
267
+ Typical helpers, see [`Zlib::GzipWriter`](https://ruby-doc.org/stdlib/libdoc/zlib/rdoc/Zlib/GzipWriter.html) docs.
246
268
 
247
269
  ## Stream::Reader
248
270
 
249
- Its behaviour is similar to builtin [`Zlib::GzipReader`](https://ruby-doc.org/stdlib-2.7.0/libdoc/zlib/rdoc/Zlib/GzipReader.html).
271
+ Its behaviour is similar to builtin [`Zlib::GzipReader`](https://ruby-doc.org/stdlib/libdoc/zlib/rdoc/Zlib/GzipReader.html).
250
272
 
251
273
  Reader maintains both source and destination buffers, it accepts both `source_buffer_length` and `destination_buffer_length` options.
252
274
 
@@ -281,7 +303,7 @@ Set another encodings.
281
303
  #tell
282
304
  ```
283
305
 
284
- See [`IO`](https://ruby-doc.org/core-2.7.0/IO.html) docs.
306
+ See [`IO`](https://ruby-doc.org/core/IO.html) docs.
285
307
 
286
308
  ```
287
309
  #read(bytes_to_read = nil, out_buffer = nil)
@@ -291,14 +313,14 @@ See [`IO`](https://ruby-doc.org/core-2.7.0/IO.html) docs.
291
313
  #closed?
292
314
  ```
293
315
 
294
- See [`Zlib::GzipReader`](https://ruby-doc.org/stdlib-2.7.0/libdoc/zlib/rdoc/Zlib/GzipReader.html) docs.
316
+ See [`Zlib::GzipReader`](https://ruby-doc.org/stdlib/libdoc/zlib/rdoc/Zlib/GzipReader.html) docs.
295
317
 
296
318
  ```
297
319
  #readpartial(bytes_to_read = nil, out_buffer = nil)
298
320
  #read_nonblock(bytes_to_read, out_buffer = nil, *options)
299
321
  ```
300
322
 
301
- See [`IO`](https://ruby-doc.org/core-2.7.0/IO.html) docs.
323
+ See [`IO`](https://ruby-doc.org/core/IO.html) docs.
302
324
 
303
325
  ```
304
326
  #getbyte
@@ -321,14 +343,26 @@ See [`IO`](https://ruby-doc.org/core-2.7.0/IO.html) docs.
321
343
  #ungetline(line)
322
344
  ```
323
345
 
324
- Typical helpers, see [`Zlib::GzipReader`](https://ruby-doc.org/stdlib-2.7.0/libdoc/zlib/rdoc/Zlib/GzipReader.html) docs.
346
+ Typical helpers, see [`Zlib::GzipReader`](https://ruby-doc.org/stdlib/libdoc/zlib/rdoc/Zlib/GzipReader.html) docs.
347
+
348
+ ## Thread safety
349
+
350
+ `:gvl` option is disabled by default, you can use bindings effectively in multiple threads.
351
+ Please be careful: bindings are not thread safe.
352
+ You should lock all shared data between threads.
353
+
354
+ For example: you should not use same compressor/decompressor inside multiple threads.
355
+ Please verify that you are using each processor inside single thread at the same time.
356
+
357
+ ## Operating systems
358
+
359
+ GNU/Linux, FreeBSD, OSX, Windows (MinGW).
325
360
 
326
361
  ## CI
327
362
 
328
- See universal test script [scripts/ci_test.sh](scripts/ci_test.sh) for CI.
329
363
  Please visit [scripts/test-images](scripts/test-images).
330
- You can run this test script using many native and cross images.
364
+ See universal test script [scripts/ci_test.sh](scripts/ci_test.sh) for CI.
331
365
 
332
366
  ## License
333
367
 
334
- MIT license, see LICENSE and AUTHORS.
368
+ MIT license, see [LICENSE](LICENSE) and [AUTHORS](AUTHORS).
data/ext/extconf.rb CHANGED
@@ -3,23 +3,96 @@
3
3
 
4
4
  require "mkmf"
5
5
 
6
- def require_header(name, types = [])
6
+ have_func "rb_thread_call_without_gvl", "ruby/thread.h"
7
+
8
+ def require_header(name, constants: [], macroses: [], types: [], variables: [])
7
9
  abort "Can't find #{name} header" unless find_header name
8
10
 
11
+ constants.each do |constant|
12
+ abort "Can't find #{constant} constant in #{name} header" unless have_const constant, name
13
+ end
14
+
15
+ macroses.each do |macro|
16
+ abort "Can't find #{macro} macro in #{name} header" unless have_macro macro, name
17
+ end
18
+
9
19
  types.each do |type|
10
20
  abort "Can't find #{type} type in #{name} header" unless find_type type, nil, name
11
21
  end
22
+
23
+ variables.each do |variable|
24
+ abort "Can't find #{variable} variable in #{name} header" unless have_var variable, name
25
+ end
12
26
  end
13
27
 
14
- require_header "lzws/buffer.h"
15
- require_header "lzws/common.h", %w[lzws_result_t]
16
- require_header "lzws/compressor/common.h", %w[lzws_compressor_options_t]
28
+ require_header(
29
+ "lzws/buffer.h",
30
+ :macroses => %w[
31
+ LZWS_DEFAULT_DESTINATION_BUFFER_LENGTH_FOR_COMPRESSOR
32
+ LZWS_DEFAULT_DESTINATION_BUFFER_LENGTH_FOR_DECOMPRESSOR
33
+ LZWS_DEFAULT_SOURCE_BUFFER_LENGTH_FOR_COMPRESSOR
34
+ LZWS_DEFAULT_SOURCE_BUFFER_LENGTH_FOR_DECOMPRESSOR
35
+ ]
36
+ )
37
+ require_header(
38
+ "lzws/common.h",
39
+ :constants => %w[
40
+ LZWS_BIGGEST_MAX_CODE_BIT_LENGTH
41
+ LZWS_LOWEST_MAX_CODE_BIT_LENGTH
42
+ ],
43
+ :types => %w[
44
+ lzws_byte_fast_t
45
+ lzws_result_t
46
+ ]
47
+ )
48
+ require_header(
49
+ "lzws/compressor/common.h",
50
+ :constants => %w[
51
+ LZWS_COMPRESSOR_ALLOCATE_FAILED
52
+ LZWS_COMPRESSOR_INVALID_MAX_CODE_BIT_LENGTH
53
+ LZWS_COMPRESSOR_NEEDS_MORE_DESTINATION
54
+ ],
55
+ :types => %w[lzws_compressor_options_t],
56
+ :variables => %w[LZWS_COMPRESSOR_DEFAULT_OPTIONS]
57
+ )
17
58
  require_header "lzws/compressor/main.h"
18
- require_header "lzws/compressor/state.h", %w[lzws_compressor_state_t]
19
- require_header "lzws/decompressor/common.h", %w[lzws_decompressor_options_t]
59
+ require_header(
60
+ "lzws/compressor/state.h",
61
+ :types => %w[lzws_compressor_state_t]
62
+ )
63
+ require_header(
64
+ "lzws/config.h",
65
+ :macroses => %w[LZWS_VERSION]
66
+ )
67
+ require_header(
68
+ "lzws/decompressor/common.h",
69
+ :constants => %w[
70
+ LZWS_DECOMPRESSOR_ALLOCATE_FAILED
71
+ LZWS_DECOMPRESSOR_CORRUPTED_SOURCE
72
+ LZWS_DECOMPRESSOR_INVALID_MAGIC_HEADER
73
+ LZWS_DECOMPRESSOR_INVALID_MAX_CODE_BIT_LENGTH
74
+ LZWS_DECOMPRESSOR_NEEDS_MORE_DESTINATION
75
+ ],
76
+ :types => %w[lzws_decompressor_options_t],
77
+ :variables => %w[LZWS_DECOMPRESSOR_DEFAULT_OPTIONS]
78
+ )
20
79
  require_header "lzws/decompressor/main.h"
21
- require_header "lzws/decompressor/state.h", %w[lzws_decompressor_state_t]
22
- require_header "lzws/file.h"
80
+ require_header(
81
+ "lzws/decompressor/state.h",
82
+ :types => %w[lzws_decompressor_state_t]
83
+ )
84
+ require_header(
85
+ "lzws/file.h",
86
+ :constants => %w[
87
+ LZWS_FILE_ALLOCATE_FAILED
88
+ LZWS_FILE_DECOMPRESSOR_CORRUPTED_SOURCE
89
+ LZWS_FILE_NOT_ENOUGH_DESTINATION_BUFFER
90
+ LZWS_FILE_NOT_ENOUGH_SOURCE_BUFFER
91
+ LZWS_FILE_READ_FAILED
92
+ LZWS_FILE_VALIDATE_FAILED
93
+ LZWS_FILE_WRITE_FAILED
94
+ ]
95
+ )
23
96
 
24
97
  def require_library(name, functions)
25
98
  functions.each do |function|
@@ -30,22 +103,19 @@ end
30
103
  require_library(
31
104
  "lzws",
32
105
  %w[
106
+ lzws_compress
107
+ lzws_compress_file
108
+ lzws_compressor_finish
109
+ lzws_compressor_free_state
110
+ lzws_compressor_get_initial_state
33
111
  lzws_create_source_buffer_for_compressor
34
- lzws_create_destination_buffer_for_compressor
35
112
  lzws_create_source_buffer_for_decompressor
113
+ lzws_create_destination_buffer_for_compressor
36
114
  lzws_create_destination_buffer_for_decompressor
37
-
38
- lzws_compressor_get_initial_state
39
- lzws_compressor_free_state
40
- lzws_compress
41
- lzws_compressor_finish
42
-
43
- lzws_decompressor_get_initial_state
44
- lzws_decompressor_free_state
45
115
  lzws_decompress
46
-
47
- lzws_compress_file
48
116
  lzws_decompress_file
117
+ lzws_decompressor_free_state
118
+ lzws_decompressor_get_initial_state
49
119
  ]
50
120
  )
51
121
 
@@ -73,7 +143,7 @@ $libs = $libs.split(%r{\s})
73
143
  .uniq
74
144
  .join " "
75
145
 
76
- if ENV["CI"] || ENV["COVERAGE"]
146
+ if ENV["CI"]
77
147
  $CFLAGS << " --coverage"
78
148
  $LDFLAGS << " --coverage"
79
149
  end
@@ -5,17 +5,15 @@
5
5
 
6
6
  #include <lzws/buffer.h>
7
7
 
8
- #include "ruby.h"
9
-
10
8
  VALUE lzws_ext_create_string_buffer(VALUE length)
11
9
  {
12
10
  return rb_str_new(NULL, NUM2SIZET(length));
13
11
  }
14
12
 
15
- VALUE lzws_ext_resize_string_buffer(VALUE args)
13
+ VALUE lzws_ext_resize_string_buffer(VALUE buffer_args)
16
14
  {
17
- VALUE buffer = rb_ary_entry(args, 0);
18
- VALUE length = rb_ary_entry(args, 1);
15
+ VALUE buffer = rb_ary_entry(buffer_args, 0);
16
+ VALUE length = rb_ary_entry(buffer_args, 1);
19
17
 
20
18
  return rb_str_resize(buffer, NUM2SIZET(length));
21
19
  }
@@ -11,12 +11,12 @@ VALUE lzws_ext_create_string_buffer(VALUE length);
11
11
  #define LZWS_EXT_CREATE_STRING_BUFFER(buffer, length, exception) \
12
12
  VALUE buffer = rb_protect(lzws_ext_create_string_buffer, SIZET2NUM(length), &exception);
13
13
 
14
- VALUE lzws_ext_resize_string_buffer(VALUE args);
14
+ VALUE lzws_ext_resize_string_buffer(VALUE buffer_args);
15
15
 
16
- #define LZWS_EXT_RESIZE_STRING_BUFFER(buffer, length, exception) \
17
- VALUE args = rb_ary_new_from_args(2, buffer, SIZET2NUM(length)); \
18
- buffer = rb_protect(lzws_ext_resize_string_buffer, args, &exception); \
19
- RB_GC_GUARD(args);
16
+ #define LZWS_EXT_RESIZE_STRING_BUFFER(buffer, length, exception) \
17
+ VALUE buffer_args = rb_ary_new_from_args(2, buffer, SIZET2NUM(length)); \
18
+ buffer = rb_protect(lzws_ext_resize_string_buffer, buffer_args, &exception); \
19
+ RB_GC_GUARD(buffer_args);
20
20
 
21
21
  void lzws_ext_buffer_exports(VALUE root_module);
22
22
 
data/ext/lzws_ext/error.c CHANGED
@@ -3,9 +3,7 @@
3
3
 
4
4
  #include "lzws_ext/error.h"
5
5
 
6
- #include "ruby.h"
7
-
8
- static inline NORETURN(void raise(const char* name, const char* description))
6
+ static inline NORETURN(void raise_error(const char* name, const char* description))
9
7
  {
10
8
  VALUE module = rb_define_module(LZWS_EXT_MODULE_NAME);
11
9
  VALUE error = rb_const_get(module, rb_intern(name));
@@ -16,28 +14,28 @@ void lzws_ext_raise_error(lzws_ext_result_t ext_result)
16
14
  {
17
15
  switch (ext_result) {
18
16
  case LZWS_EXT_ERROR_ALLOCATE_FAILED:
19
- raise("AllocateError", "allocate error");
17
+ raise_error("AllocateError", "allocate error");
20
18
  case LZWS_EXT_ERROR_VALIDATE_FAILED:
21
- raise("ValidateError", "validate error");
19
+ raise_error("ValidateError", "validate error");
22
20
 
23
21
  case LZWS_EXT_ERROR_USED_AFTER_CLOSE:
24
- raise("UsedAfterCloseError", "used after closed");
22
+ raise_error("UsedAfterCloseError", "used after closed");
25
23
  case LZWS_EXT_ERROR_NOT_ENOUGH_SOURCE_BUFFER:
26
- raise("NotEnoughSourceBufferError", "not enough source buffer");
24
+ raise_error("NotEnoughSourceBufferError", "not enough source buffer");
27
25
  case LZWS_EXT_ERROR_NOT_ENOUGH_DESTINATION_BUFFER:
28
- raise("NotEnoughDestinationBufferError", "not enough destination buffer");
26
+ raise_error("NotEnoughDestinationBufferError", "not enough destination buffer");
29
27
  case LZWS_EXT_ERROR_DECOMPRESSOR_CORRUPTED_SOURCE:
30
- raise("DecompressorCorruptedSourceError", "decompressor received corrupted source");
28
+ raise_error("DecompressorCorruptedSourceError", "decompressor received corrupted source");
31
29
 
32
30
  case LZWS_EXT_ERROR_ACCESS_IO:
33
- raise("AccessIOError", "failed to access IO");
31
+ raise_error("AccessIOError", "failed to access IO");
34
32
  case LZWS_EXT_ERROR_READ_IO:
35
- raise("ReadIOError", "failed to read IO");
33
+ raise_error("ReadIOError", "failed to read IO");
36
34
  case LZWS_EXT_ERROR_WRITE_IO:
37
- raise("WriteIOError", "failed to write IO");
35
+ raise_error("WriteIOError", "failed to write IO");
38
36
 
39
37
  default:
40
38
  // LZWS_EXT_ERROR_UNEXPECTED
41
- raise("UnexpectedError", "unexpected error");
39
+ raise_error("UnexpectedError", "unexpected error");
42
40
  }
43
41
  }
@@ -0,0 +1,24 @@
1
+ // Ruby bindings for lzws library.
2
+ // Copyright (c) 2019 AUTHORS, MIT License.
3
+
4
+ #if !defined(LZWS_EXT_GVL_H)
5
+ #define LZWS_EXT_GVL_H
6
+
7
+ #ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
8
+
9
+ #include "ruby/thread.h"
10
+
11
+ #define LZWS_EXT_GVL_WRAP(gvl, function, data) \
12
+ if (gvl) { \
13
+ function((void*) data); \
14
+ } else { \
15
+ rb_thread_call_without_gvl(function, (void*) data, RUBY_UBF_IO, NULL); \
16
+ }
17
+
18
+ #else
19
+
20
+ #define LZWS_EXT_GVL_WRAP(_gvl, function, data) function((void*) data);
21
+
22
+ #endif
23
+
24
+ #endif // LZWS_EXT_GVL_H
data/ext/lzws_ext/io.c CHANGED
@@ -6,11 +6,13 @@
6
6
  #include <lzws/file.h>
7
7
 
8
8
  #include "lzws_ext/error.h"
9
+ #include "lzws_ext/gvl.h"
9
10
  #include "lzws_ext/macro.h"
10
11
  #include "lzws_ext/option.h"
11
- #include "ruby.h"
12
12
  #include "ruby/io.h"
13
13
 
14
+ // -- utils --
15
+
14
16
  #define GET_FILE(target) \
15
17
  Check_Type(target, T_FILE); \
16
18
  \
@@ -47,20 +49,52 @@ static inline lzws_ext_result_t get_file_error(lzws_result_t result)
47
49
  }
48
50
  }
49
51
 
52
+ // -- compress --
53
+
54
+ typedef struct
55
+ {
56
+ FILE* source_file;
57
+ size_t source_buffer_length;
58
+ FILE* destination_file;
59
+ size_t destination_buffer_length;
60
+ lzws_compressor_options_t* compressor_options_ptr;
61
+ lzws_result_t result;
62
+ } compress_args_t;
63
+
64
+ static inline void* compress_wrapper(void* data)
65
+ {
66
+ compress_args_t* args = data;
67
+
68
+ args->result = lzws_compress_file(
69
+ args->source_file,
70
+ args->source_buffer_length,
71
+ args->destination_file,
72
+ args->destination_buffer_length,
73
+ args->compressor_options_ptr);
74
+
75
+ return NULL;
76
+ }
77
+
50
78
  VALUE lzws_ext_compress_io(VALUE LZWS_EXT_UNUSED(self), VALUE source, VALUE destination, VALUE options)
51
79
  {
52
80
  GET_FILE(source);
53
81
  GET_FILE(destination);
54
82
  Check_Type(options, T_HASH);
83
+ LZWS_EXT_GET_SIZE_OPTION(options, source_buffer_length);
84
+ LZWS_EXT_GET_SIZE_OPTION(options, destination_buffer_length);
85
+ LZWS_EXT_GET_BOOL_OPTION(options, gvl);
55
86
  LZWS_EXT_GET_COMPRESSOR_OPTIONS(options);
56
- LZWS_EXT_GET_BUFFER_LENGTH_OPTION(options, source_buffer_length);
57
- LZWS_EXT_GET_BUFFER_LENGTH_OPTION(options, destination_buffer_length);
58
87
 
59
- lzws_result_t result = lzws_compress_file(
60
- source_file, source_buffer_length, destination_file, destination_buffer_length, &compressor_options);
88
+ compress_args_t args = {
89
+ .source_file = source_file,
90
+ .source_buffer_length = source_buffer_length,
91
+ .destination_file = destination_file,
92
+ .destination_buffer_length = destination_buffer_length,
93
+ .compressor_options_ptr = &compressor_options};
61
94
 
62
- if (result != 0) {
63
- lzws_ext_raise_error(get_file_error(result));
95
+ LZWS_EXT_GVL_WRAP(gvl, compress_wrapper, &args);
96
+ if (args.result != 0) {
97
+ lzws_ext_raise_error(get_file_error(args.result));
64
98
  }
65
99
 
66
100
  // Ruby itself won't flush stdio file before closing fd, flush is required.
@@ -69,20 +103,52 @@ VALUE lzws_ext_compress_io(VALUE LZWS_EXT_UNUSED(self), VALUE source, VALUE dest
69
103
  return Qnil;
70
104
  }
71
105
 
106
+ // -- decompress --
107
+
108
+ typedef struct
109
+ {
110
+ FILE* source_file;
111
+ size_t source_buffer_length;
112
+ FILE* destination_file;
113
+ size_t destination_buffer_length;
114
+ lzws_decompressor_options_t* decompressor_options_ptr;
115
+ lzws_result_t result;
116
+ } decompress_args_t;
117
+
118
+ static inline void* decompress_wrapper(void* data)
119
+ {
120
+ decompress_args_t* args = data;
121
+
122
+ args->result = lzws_decompress_file(
123
+ args->source_file,
124
+ args->source_buffer_length,
125
+ args->destination_file,
126
+ args->destination_buffer_length,
127
+ args->decompressor_options_ptr);
128
+
129
+ return NULL;
130
+ }
131
+
72
132
  VALUE lzws_ext_decompress_io(VALUE LZWS_EXT_UNUSED(self), VALUE source, VALUE destination, VALUE options)
73
133
  {
74
134
  GET_FILE(source);
75
135
  GET_FILE(destination);
76
136
  Check_Type(options, T_HASH);
137
+ LZWS_EXT_GET_SIZE_OPTION(options, source_buffer_length);
138
+ LZWS_EXT_GET_SIZE_OPTION(options, destination_buffer_length);
139
+ LZWS_EXT_GET_BOOL_OPTION(options, gvl);
77
140
  LZWS_EXT_GET_DECOMPRESSOR_OPTIONS(options);
78
- LZWS_EXT_GET_BUFFER_LENGTH_OPTION(options, source_buffer_length);
79
- LZWS_EXT_GET_BUFFER_LENGTH_OPTION(options, destination_buffer_length);
80
141
 
81
- lzws_result_t result = lzws_decompress_file(
82
- source_file, source_buffer_length, destination_file, destination_buffer_length, &decompressor_options);
142
+ decompress_args_t args = {
143
+ .source_file = source_file,
144
+ .source_buffer_length = source_buffer_length,
145
+ .destination_file = destination_file,
146
+ .destination_buffer_length = destination_buffer_length,
147
+ .decompressor_options_ptr = &decompressor_options};
83
148
 
84
- if (result != 0) {
85
- lzws_ext_raise_error(get_file_error(result));
149
+ LZWS_EXT_GVL_WRAP(gvl, decompress_wrapper, &args);
150
+ if (args.result != 0) {
151
+ lzws_ext_raise_error(get_file_error(args.result));
86
152
  }
87
153
 
88
154
  // Ruby itself won't flush stdio file before closing fd, flush is required.
@@ -91,6 +157,8 @@ VALUE lzws_ext_decompress_io(VALUE LZWS_EXT_UNUSED(self), VALUE source, VALUE de
91
157
  return Qnil;
92
158
  }
93
159
 
160
+ // -- exports --
161
+
94
162
  void lzws_ext_io_exports(VALUE root_module)
95
163
  {
96
164
  rb_define_module_function(root_module, "_native_compress_io", RUBY_METHOD_FUNC(lzws_ext_compress_io), 3);
data/ext/lzws_ext/main.c CHANGED
@@ -1,13 +1,14 @@
1
1
  // Ruby bindings for lzws library.
2
2
  // Copyright (c) 2019 AUTHORS, MIT License.
3
3
 
4
+ #include <lzws/config.h>
5
+
4
6
  #include "lzws_ext/buffer.h"
5
7
  #include "lzws_ext/io.h"
6
8
  #include "lzws_ext/option.h"
7
9
  #include "lzws_ext/stream/compressor.h"
8
10
  #include "lzws_ext/stream/decompressor.h"
9
11
  #include "lzws_ext/string.h"
10
- #include "ruby.h"
11
12
 
12
13
  void Init_lzws_ext()
13
14
  {
@@ -19,4 +20,7 @@ void Init_lzws_ext()
19
20
  lzws_ext_compressor_exports(root_module);
20
21
  lzws_ext_decompressor_exports(root_module);
21
22
  lzws_ext_string_exports(root_module);
23
+
24
+ VALUE version = rb_str_new2(LZWS_VERSION);
25
+ rb_define_const(root_module, "LIBRARY_VERSION", rb_obj_freeze(version));
22
26
  }