ruby-lzws 1.2.0 → 1.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 62d885a3a75f291ea4b808da4ef4e47a68574e74dae8fe39aad61d228a62643c
4
- data.tar.gz: 90e5fc29ea554392dcf93bb391a38361707c14306099b6654765e399d111ea9d
3
+ metadata.gz: 46de1059ebc7e81a3c93869a70801e20571e99631a0a9a8acc0cad9b06e9c21f
4
+ data.tar.gz: c4d1d3859bd374404b496b9ae393f34b08b49bf94655e420862545fd8949cc12
5
5
  SHA512:
6
- metadata.gz: 66c07c08d7094be0e9f2d802c01796529e0c14bcde067752aa629d7e3337218e0d6a08e7cfe08725659097fb0e3be90e7565a872a3c79dc96813afa6b235e4ab
7
- data.tar.gz: a1d74a3408881c23f816818931685ec084bd2a0e9c114be212f8a059e37af8da882e9235f3780524e5e67557b3c9945f33b3ecab8535768e4737e972fc724d87
6
+ metadata.gz: 88f2f3627fb6f98fffc6c1289301a9362c6ab22edc7de42732a90c85b1065bd2e3ee73547dbab0ee1c40d14c3490053f43d36d7b67abb25aa6ec2e938211e95e
7
+ data.tar.gz: e6b97fea5d39bd2ee8b4e9ad43d16453f0640536c86dd9e0d425443c3a48a5bb3bf99c5bb452e4065f3c21489572a4bf05a9809b1eccfd7ef39773a1eea0d05d
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
+ | Travis | AppVeyor | Circle | Codecov | Gem |
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) | [![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) for example.
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,25 @@ 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
+
87
99
  ## Options
88
100
 
89
101
  | Option | Values | Default | Description |
90
102
  |-----------------------------|------------|----------|-------------|
91
103
  | `source_buffer_length` | 0, 2 - inf | 0 (auto) | internal buffer length for source data |
92
104
  | `destination_buffer_length` | 0, 2 - inf | 0 (auto) | internal buffer length for description data |
105
+ | `gvl` | true/false | false | enables global VM lock where possible |
93
106
  | `max_code_bit_length` | 9 - 16 | 16 | max code bit length |
94
107
  | `block_mode` | true/false | true | enables block mode |
95
108
  | `without_magic_header` | true/false | false | disables magic header |
@@ -101,6 +114,10 @@ There are internal buffers for compressed and decompressed data.
101
114
  For example you want to use 1 KB as `source_buffer_length` for compressor - please use 256 B as `destination_buffer_length`.
102
115
  You want to use 256 B as `source_buffer_length` for decompressor - please use 1 KB as `destination_buffer_length`.
103
116
 
117
+ `gvl` is disabled by default, this mode allows running multiple compressors/decompressors in different threads simultaneously.
118
+ Please consider enabling `gvl` if you don't want to launch processors in separate threads.
119
+ If `gvl` is enabled ruby won't waste time on acquiring/releasing VM lock.
120
+
104
121
  You can also read lzws docs for more info about options.
105
122
 
106
123
  | Option | Related constants |
@@ -111,6 +128,7 @@ Possible compressor options:
111
128
  ```
112
129
  :source_buffer_length
113
130
  :destination_buffer_length
131
+ :gvl
114
132
  :max_code_bit_length
115
133
  :block_mode
116
134
  :without_magic_header
@@ -123,6 +141,7 @@ Possible decompressor options:
123
141
  ```
124
142
  :source_buffer_length
125
143
  :destination_buffer_length
144
+ :gvl
126
145
  :without_magic_header
127
146
  :msb
128
147
  :unaligned_bit_groups
@@ -138,18 +157,6 @@ data = LZWS::String.compress "TOBEORNOTTOBEORTOBEORNOT", :msb => true
138
157
  puts LZWS::String.decompress(data, :msb => true)
139
158
  ```
140
159
 
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
160
  Please read more about compatibility in lzws docs.
154
161
 
155
162
  ## String
@@ -323,12 +330,18 @@ See [`IO`](https://ruby-doc.org/core-2.7.0/IO.html) docs.
323
330
 
324
331
  Typical helpers, see [`Zlib::GzipReader`](https://ruby-doc.org/stdlib-2.7.0/libdoc/zlib/rdoc/Zlib/GzipReader.html) docs.
325
332
 
333
+ ## Thread safety
334
+
335
+ `:gvl` option is disabled by default, you can use bindings effectively in multiple threads.
336
+ Please be careful: bindings are not thread safe.
337
+ You should lock all shared data between threads.
338
+
326
339
  ## CI
327
340
 
328
- See universal test script [scripts/ci_test.sh](scripts/ci_test.sh) for CI.
329
341
  Please visit [scripts/test-images](scripts/test-images).
330
- You can run this test script using many native and cross images.
342
+ See universal test script [scripts/ci_test.sh](scripts/ci_test.sh) for CI.
343
+ You can run this script using many native and cross images.
331
344
 
332
345
  ## License
333
346
 
334
- MIT license, see LICENSE and AUTHORS.
347
+ MIT license, see [LICENSE](LICENSE) and [AUTHORS](AUTHORS).
@@ -3,6 +3,8 @@
3
3
 
4
4
  require "mkmf"
5
5
 
6
+ have_func "rb_thread_call_without_gvl", "ruby/thread.h"
7
+
6
8
  def require_header(name, types = [])
7
9
  abort "Can't find #{name} header" unless find_header name
8
10
 
@@ -12,10 +12,10 @@ VALUE lzws_ext_create_string_buffer(VALUE length)
12
12
  return rb_str_new(NULL, NUM2SIZET(length));
13
13
  }
14
14
 
15
- VALUE lzws_ext_resize_string_buffer(VALUE args)
15
+ VALUE lzws_ext_resize_string_buffer(VALUE buffer_args)
16
16
  {
17
- VALUE buffer = rb_ary_entry(args, 0);
18
- VALUE length = rb_ary_entry(args, 1);
17
+ VALUE buffer = rb_ary_entry(buffer_args, 0);
18
+ VALUE length = rb_ary_entry(buffer_args, 1);
19
19
 
20
20
  return rb_str_resize(buffer, NUM2SIZET(length));
21
21
  }
@@ -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
 
@@ -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
@@ -6,11 +6,14 @@
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
12
  #include "ruby.h"
12
13
  #include "ruby/io.h"
13
14
 
15
+ // -- utils --
16
+
14
17
  #define GET_FILE(target) \
15
18
  Check_Type(target, T_FILE); \
16
19
  \
@@ -47,20 +50,52 @@ static inline lzws_ext_result_t get_file_error(lzws_result_t result)
47
50
  }
48
51
  }
49
52
 
53
+ // -- compress --
54
+
55
+ typedef struct
56
+ {
57
+ FILE* source_file;
58
+ size_t source_buffer_length;
59
+ FILE* destination_file;
60
+ size_t destination_buffer_length;
61
+ lzws_compressor_options_t* compressor_options_ptr;
62
+ lzws_result_t result;
63
+ } compress_args_t;
64
+
65
+ static inline void* compress_wrapper(void* data)
66
+ {
67
+ compress_args_t* args = data;
68
+
69
+ args->result = lzws_compress_file(
70
+ args->source_file,
71
+ args->source_buffer_length,
72
+ args->destination_file,
73
+ args->destination_buffer_length,
74
+ args->compressor_options_ptr);
75
+
76
+ return NULL;
77
+ }
78
+
50
79
  VALUE lzws_ext_compress_io(VALUE LZWS_EXT_UNUSED(self), VALUE source, VALUE destination, VALUE options)
51
80
  {
52
81
  GET_FILE(source);
53
82
  GET_FILE(destination);
54
83
  Check_Type(options, T_HASH);
84
+ LZWS_EXT_GET_SIZE_OPTION(options, source_buffer_length);
85
+ LZWS_EXT_GET_SIZE_OPTION(options, destination_buffer_length);
86
+ LZWS_EXT_GET_BOOL_OPTION(options, gvl);
55
87
  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
88
 
59
- lzws_result_t result = lzws_compress_file(
60
- source_file, source_buffer_length, destination_file, destination_buffer_length, &compressor_options);
89
+ compress_args_t args = {
90
+ .source_file = source_file,
91
+ .source_buffer_length = source_buffer_length,
92
+ .destination_file = destination_file,
93
+ .destination_buffer_length = destination_buffer_length,
94
+ .compressor_options_ptr = &compressor_options};
61
95
 
62
- if (result != 0) {
63
- lzws_ext_raise_error(get_file_error(result));
96
+ LZWS_EXT_GVL_WRAP(gvl, compress_wrapper, &args);
97
+ if (args.result != 0) {
98
+ lzws_ext_raise_error(get_file_error(args.result));
64
99
  }
65
100
 
66
101
  // Ruby itself won't flush stdio file before closing fd, flush is required.
@@ -69,20 +104,52 @@ VALUE lzws_ext_compress_io(VALUE LZWS_EXT_UNUSED(self), VALUE source, VALUE dest
69
104
  return Qnil;
70
105
  }
71
106
 
107
+ // -- decompress --
108
+
109
+ typedef struct
110
+ {
111
+ FILE* source_file;
112
+ size_t source_buffer_length;
113
+ FILE* destination_file;
114
+ size_t destination_buffer_length;
115
+ lzws_decompressor_options_t* decompressor_options_ptr;
116
+ lzws_result_t result;
117
+ } decompress_args_t;
118
+
119
+ static inline void* decompress_wrapper(void* data)
120
+ {
121
+ decompress_args_t* args = data;
122
+
123
+ args->result = lzws_decompress_file(
124
+ args->source_file,
125
+ args->source_buffer_length,
126
+ args->destination_file,
127
+ args->destination_buffer_length,
128
+ args->decompressor_options_ptr);
129
+
130
+ return NULL;
131
+ }
132
+
72
133
  VALUE lzws_ext_decompress_io(VALUE LZWS_EXT_UNUSED(self), VALUE source, VALUE destination, VALUE options)
73
134
  {
74
135
  GET_FILE(source);
75
136
  GET_FILE(destination);
76
137
  Check_Type(options, T_HASH);
138
+ LZWS_EXT_GET_SIZE_OPTION(options, source_buffer_length);
139
+ LZWS_EXT_GET_SIZE_OPTION(options, destination_buffer_length);
140
+ LZWS_EXT_GET_BOOL_OPTION(options, gvl);
77
141
  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
142
 
81
- lzws_result_t result = lzws_decompress_file(
82
- source_file, source_buffer_length, destination_file, destination_buffer_length, &decompressor_options);
143
+ decompress_args_t args = {
144
+ .source_file = source_file,
145
+ .source_buffer_length = source_buffer_length,
146
+ .destination_file = destination_file,
147
+ .destination_buffer_length = destination_buffer_length,
148
+ .decompressor_options_ptr = &decompressor_options};
83
149
 
84
- if (result != 0) {
85
- lzws_ext_raise_error(get_file_error(result));
150
+ LZWS_EXT_GVL_WRAP(gvl, decompress_wrapper, &args);
151
+ if (args.result != 0) {
152
+ lzws_ext_raise_error(get_file_error(args.result));
86
153
  }
87
154
 
88
155
  // Ruby itself won't flush stdio file before closing fd, flush is required.
@@ -91,6 +158,8 @@ VALUE lzws_ext_decompress_io(VALUE LZWS_EXT_UNUSED(self), VALUE source, VALUE de
91
158
  return Qnil;
92
159
  }
93
160
 
161
+ // -- exports --
162
+
94
163
  void lzws_ext_io_exports(VALUE root_module)
95
164
  {
96
165
  rb_define_module_function(root_module, "_native_compress_io", RUBY_METHOD_FUNC(lzws_ext_compress_io), 3);
@@ -8,47 +8,65 @@
8
8
 
9
9
  // -- values --
10
10
 
11
- static inline VALUE get_raw_option_value(VALUE options, const char* name)
11
+ static inline VALUE get_raw_value(VALUE options, const char* name)
12
12
  {
13
13
  return rb_funcall(options, rb_intern("[]"), 1, ID2SYM(rb_intern(name)));
14
14
  }
15
15
 
16
- void lzws_ext_get_bool_option(VALUE options, bool* option, const char* name)
16
+ static inline bool get_bool_value(VALUE raw_value)
17
17
  {
18
- VALUE raw_value = get_raw_option_value(options, name);
19
- if (raw_value == Qnil) {
20
- return;
21
- }
22
-
23
18
  int raw_type = TYPE(raw_value);
24
19
  if (raw_type != T_TRUE && raw_type != T_FALSE) {
25
20
  lzws_ext_raise_error(LZWS_EXT_ERROR_VALIDATE_FAILED);
26
21
  }
27
22
 
28
- *option = raw_type == T_TRUE;
23
+ return raw_type == T_TRUE;
29
24
  }
30
25
 
31
- void lzws_ext_get_max_code_bit_length_option(VALUE options, lzws_byte_fast_t* option, const char* name)
26
+ static inline unsigned int get_uint_value(VALUE raw_value)
32
27
  {
33
- VALUE raw_value = get_raw_option_value(options, name);
34
- if (raw_value == Qnil) {
35
- return;
36
- }
37
-
38
28
  Check_Type(raw_value, T_FIXNUM);
39
29
 
40
- *option = NUM2UINT(raw_value);
30
+ return NUM2UINT(raw_value);
41
31
  }
42
32
 
43
- size_t lzws_ext_get_size_option_value(VALUE options, const char* name)
33
+ static inline size_t get_size_value(VALUE raw_value)
44
34
  {
45
- VALUE raw_value = get_raw_option_value(options, name);
46
-
47
35
  Check_Type(raw_value, T_FIXNUM);
48
36
 
49
37
  return NUM2SIZET(raw_value);
50
38
  }
51
39
 
40
+ void lzws_ext_resolve_bool_option(VALUE options, bool* option, const char* name)
41
+ {
42
+ VALUE raw_value = get_raw_value(options, name);
43
+ if (raw_value != Qnil) {
44
+ *option = get_bool_value(raw_value);
45
+ }
46
+ }
47
+
48
+ void lzws_ext_resolve_max_code_bit_length_option(VALUE options, lzws_byte_fast_t* option, const char* name)
49
+ {
50
+ VALUE raw_value = get_raw_value(options, name);
51
+ if (raw_value != Qnil) {
52
+ *option = get_uint_value(raw_value);
53
+ }
54
+ }
55
+
56
+ bool lzws_ext_get_bool_option_value(VALUE options, const char* name)
57
+ {
58
+ VALUE raw_value = get_raw_value(options, name);
59
+
60
+ return get_bool_value(raw_value);
61
+ }
62
+
63
+ size_t lzws_ext_get_size_option_value(VALUE options, const char* name)
64
+ {
65
+ VALUE raw_value = get_raw_value(options, name);
66
+
67
+ return get_size_value(raw_value);
68
+ }
69
+
52
70
  // -- exports --
53
71
 
54
72
  void lzws_ext_option_exports(VALUE root_module)
@@ -11,36 +11,38 @@
11
11
 
12
12
  #include "ruby.h"
13
13
 
14
- void lzws_ext_get_bool_option(VALUE options, bool* option, const char* name);
15
- void lzws_ext_get_max_code_bit_length_option(VALUE options, lzws_byte_fast_t* option, const char* name);
14
+ void lzws_ext_resolve_bool_option(VALUE options, bool* option, const char* name);
15
+ void lzws_ext_resolve_max_code_bit_length_option(VALUE options, lzws_byte_fast_t* option, const char* name);
16
16
 
17
- #define LZWS_EXT_GET_BOOL_OPTION(options, target_options, name) \
18
- lzws_ext_get_bool_option(options, &target_options.name, #name);
17
+ #define LZWS_EXT_RESOLVE_BOOL_OPTION(options, target_options, name) \
18
+ lzws_ext_resolve_bool_option(options, &target_options.name, #name);
19
19
 
20
- #define LZWS_EXT_GET_MAX_CODE_BIT_LENGTH_OPTION(options, target_options, name) \
21
- lzws_ext_get_max_code_bit_length_option(options, &target_options.name, #name);
20
+ #define LZWS_EXT_RESOLVE_MAX_CODE_BIT_LENGTH_OPTION(options, target_options, name) \
21
+ lzws_ext_resolve_max_code_bit_length_option(options, &target_options.name, #name);
22
22
 
23
- #define LZWS_EXT_GET_COMPRESSOR_OPTIONS(options) \
24
- lzws_compressor_options_t compressor_options = LZWS_COMPRESSOR_DEFAULT_OPTIONS; \
25
- \
26
- LZWS_EXT_GET_BOOL_OPTION(options, compressor_options, without_magic_header); \
27
- LZWS_EXT_GET_MAX_CODE_BIT_LENGTH_OPTION(options, compressor_options, max_code_bit_length); \
28
- LZWS_EXT_GET_BOOL_OPTION(options, compressor_options, block_mode); \
29
- LZWS_EXT_GET_BOOL_OPTION(options, compressor_options, msb); \
30
- LZWS_EXT_GET_BOOL_OPTION(options, compressor_options, unaligned_bit_groups); \
31
- LZWS_EXT_GET_BOOL_OPTION(options, compressor_options, quiet);
23
+ #define LZWS_EXT_GET_COMPRESSOR_OPTIONS(options) \
24
+ lzws_compressor_options_t compressor_options = LZWS_COMPRESSOR_DEFAULT_OPTIONS; \
25
+ \
26
+ LZWS_EXT_RESOLVE_BOOL_OPTION(options, compressor_options, without_magic_header); \
27
+ LZWS_EXT_RESOLVE_MAX_CODE_BIT_LENGTH_OPTION(options, compressor_options, max_code_bit_length); \
28
+ LZWS_EXT_RESOLVE_BOOL_OPTION(options, compressor_options, block_mode); \
29
+ LZWS_EXT_RESOLVE_BOOL_OPTION(options, compressor_options, msb); \
30
+ LZWS_EXT_RESOLVE_BOOL_OPTION(options, compressor_options, unaligned_bit_groups); \
31
+ LZWS_EXT_RESOLVE_BOOL_OPTION(options, compressor_options, quiet);
32
32
 
33
33
  #define LZWS_EXT_GET_DECOMPRESSOR_OPTIONS(options) \
34
34
  lzws_decompressor_options_t decompressor_options = LZWS_DECOMPRESSOR_DEFAULT_OPTIONS; \
35
35
  \
36
- LZWS_EXT_GET_BOOL_OPTION(options, decompressor_options, without_magic_header); \
37
- LZWS_EXT_GET_BOOL_OPTION(options, decompressor_options, msb); \
38
- LZWS_EXT_GET_BOOL_OPTION(options, decompressor_options, unaligned_bit_groups); \
39
- LZWS_EXT_GET_BOOL_OPTION(options, decompressor_options, quiet);
36
+ LZWS_EXT_RESOLVE_BOOL_OPTION(options, decompressor_options, without_magic_header); \
37
+ LZWS_EXT_RESOLVE_BOOL_OPTION(options, decompressor_options, msb); \
38
+ LZWS_EXT_RESOLVE_BOOL_OPTION(options, decompressor_options, unaligned_bit_groups); \
39
+ LZWS_EXT_RESOLVE_BOOL_OPTION(options, decompressor_options, quiet);
40
40
 
41
+ bool lzws_ext_get_bool_option_value(VALUE options, const char* name);
41
42
  size_t lzws_ext_get_size_option_value(VALUE options, const char* name);
42
43
 
43
- #define LZWS_EXT_GET_BUFFER_LENGTH_OPTION(options, name) size_t name = lzws_ext_get_size_option_value(options, #name);
44
+ #define LZWS_EXT_GET_BOOL_OPTION(options, name) bool name = lzws_ext_get_bool_option_value(options, #name);
45
+ #define LZWS_EXT_GET_SIZE_OPTION(options, name) size_t name = lzws_ext_get_size_option_value(options, #name);
44
46
 
45
47
  void lzws_ext_option_exports(VALUE root_module);
46
48
 
@@ -8,9 +8,12 @@
8
8
  #include <lzws/compressor/state.h>
9
9
 
10
10
  #include "lzws_ext/error.h"
11
+ #include "lzws_ext/gvl.h"
11
12
  #include "lzws_ext/option.h"
12
13
  #include "ruby.h"
13
14
 
15
+ // -- initialization --
16
+
14
17
  static void free_compressor(lzws_ext_compressor_t* compressor_ptr)
15
18
  {
16
19
  lzws_compressor_state_t* state_ptr = compressor_ptr->state_ptr;
@@ -36,6 +39,7 @@ VALUE lzws_ext_allocate_compressor(VALUE klass)
36
39
  compressor_ptr->destination_buffer_length = 0;
37
40
  compressor_ptr->remaining_destination_buffer = NULL;
38
41
  compressor_ptr->remaining_destination_buffer_length = 0;
42
+ compressor_ptr->gvl = false;
39
43
 
40
44
  return self;
41
45
  }
@@ -48,8 +52,9 @@ VALUE lzws_ext_initialize_compressor(VALUE self, VALUE options)
48
52
  {
49
53
  GET_COMPRESSOR(self);
50
54
  Check_Type(options, T_HASH);
55
+ LZWS_EXT_GET_SIZE_OPTION(options, destination_buffer_length);
56
+ LZWS_EXT_GET_BOOL_OPTION(options, gvl);
51
57
  LZWS_EXT_GET_COMPRESSOR_OPTIONS(options);
52
- LZWS_EXT_GET_BUFFER_LENGTH_OPTION(options, destination_buffer_length);
53
58
 
54
59
  lzws_compressor_state_t* state_ptr;
55
60
 
@@ -80,63 +85,106 @@ VALUE lzws_ext_initialize_compressor(VALUE self, VALUE options)
80
85
  compressor_ptr->destination_buffer_length = destination_buffer_length;
81
86
  compressor_ptr->remaining_destination_buffer = destination_buffer;
82
87
  compressor_ptr->remaining_destination_buffer_length = destination_buffer_length;
88
+ compressor_ptr->gvl = gvl;
83
89
 
84
90
  return Qnil;
85
91
  }
86
92
 
93
+ // -- compress --
94
+
87
95
  #define DO_NOT_USE_AFTER_CLOSE(compressor_ptr) \
88
96
  if (compressor_ptr->state_ptr == NULL || compressor_ptr->destination_buffer == NULL) { \
89
97
  lzws_ext_raise_error(LZWS_EXT_ERROR_USED_AFTER_CLOSE); \
90
98
  }
91
99
 
92
- #define GET_SOURCE_DATA(source_value) \
93
- Check_Type(source_value, T_STRING); \
94
- \
95
- const char* source = RSTRING_PTR(source_value); \
96
- size_t source_length = RSTRING_LEN(source_value); \
97
- lzws_ext_byte_t* remaining_source = (lzws_ext_byte_t*) source; \
98
- size_t remaining_source_length = source_length;
100
+ typedef struct
101
+ {
102
+ lzws_ext_compressor_t* compressor_ptr;
103
+ lzws_ext_byte_t* remaining_source;
104
+ size_t* remaining_source_length_ptr;
105
+ lzws_result_t result;
106
+ } compress_args_t;
99
107
 
100
- VALUE lzws_ext_compress(VALUE self, VALUE source_value)
108
+ static inline void* compress_wrapper(void* data)
101
109
  {
102
- GET_COMPRESSOR(self);
103
- DO_NOT_USE_AFTER_CLOSE(compressor_ptr);
104
- GET_SOURCE_DATA(source_value);
110
+ compress_args_t* args = data;
111
+ lzws_ext_compressor_t* compressor_ptr = args->compressor_ptr;
105
112
 
106
- lzws_result_t result = lzws_compress(
113
+ args->result = lzws_compress(
107
114
  compressor_ptr->state_ptr,
108
- &remaining_source,
109
- &remaining_source_length,
115
+ &args->remaining_source,
116
+ args->remaining_source_length_ptr,
110
117
  &compressor_ptr->remaining_destination_buffer,
111
118
  &compressor_ptr->remaining_destination_buffer_length);
112
119
 
113
- if (result != 0 && result != LZWS_COMPRESSOR_NEEDS_MORE_DESTINATION) {
120
+ return NULL;
121
+ }
122
+
123
+ VALUE lzws_ext_compress(VALUE self, VALUE source_value)
124
+ {
125
+ GET_COMPRESSOR(self);
126
+ DO_NOT_USE_AFTER_CLOSE(compressor_ptr);
127
+ Check_Type(source_value, T_STRING);
128
+
129
+ const char* source = RSTRING_PTR(source_value);
130
+ size_t source_length = RSTRING_LEN(source_value);
131
+ lzws_ext_byte_t* remaining_source = (lzws_ext_byte_t*) source;
132
+ size_t remaining_source_length = source_length;
133
+
134
+ compress_args_t args = {
135
+ .compressor_ptr = compressor_ptr,
136
+ .remaining_source = remaining_source,
137
+ .remaining_source_length_ptr = &remaining_source_length};
138
+
139
+ LZWS_EXT_GVL_WRAP(compressor_ptr->gvl, compress_wrapper, &args);
140
+ if (args.result != 0 && args.result != LZWS_COMPRESSOR_NEEDS_MORE_DESTINATION) {
114
141
  lzws_ext_raise_error(LZWS_EXT_ERROR_UNEXPECTED);
115
142
  }
116
143
 
117
144
  VALUE bytes_written = SIZET2NUM(source_length - remaining_source_length);
118
- VALUE needs_more_destination = result == LZWS_COMPRESSOR_NEEDS_MORE_DESTINATION ? Qtrue : Qfalse;
145
+ VALUE needs_more_destination = args.result == LZWS_COMPRESSOR_NEEDS_MORE_DESTINATION ? Qtrue : Qfalse;
119
146
 
120
147
  return rb_ary_new_from_args(2, bytes_written, needs_more_destination);
121
148
  }
122
149
 
123
- VALUE lzws_ext_compressor_finish(VALUE self)
150
+ // -- compressor finish --
151
+
152
+ typedef struct
124
153
  {
125
- GET_COMPRESSOR(self);
126
- DO_NOT_USE_AFTER_CLOSE(compressor_ptr);
154
+ lzws_ext_compressor_t* compressor_ptr;
155
+ lzws_result_t result;
156
+ } compressor_finish_args_t;
127
157
 
128
- lzws_result_t result = lzws_compressor_finish(
158
+ static inline void* compressor_finish_wrapper(void* data)
159
+ {
160
+ compressor_finish_args_t* args = data;
161
+ lzws_ext_compressor_t* compressor_ptr = args->compressor_ptr;
162
+
163
+ args->result = lzws_compressor_finish(
129
164
  compressor_ptr->state_ptr,
130
165
  &compressor_ptr->remaining_destination_buffer,
131
166
  &compressor_ptr->remaining_destination_buffer_length);
132
167
 
133
- if (result != 0 && result != LZWS_COMPRESSOR_NEEDS_MORE_DESTINATION) {
168
+ return NULL;
169
+ }
170
+
171
+ VALUE lzws_ext_compressor_finish(VALUE self)
172
+ {
173
+ GET_COMPRESSOR(self);
174
+ DO_NOT_USE_AFTER_CLOSE(compressor_ptr);
175
+
176
+ compressor_finish_args_t args = {.compressor_ptr = compressor_ptr};
177
+
178
+ LZWS_EXT_GVL_WRAP(compressor_ptr->gvl, compressor_finish_wrapper, &args);
179
+ if (args.result != 0 && args.result != LZWS_COMPRESSOR_NEEDS_MORE_DESTINATION) {
134
180
  lzws_ext_raise_error(LZWS_EXT_ERROR_UNEXPECTED);
135
181
  }
136
182
 
137
- return result == LZWS_COMPRESSOR_NEEDS_MORE_DESTINATION ? Qtrue : Qfalse;
183
+ return args.result == LZWS_COMPRESSOR_NEEDS_MORE_DESTINATION ? Qtrue : Qfalse;
138
184
  }
139
185
 
186
+ // -- other --
187
+
140
188
  VALUE lzws_ext_compressor_read_result(VALUE self)
141
189
  {
142
190
  GET_COMPRESSOR(self);
@@ -156,6 +204,8 @@ VALUE lzws_ext_compressor_read_result(VALUE self)
156
204
  return result_value;
157
205
  }
158
206
 
207
+ // -- cleanup --
208
+
159
209
  VALUE lzws_ext_compressor_close(VALUE self)
160
210
  {
161
211
  GET_COMPRESSOR(self);
@@ -175,12 +225,14 @@ VALUE lzws_ext_compressor_close(VALUE self)
175
225
  compressor_ptr->destination_buffer = NULL;
176
226
  }
177
227
 
178
- // It is possible to keep "destination_buffer_length", "remaining_destination_buffer"
179
- // and "remaining_destination_buffer_length" as is.
228
+ // It is possible to keep "destination_buffer_length", "remaining_destination_buffer",
229
+ // "remaining_destination_buffer_length" and "gvl" as is.
180
230
 
181
231
  return Qnil;
182
232
  }
183
233
 
234
+ // -- exports --
235
+
184
236
  void lzws_ext_compressor_exports(VALUE root_module)
185
237
  {
186
238
  VALUE module = rb_define_module_under(root_module, "Stream");
@@ -5,6 +5,7 @@
5
5
  #define LZWS_EXT_STREAM_COMPRESSOR_H
6
6
 
7
7
  #include <lzws/compressor/state.h>
8
+ #include <stdbool.h>
8
9
  #include <stdlib.h>
9
10
 
10
11
  #include "lzws_ext/common.h"
@@ -17,6 +18,7 @@ typedef struct
17
18
  size_t destination_buffer_length;
18
19
  lzws_ext_byte_t* remaining_destination_buffer;
19
20
  size_t remaining_destination_buffer_length;
21
+ bool gvl;
20
22
  } lzws_ext_compressor_t;
21
23
 
22
24
  VALUE lzws_ext_allocate_compressor(VALUE klass);
@@ -8,9 +8,12 @@
8
8
  #include <lzws/decompressor/state.h>
9
9
 
10
10
  #include "lzws_ext/error.h"
11
+ #include "lzws_ext/gvl.h"
11
12
  #include "lzws_ext/option.h"
12
13
  #include "ruby.h"
13
14
 
15
+ // -- initialization --
16
+
14
17
  static void free_decompressor(lzws_ext_decompressor_t* decompressor_ptr)
15
18
  {
16
19
  lzws_decompressor_state_t* state_ptr = decompressor_ptr->state_ptr;
@@ -48,8 +51,9 @@ VALUE lzws_ext_initialize_decompressor(VALUE self, VALUE options)
48
51
  {
49
52
  GET_DECOMPRESSOR(self);
50
53
  Check_Type(options, T_HASH);
54
+ LZWS_EXT_GET_SIZE_OPTION(options, destination_buffer_length);
55
+ LZWS_EXT_GET_BOOL_OPTION(options, gvl);
51
56
  LZWS_EXT_GET_DECOMPRESSOR_OPTIONS(options);
52
- LZWS_EXT_GET_BUFFER_LENGTH_OPTION(options, destination_buffer_length);
53
57
 
54
58
  lzws_decompressor_state_t* state_ptr;
55
59
 
@@ -78,38 +82,60 @@ VALUE lzws_ext_initialize_decompressor(VALUE self, VALUE options)
78
82
  decompressor_ptr->destination_buffer_length = destination_buffer_length;
79
83
  decompressor_ptr->remaining_destination_buffer = destination_buffer;
80
84
  decompressor_ptr->remaining_destination_buffer_length = destination_buffer_length;
85
+ decompressor_ptr->gvl = gvl;
81
86
 
82
87
  return Qnil;
83
88
  }
84
89
 
90
+ // -- decompress --
91
+
85
92
  #define DO_NOT_USE_AFTER_CLOSE(decompressor_ptr) \
86
93
  if (decompressor_ptr->state_ptr == NULL || decompressor_ptr->destination_buffer == NULL) { \
87
94
  lzws_ext_raise_error(LZWS_EXT_ERROR_USED_AFTER_CLOSE); \
88
95
  }
89
96
 
90
- #define GET_SOURCE_DATA(source_value) \
91
- Check_Type(source_value, T_STRING); \
92
- \
93
- const char* source = RSTRING_PTR(source_value); \
94
- size_t source_length = RSTRING_LEN(source_value); \
95
- lzws_ext_byte_t* remaining_source = (lzws_ext_byte_t*) source; \
96
- size_t remaining_source_length = source_length;
97
+ typedef struct
98
+ {
99
+ lzws_ext_decompressor_t* decompressor_ptr;
100
+ lzws_ext_byte_t* remaining_source;
101
+ size_t* remaining_source_length_ptr;
102
+ lzws_result_t result;
103
+ } decompress_args_t;
97
104
 
98
- VALUE lzws_ext_decompress(VALUE self, VALUE source_value)
105
+ static inline void* decompress_wrapper(void* data)
99
106
  {
100
- GET_DECOMPRESSOR(self);
101
- DO_NOT_USE_AFTER_CLOSE(decompressor_ptr);
102
- GET_SOURCE_DATA(source_value);
107
+ decompress_args_t* args = data;
108
+ lzws_ext_decompressor_t* decompressor_ptr = args->decompressor_ptr;
103
109
 
104
- lzws_result_t result = lzws_decompress(
110
+ args->result = lzws_decompress(
105
111
  decompressor_ptr->state_ptr,
106
- &remaining_source,
107
- &remaining_source_length,
112
+ &args->remaining_source,
113
+ args->remaining_source_length_ptr,
108
114
  &decompressor_ptr->remaining_destination_buffer,
109
115
  &decompressor_ptr->remaining_destination_buffer_length);
110
116
 
111
- if (result != 0 && result != LZWS_DECOMPRESSOR_NEEDS_MORE_DESTINATION) {
112
- switch (result) {
117
+ return NULL;
118
+ }
119
+
120
+ VALUE lzws_ext_decompress(VALUE self, VALUE source_value)
121
+ {
122
+ GET_DECOMPRESSOR(self);
123
+ DO_NOT_USE_AFTER_CLOSE(decompressor_ptr);
124
+ Check_Type(source_value, T_STRING);
125
+
126
+ const char* source = RSTRING_PTR(source_value);
127
+ size_t source_length = RSTRING_LEN(source_value);
128
+ lzws_ext_byte_t* remaining_source = (lzws_ext_byte_t*) source;
129
+ size_t remaining_source_length = source_length;
130
+
131
+ decompress_args_t args = {
132
+ .decompressor_ptr = decompressor_ptr,
133
+ .remaining_source = remaining_source,
134
+ .remaining_source_length_ptr = &remaining_source_length};
135
+
136
+ LZWS_EXT_GVL_WRAP(decompressor_ptr->gvl, decompress_wrapper, &args);
137
+ if (args.result != 0 && args.result != LZWS_DECOMPRESSOR_NEEDS_MORE_DESTINATION) {
138
+ switch (args.result) {
113
139
  case LZWS_DECOMPRESSOR_INVALID_MAGIC_HEADER:
114
140
  case LZWS_DECOMPRESSOR_INVALID_MAX_CODE_BIT_LENGTH:
115
141
  lzws_ext_raise_error(LZWS_EXT_ERROR_VALIDATE_FAILED);
@@ -121,11 +147,13 @@ VALUE lzws_ext_decompress(VALUE self, VALUE source_value)
121
147
  }
122
148
 
123
149
  VALUE bytes_read = SIZET2NUM(source_length - remaining_source_length);
124
- VALUE needs_more_destination = result == LZWS_DECOMPRESSOR_NEEDS_MORE_DESTINATION ? Qtrue : Qfalse;
150
+ VALUE needs_more_destination = args.result == LZWS_DECOMPRESSOR_NEEDS_MORE_DESTINATION ? Qtrue : Qfalse;
125
151
 
126
152
  return rb_ary_new_from_args(2, bytes_read, needs_more_destination);
127
153
  }
128
154
 
155
+ // -- other --
156
+
129
157
  VALUE lzws_ext_decompressor_read_result(VALUE self)
130
158
  {
131
159
  GET_DECOMPRESSOR(self);
@@ -145,6 +173,8 @@ VALUE lzws_ext_decompressor_read_result(VALUE self)
145
173
  return result_value;
146
174
  }
147
175
 
176
+ // -- cleanup --
177
+
148
178
  VALUE lzws_ext_decompressor_close(VALUE self)
149
179
  {
150
180
  GET_DECOMPRESSOR(self);
@@ -170,6 +200,8 @@ VALUE lzws_ext_decompressor_close(VALUE self)
170
200
  return Qnil;
171
201
  }
172
202
 
203
+ // -- exports --
204
+
173
205
  void lzws_ext_decompressor_exports(VALUE root_module)
174
206
  {
175
207
  VALUE module = rb_define_module_under(root_module, "Stream");
@@ -5,6 +5,7 @@
5
5
  #define LZWS_EXT_STREAM_DECOMPRESSOR_H
6
6
 
7
7
  #include <lzws/decompressor/state.h>
8
+ #include <stdbool.h>
8
9
  #include <stdlib.h>
9
10
 
10
11
  #include "lzws_ext/common.h"
@@ -17,6 +18,7 @@ typedef struct
17
18
  size_t destination_buffer_length;
18
19
  lzws_ext_byte_t* remaining_destination_buffer;
19
20
  size_t remaining_destination_buffer_length;
21
+ bool gvl;
20
22
  } lzws_ext_decompressor_t;
21
23
 
22
24
  VALUE lzws_ext_allocate_decompressor(VALUE klass);
@@ -12,6 +12,7 @@
12
12
 
13
13
  #include "lzws_ext/buffer.h"
14
14
  #include "lzws_ext/error.h"
15
+ #include "lzws_ext/gvl.h"
15
16
  #include "lzws_ext/macro.h"
16
17
  #include "lzws_ext/option.h"
17
18
  #include "ruby.h"
@@ -41,30 +42,67 @@ static inline lzws_ext_result_t increase_destination_buffer(
41
42
  return 0;
42
43
  }
43
44
 
44
- // -- utils --
45
+ // -- compress --
45
46
 
46
- #define GET_SOURCE_DATA(source_value) \
47
- Check_Type(source_value, T_STRING); \
48
- \
49
- const char* source = RSTRING_PTR(source_value); \
50
- size_t source_length = RSTRING_LEN(source_value);
47
+ typedef struct
48
+ {
49
+ lzws_compressor_state_t* state_ptr;
50
+ lzws_ext_byte_t** remaining_source_ptr;
51
+ size_t* remaining_source_length_ptr;
52
+ lzws_ext_byte_t* remaining_destination_buffer;
53
+ size_t* remaining_destination_buffer_length_ptr;
54
+ lzws_result_t result;
55
+ } compress_args_t;
56
+
57
+ typedef struct
58
+ {
59
+ lzws_compressor_state_t* state_ptr;
60
+ lzws_ext_byte_t* remaining_destination_buffer;
61
+ size_t* remaining_destination_buffer_length_ptr;
62
+ lzws_result_t result;
63
+ } compressor_finish_args_t;
51
64
 
52
- // -- compress --
65
+ static inline void* compress_wrapper(void* data)
66
+ {
67
+ compress_args_t* args = data;
68
+
69
+ args->result = lzws_compress(
70
+ args->state_ptr,
71
+ args->remaining_source_ptr,
72
+ args->remaining_source_length_ptr,
73
+ &args->remaining_destination_buffer,
74
+ args->remaining_destination_buffer_length_ptr);
75
+
76
+ return NULL;
77
+ }
78
+
79
+ static inline void* compressor_finish_wrapper(void* data)
80
+ {
81
+ compressor_finish_args_t* args = data;
82
+
83
+ args->result = lzws_compressor_finish(
84
+ args->state_ptr, &args->remaining_destination_buffer, args->remaining_destination_buffer_length_ptr);
85
+
86
+ return NULL;
87
+ }
53
88
 
54
- #define BUFFERED_COMPRESS(function, ...) \
89
+ #define BUFFERED_COMPRESS(gvl, wrapper, args) \
55
90
  while (true) { \
56
91
  lzws_ext_byte_t* remaining_destination_buffer = \
57
92
  (lzws_ext_byte_t*) RSTRING_PTR(destination_value) + destination_length; \
58
93
  size_t prev_remaining_destination_buffer_length = remaining_destination_buffer_length; \
59
94
  \
60
- result = function(__VA_ARGS__, &remaining_destination_buffer, &remaining_destination_buffer_length); \
61
- if (result != 0 && result != LZWS_COMPRESSOR_NEEDS_MORE_DESTINATION) { \
95
+ args.remaining_destination_buffer = remaining_destination_buffer; \
96
+ args.remaining_destination_buffer_length_ptr = &remaining_destination_buffer_length; \
97
+ \
98
+ LZWS_EXT_GVL_WRAP(gvl, wrapper, &args); \
99
+ if (args.result != 0 && args.result != LZWS_COMPRESSOR_NEEDS_MORE_DESTINATION) { \
62
100
  return LZWS_EXT_ERROR_UNEXPECTED; \
63
101
  } \
64
102
  \
65
103
  destination_length += prev_remaining_destination_buffer_length - remaining_destination_buffer_length; \
66
104
  \
67
- if (result == LZWS_COMPRESSOR_NEEDS_MORE_DESTINATION) { \
105
+ if (args.result == LZWS_COMPRESSOR_NEEDS_MORE_DESTINATION) { \
68
106
  ext_result = increase_destination_buffer( \
69
107
  destination_value, destination_length, &remaining_destination_buffer_length, destination_buffer_length); \
70
108
  \
@@ -83,17 +121,24 @@ static inline lzws_ext_result_t compress(
83
121
  const char* source,
84
122
  size_t source_length,
85
123
  VALUE destination_value,
86
- size_t destination_buffer_length)
124
+ size_t destination_buffer_length,
125
+ bool gvl)
87
126
  {
88
- lzws_result_t result;
89
127
  lzws_ext_result_t ext_result;
90
128
  lzws_ext_byte_t* remaining_source = (lzws_ext_byte_t*) source;
91
129
  size_t remaining_source_length = source_length;
92
130
  size_t destination_length = 0;
93
131
  size_t remaining_destination_buffer_length = destination_buffer_length;
94
132
 
95
- BUFFERED_COMPRESS(lzws_compress, state_ptr, &remaining_source, &remaining_source_length);
96
- BUFFERED_COMPRESS(lzws_compressor_finish, state_ptr);
133
+ compress_args_t args = {
134
+ .state_ptr = state_ptr,
135
+ .remaining_source_ptr = &remaining_source,
136
+ .remaining_source_length_ptr = &remaining_source_length};
137
+
138
+ BUFFERED_COMPRESS(gvl, compress_wrapper, args);
139
+
140
+ compressor_finish_args_t finish_args = {.state_ptr = state_ptr};
141
+ BUFFERED_COMPRESS(gvl, compressor_finish_wrapper, finish_args);
97
142
 
98
143
  int exception;
99
144
 
@@ -107,10 +152,11 @@ static inline lzws_ext_result_t compress(
107
152
 
108
153
  VALUE lzws_ext_compress_string(VALUE LZWS_EXT_UNUSED(self), VALUE source_value, VALUE options)
109
154
  {
110
- GET_SOURCE_DATA(source_value);
155
+ Check_Type(source_value, T_STRING);
111
156
  Check_Type(options, T_HASH);
157
+ LZWS_EXT_GET_SIZE_OPTION(options, destination_buffer_length);
158
+ LZWS_EXT_GET_BOOL_OPTION(options, gvl);
112
159
  LZWS_EXT_GET_COMPRESSOR_OPTIONS(options);
113
- LZWS_EXT_GET_BUFFER_LENGTH_OPTION(options, destination_buffer_length);
114
160
 
115
161
  lzws_compressor_state_t* state_ptr;
116
162
 
@@ -138,8 +184,11 @@ VALUE lzws_ext_compress_string(VALUE LZWS_EXT_UNUSED(self), VALUE source_value,
138
184
  lzws_ext_raise_error(LZWS_EXT_ERROR_ALLOCATE_FAILED);
139
185
  }
140
186
 
187
+ const char* source = RSTRING_PTR(source_value);
188
+ size_t source_length = RSTRING_LEN(source_value);
189
+
141
190
  lzws_ext_result_t ext_result =
142
- compress(state_ptr, source, source_length, destination_value, destination_buffer_length);
191
+ compress(state_ptr, source, source_length, destination_value, destination_buffer_length, gvl);
143
192
 
144
193
  lzws_compressor_free_state(state_ptr);
145
194
 
@@ -152,34 +201,60 @@ VALUE lzws_ext_compress_string(VALUE LZWS_EXT_UNUSED(self), VALUE source_value,
152
201
 
153
202
  // -- decompress --
154
203
 
204
+ typedef struct
205
+ {
206
+ lzws_decompressor_state_t* state_ptr;
207
+ lzws_ext_byte_t** remaining_source_ptr;
208
+ size_t* remaining_source_length_ptr;
209
+ lzws_ext_byte_t* remaining_destination_buffer;
210
+ size_t* remaining_destination_buffer_length_ptr;
211
+ lzws_result_t result;
212
+ } decompress_args_t;
213
+
214
+ static inline void* decompress_wrapper(void* data)
215
+ {
216
+ decompress_args_t* args = data;
217
+
218
+ args->result = lzws_decompress(
219
+ args->state_ptr,
220
+ args->remaining_source_ptr,
221
+ args->remaining_source_length_ptr,
222
+ &args->remaining_destination_buffer,
223
+ args->remaining_destination_buffer_length_ptr);
224
+
225
+ return NULL;
226
+ }
227
+
155
228
  static inline lzws_ext_result_t decompress(
156
229
  lzws_decompressor_state_t* state_ptr,
157
230
  const char* source,
158
231
  size_t source_length,
159
232
  VALUE destination_value,
160
- size_t destination_buffer_length)
233
+ size_t destination_buffer_length,
234
+ bool gvl)
161
235
  {
162
- lzws_result_t result;
163
236
  lzws_ext_result_t ext_result;
164
237
  lzws_ext_byte_t* remaining_source = (lzws_ext_byte_t*) source;
165
238
  size_t remaining_source_length = source_length;
166
239
  size_t destination_length = 0;
167
240
  size_t remaining_destination_buffer_length = destination_buffer_length;
168
241
 
242
+ decompress_args_t args = {
243
+ .state_ptr = state_ptr,
244
+ .remaining_source_ptr = &remaining_source,
245
+ .remaining_source_length_ptr = &remaining_source_length};
246
+
169
247
  while (true) {
170
248
  lzws_ext_byte_t* remaining_destination_buffer =
171
249
  (lzws_ext_byte_t*) RSTRING_PTR(destination_value) + destination_length;
172
250
  size_t prev_remaining_destination_buffer_length = remaining_destination_buffer_length;
173
251
 
174
- result = lzws_decompress(
175
- state_ptr,
176
- &remaining_source,
177
- &remaining_source_length,
178
- &remaining_destination_buffer,
179
- &remaining_destination_buffer_length);
252
+ args.remaining_destination_buffer = remaining_destination_buffer;
253
+ args.remaining_destination_buffer_length_ptr = &remaining_destination_buffer_length;
180
254
 
181
- if (result != 0 && result != LZWS_DECOMPRESSOR_NEEDS_MORE_DESTINATION) {
182
- switch (result) {
255
+ LZWS_EXT_GVL_WRAP(gvl, decompress_wrapper, &args);
256
+ if (args.result != 0 && args.result != LZWS_DECOMPRESSOR_NEEDS_MORE_DESTINATION) {
257
+ switch (args.result) {
183
258
  case LZWS_DECOMPRESSOR_INVALID_MAGIC_HEADER:
184
259
  case LZWS_DECOMPRESSOR_INVALID_MAX_CODE_BIT_LENGTH:
185
260
  return LZWS_EXT_ERROR_VALIDATE_FAILED;
@@ -192,7 +267,7 @@ static inline lzws_ext_result_t decompress(
192
267
 
193
268
  destination_length += prev_remaining_destination_buffer_length - remaining_destination_buffer_length;
194
269
 
195
- if (result == LZWS_DECOMPRESSOR_NEEDS_MORE_DESTINATION) {
270
+ if (args.result == LZWS_DECOMPRESSOR_NEEDS_MORE_DESTINATION) {
196
271
  ext_result = increase_destination_buffer(
197
272
  destination_value, destination_length, &remaining_destination_buffer_length, destination_buffer_length);
198
273
 
@@ -218,10 +293,11 @@ static inline lzws_ext_result_t decompress(
218
293
 
219
294
  VALUE lzws_ext_decompress_string(VALUE LZWS_EXT_UNUSED(self), VALUE source_value, VALUE options)
220
295
  {
221
- GET_SOURCE_DATA(source_value);
296
+ Check_Type(source_value, T_STRING);
222
297
  Check_Type(options, T_HASH);
298
+ LZWS_EXT_GET_SIZE_OPTION(options, destination_buffer_length);
299
+ LZWS_EXT_GET_BOOL_OPTION(options, gvl);
223
300
  LZWS_EXT_GET_DECOMPRESSOR_OPTIONS(options);
224
- LZWS_EXT_GET_BUFFER_LENGTH_OPTION(options, destination_buffer_length);
225
301
 
226
302
  lzws_decompressor_state_t* state_ptr;
227
303
 
@@ -247,8 +323,11 @@ VALUE lzws_ext_decompress_string(VALUE LZWS_EXT_UNUSED(self), VALUE source_value
247
323
  lzws_ext_raise_error(LZWS_EXT_ERROR_ALLOCATE_FAILED);
248
324
  }
249
325
 
326
+ const char* source = RSTRING_PTR(source_value);
327
+ size_t source_length = RSTRING_LEN(source_value);
328
+
250
329
  lzws_ext_result_t ext_result =
251
- decompress(state_ptr, source, source_length, destination_value, destination_buffer_length);
330
+ decompress(state_ptr, source, source_length, destination_value, destination_buffer_length, gvl);
252
331
 
253
332
  lzws_decompressor_free_state(state_ptr);
254
333
 
@@ -259,6 +338,8 @@ VALUE lzws_ext_decompress_string(VALUE LZWS_EXT_UNUSED(self), VALUE source_value
259
338
  return destination_value;
260
339
  }
261
340
 
341
+ // -- exports --
342
+
262
343
  void lzws_ext_string_exports(VALUE root_module)
263
344
  {
264
345
  rb_define_module_function(root_module, "_native_compress_string", RUBY_METHOD_FUNC(lzws_ext_compress_string), 2);
@@ -10,14 +10,36 @@ module LZWS
10
10
  module Option
11
11
  DEFAULT_BUFFER_LENGTH = 0
12
12
 
13
+ COMPRESSOR_DEFAULTS = {
14
+ :gvl => false,
15
+ :max_code_bit_length => nil,
16
+ :without_magic_header => nil,
17
+ :block_mode => nil,
18
+ :msb => nil,
19
+ :unaligned_bit_groups => nil,
20
+ :quiet => nil
21
+ }
22
+ .freeze
23
+
24
+ DECOMPRESSOR_DEFAULTS = {
25
+ :gvl => false,
26
+ :without_magic_header => nil,
27
+ :msb => nil,
28
+ :unaligned_bit_groups => nil,
29
+ :quiet => nil
30
+ }
31
+ .freeze
32
+
13
33
  def self.get_compressor_options(options, buffer_length_names)
14
34
  Validation.validate_hash options
15
35
 
16
36
  buffer_length_defaults = buffer_length_names.each_with_object({}) { |name, defaults| defaults[name] = DEFAULT_BUFFER_LENGTH }
17
- options = buffer_length_defaults.merge options
37
+ options = COMPRESSOR_DEFAULTS.merge(buffer_length_defaults).merge options
18
38
 
19
39
  buffer_length_names.each { |name| Validation.validate_not_negative_integer options[name] }
20
40
 
41
+ Validation.validate_bool options[:gvl]
42
+
21
43
  max_code_bit_length = options[:max_code_bit_length]
22
44
  unless max_code_bit_length.nil?
23
45
  Validation.validate_positive_integer max_code_bit_length
@@ -47,10 +69,12 @@ module LZWS
47
69
  Validation.validate_hash options
48
70
 
49
71
  buffer_length_defaults = buffer_length_names.each_with_object({}) { |name, defaults| defaults[name] = DEFAULT_BUFFER_LENGTH }
50
- options = buffer_length_defaults.merge options
72
+ options = DECOMPRESSOR_DEFAULTS.merge(buffer_length_defaults).merge options
51
73
 
52
74
  buffer_length_names.each { |name| Validation.validate_not_negative_integer options[name] }
53
75
 
76
+ Validation.validate_bool options[:gvl]
77
+
54
78
  without_magic_header = options[:without_magic_header]
55
79
  Validation.validate_bool without_magic_header unless without_magic_header.nil?
56
80
 
@@ -2,5 +2,5 @@
2
2
  # Copyright (c) 2019 AUTHORS, MIT License.
3
3
 
4
4
  module LZWS
5
- VERSION = "1.2.0".freeze
5
+ VERSION = "1.3.0".freeze
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-lzws
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Aladjev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-10-14 00:00:00.000000000 Z
11
+ date: 2020-10-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: codecov
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '1.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: parallel
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: rake
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -152,6 +166,7 @@ files:
152
166
  - ext/lzws_ext/common.h
153
167
  - ext/lzws_ext/error.c
154
168
  - ext/lzws_ext/error.h
169
+ - ext/lzws_ext/gvl.h
155
170
  - ext/lzws_ext/io.c
156
171
  - ext/lzws_ext/io.h
157
172
  - ext/lzws_ext/macro.h
@@ -203,5 +218,5 @@ requirements: []
203
218
  rubygems_version: 3.1.4
204
219
  signing_key:
205
220
  specification_version: 4
206
- summary: Ruby bindings for lzws library.
221
+ summary: Ruby bindings for lzws library (compatible with UNIX compress).
207
222
  test_files: []