extlzma2 2.0.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.
@@ -0,0 +1,440 @@
1
+ #include "extlzma2.h"
2
+
3
+ VALUE extlzma_cStream;
4
+ static VALUE cEncoder;
5
+ static VALUE cDecoder;
6
+ static VALUE cAutoDecoder;
7
+ static VALUE cRawEncoder;
8
+ static VALUE cRawDecoder;
9
+
10
+ enum
11
+ {
12
+ WORK_BUFFER_SIZE = 256 * 1024, // 256 KiB
13
+ BUFFER_BLOCK_SIZE = WORK_BUFFER_SIZE,
14
+ UPDATE_TRY_MAX = 2,
15
+ };
16
+
17
+ static inline void filter_copy(lzma_filter *dest, VALUE filter)
18
+ {
19
+ memcpy(dest, extlzma_getfilter(filter), sizeof(*dest));
20
+ }
21
+
22
+ static void stream_clear(lzma_stream *stream)
23
+ {
24
+ static const lzma_stream init = LZMA_STREAM_INIT;
25
+ memcpy(stream, &init, sizeof(init));
26
+ }
27
+
28
+ static inline lzma_stream *getstream(VALUE lzma)
29
+ {
30
+ return getref(lzma);
31
+ }
32
+
33
+ static inline void stream_cleanup(void *pp)
34
+ {
35
+ if (pp)
36
+ {
37
+ lzma_stream *p = (lzma_stream *)pp;
38
+ lzma_end(p);
39
+ free(p);
40
+ }
41
+ }
42
+
43
+ static void stream_mark(void *p)
44
+ {
45
+ }
46
+
47
+ static VALUE stream_alloc(VALUE klass)
48
+ {
49
+ lzma_stream *p;
50
+ VALUE obj = Data_Make_Struct(klass, lzma_stream, stream_mark, stream_cleanup, p);
51
+ stream_clear(p);
52
+ return obj;
53
+ }
54
+
55
+ static VALUE aux_str_reserve(VALUE str, size_t size)
56
+ {
57
+ size_t capa = rb_str_capacity(str);
58
+ if (capa < size)
59
+ {
60
+ rb_str_modify_expand(str, size - RSTRING_LEN(str));
61
+ }
62
+ else
63
+ {
64
+ rb_str_modify(str);
65
+ }
66
+
67
+ return str;
68
+ }
69
+
70
+ static inline void *aux_lzma_code_nogvl(va_list *p)
71
+ {
72
+ lzma_stream *stream = va_arg(*p, lzma_stream *);
73
+ lzma_action sync = va_arg(*p, lzma_action);
74
+ return (void *)lzma_code(stream, sync);
75
+ }
76
+
77
+ static inline lzma_ret aux_lzma_code(lzma_stream *stream, lzma_action sync)
78
+ {
79
+ return (lzma_ret)aux_thread_call_without_gvl(aux_lzma_code_nogvl, stream, sync);
80
+ }
81
+
82
+ /*
83
+ * call-seq:
84
+ * code(src, dest, maxdest, action) -> status
85
+ *
86
+ * +lzma_code+ を用いて、圧縮/伸長処理を行います。
87
+ *
88
+ * [RETURN]
89
+ * +lzma_code+ が返す整数値をそのまま返します。
90
+ *
91
+ * [src]
92
+ * 処理前のバイナリデータが格納された文字列オブジェクトを与えます。
93
+ *
94
+ * このオブジェクトは変更されます。
95
+ *
96
+ * 処理された部分が取り除かれます (処理されなかった部分が残ります)。
97
+ *
98
+ * [dest]
99
+ * 処理後のバイナリデータを格納する文字列オブジェクトを与えます。
100
+ *
101
+ * [maxdest]
102
+ * dest の最大処理バイト数を与えます。
103
+ *
104
+ * [action]
105
+ * +lzma_code+ の +action+ 引数に渡される整数値です。
106
+ */
107
+ static VALUE stream_code(VALUE stream, VALUE src, VALUE dest, VALUE maxdest, VALUE action)
108
+ {
109
+ lzma_stream *p = getstream(stream);
110
+
111
+ if (NIL_P(src))
112
+ {
113
+ p->next_in = NULL;
114
+ p->avail_in = 0;
115
+ }
116
+ else
117
+ {
118
+ rb_check_type(src, RUBY_T_STRING);
119
+ rb_str_modify(src);
120
+ p->next_in = (uint8_t *)RSTRING_PTR(src);
121
+ p->avail_in = RSTRING_LEN(src);
122
+ }
123
+
124
+ size_t maxdestn = NUM2SIZET(maxdest);
125
+ rb_check_type(dest, RUBY_T_STRING);
126
+ aux_str_reserve(dest, maxdestn);
127
+ p->next_out = (uint8_t *)RSTRING_PTR(dest);
128
+ p->avail_out = maxdestn;
129
+
130
+ lzma_action act = NUM2INT(action);
131
+
132
+ lzma_ret s = aux_lzma_code(p, act);
133
+
134
+ if (p->next_in)
135
+ {
136
+ size_t srcrest = p->avail_in;
137
+ memmove(RSTRING_PTR(src), p->next_in, srcrest);
138
+ rb_str_set_len(src, srcrest);
139
+ }
140
+
141
+ rb_str_set_len(dest, maxdestn - p->avail_out);
142
+
143
+ return UINT2NUM(s);
144
+ }
145
+
146
+ // filter は LZMA::Filter クラスのインスタンスを与えることができる
147
+ static void filter_setup(lzma_filter filterpack[LZMA_FILTERS_MAX + 1], VALUE filter[], VALUE *filterend, VALUE encoder)
148
+ {
149
+ if ((filterend - filter) > LZMA_FILTERS_MAX)
150
+ {
151
+ rb_raise(extlzma_eFilterTooLong,
152
+ "filter chain too long (max %d, but given %d)",
153
+ LZMA_FILTERS_MAX, (int)(filterend - filter));
154
+ }
155
+ for (; filter < filterend; filter++, filterpack++)
156
+ {
157
+ VALUE f = *filter;
158
+ if (!rb_obj_is_kind_of(f, extlzma_cFilter))
159
+ {
160
+ rb_raise(rb_eTypeError,
161
+ "not a filter - #<%s:%p>",
162
+ rb_obj_classname(f), (void *)f);
163
+ }
164
+ filter_copy(filterpack, f);
165
+ }
166
+ filterpack->id = LZMA_VLI_UNKNOWN;
167
+ filterpack->options = NULL;
168
+ }
169
+
170
+ #define RETRY_NOMEM(TIMES, STMT) \
171
+ ({ \
172
+ lzma_ret RETRY_NOMEM_status = 0; \
173
+ int RETRY_NOMEM_i; \
174
+ for (RETRY_NOMEM_i = (TIMES); RETRY_NOMEM_i > 0; RETRY_NOMEM_i--) \
175
+ { \
176
+ RETRY_NOMEM_status = ({ STMT; }); \
177
+ if (RETRY_NOMEM_status == LZMA_MEM_ERROR && RETRY_NOMEM_i > 1) \
178
+ { \
179
+ rb_gc(); \
180
+ continue; \
181
+ } \
182
+ break; \
183
+ } \
184
+ RETRY_NOMEM_status; \
185
+ })
186
+
187
+ static int conv_checkmethod(VALUE check)
188
+ {
189
+ check = rb_hash_lookup2(check, ID2SYM(extlzma_id_check), Qundef);
190
+ switch (check)
191
+ {
192
+ case Qnil:
193
+ return LZMA_CHECK_NONE;
194
+ case Qundef:
195
+ return LZMA_CHECK_CRC64;
196
+ default:
197
+ if (check == ID2SYM(extlzma_id_none))
198
+ {
199
+ return LZMA_CHECK_NONE;
200
+ }
201
+ else if (check == ID2SYM(extlzma_id_crc32))
202
+ {
203
+ return LZMA_CHECK_CRC32;
204
+ }
205
+ else if (check == ID2SYM(extlzma_id_crc64))
206
+ {
207
+ return LZMA_CHECK_CRC64;
208
+ }
209
+ else if (check == ID2SYM(extlzma_id_sha256))
210
+ {
211
+ return LZMA_CHECK_SHA256;
212
+ }
213
+ else
214
+ {
215
+ return NUM2UINT(check);
216
+ }
217
+ }
218
+ }
219
+
220
+ static inline void ext_encoder_init_scanargs(VALUE encoder, int argc, VALUE argv[], lzma_filter filterpack[LZMA_FILTERS_MAX + 1], uint32_t *check)
221
+ {
222
+ if (check)
223
+ {
224
+ VALUE tmp;
225
+ rb_scan_args(argc, argv, "13:", NULL, NULL, NULL, NULL, &tmp);
226
+ if (NIL_P(tmp))
227
+ {
228
+ *check = LZMA_CHECK_CRC64;
229
+ }
230
+ else
231
+ {
232
+ *check = conv_checkmethod(tmp);
233
+ argc--;
234
+ }
235
+ }
236
+ else
237
+ {
238
+ rb_scan_args(argc, argv, "13", NULL, NULL, NULL, NULL);
239
+ }
240
+ memset(filterpack, 0, sizeof(lzma_filter[LZMA_FILTERS_MAX + 1]));
241
+ filter_setup(filterpack, argv, argv + argc, encoder);
242
+ }
243
+
244
+ /*
245
+ * call-seq:
246
+ * initialize(filter1, check: CHECK_CRC64) -> encoder
247
+ * initialize(filter1, filter2, check: CHECK_CRC64) -> encoder
248
+ * initialize(filter1, filter2, filter3, check: CHECK_CRC64) -> encoder
249
+ * initialize(filter1, filter2, filter3, filter4, check: CHECK_CRC64) -> encoder
250
+ *
251
+ * 圧縮器を生成します。圧縮されたデータストリームは xz ファイルフォーマットです。
252
+ *
253
+ * [RETURN]
254
+ * 生成された圧縮器
255
+ *
256
+ * [filter1, filter2, filter3, filter4]
257
+ * LZMA::Filter インスタンス。最低一つを必要とします。
258
+ *
259
+ * [check]
260
+ * チェックメソッド。
261
+ *
262
+ * CHECK_NONE CHECK_CRC32 CHECK_CRC64 CHECK_SHA256 のいずれかの定数を与えます。
263
+ *
264
+ * [EXCEPTIONS]
265
+ * (NO DOCUMENTS)
266
+ */
267
+ static VALUE encoder_init(int argc, VALUE argv[], VALUE stream)
268
+ {
269
+ lzma_stream *p = getstream(stream);
270
+
271
+ uint32_t check;
272
+ lzma_filter filterpack[LZMA_FILTERS_MAX + 1];
273
+ ext_encoder_init_scanargs(stream, argc, argv, filterpack, &check);
274
+
275
+ AUX_LZMA_TEST(RETRY_NOMEM(2, lzma_stream_encoder(p, filterpack, check)));
276
+
277
+ return stream;
278
+ }
279
+
280
+ static inline void ext_decoder_init_scanargs(int argc, VALUE argv[], uint64_t *memlimit, uint32_t *flags)
281
+ {
282
+ switch (argc)
283
+ {
284
+ case 0:
285
+ *memlimit = UINT64_MAX;
286
+ *flags = 0;
287
+ return;
288
+ case 1:
289
+ *memlimit = NIL_P(argv[0]) ? UINT64_MAX : NUM2SIZET(argv[0]);
290
+ *flags = 0;
291
+ return;
292
+ case 2:
293
+ *memlimit = NIL_P(argv[0]) ? UINT64_MAX : NUM2SIZET(argv[0]);
294
+ *flags = NIL_P(argv[1]) ? 0 : (uint32_t)NUM2UINT(argv[1]);
295
+ return;
296
+ }
297
+
298
+ rb_error_arity(argc, 0, 2);
299
+ }
300
+
301
+ /*
302
+ * call-seq:
303
+ * initialize(memlimit = nil, flags = 0)
304
+ *
305
+ * [RETURN]
306
+ * 伸張器を返します。
307
+ *
308
+ * [memlimit]
309
+ * 作業メモリ量の最大値を指定します。単位はバイトです。
310
+ *
311
+ * [flags]
312
+ * 伸張器の挙動を変更するための整数値を指定します。定数として次のものが利用できます。
313
+ *
314
+ * - LZMA::TELL_NO_CHECK
315
+ * - LZMA::TELL_UNSUPPORTED_CHECK
316
+ * - LZMA::TELL_ANY_CHECK
317
+ * - LZMA::CONCATENATED
318
+ *
319
+ * これらの意味は xz ユーティリティに含まれる liblzma/api/lzma/container.h に記述されています。
320
+ */
321
+ static VALUE autodecoder_init(int argc, VALUE argv[], VALUE stream)
322
+ {
323
+ lzma_stream *p = getstream(stream);
324
+ uint64_t memlimit;
325
+ uint32_t flags;
326
+ ext_decoder_init_scanargs(argc, argv, &memlimit, &flags);
327
+
328
+ AUX_LZMA_TEST(RETRY_NOMEM(2, lzma_auto_decoder(p, memlimit, flags)));
329
+
330
+ return stream;
331
+ }
332
+
333
+ /*
334
+ * call-seq:
335
+ * initialize(memlimit = nil, flags = 0)
336
+ *
337
+ * xz ストリームの伸張器を返します。
338
+ *
339
+ * 引数については AutoDecoder#initialize と同じです。
340
+ */
341
+ static VALUE decoder_init(int argc, VALUE argv[], VALUE stream)
342
+ {
343
+ lzma_stream *p = getstream(stream);
344
+
345
+ uint64_t memlimit;
346
+ uint32_t flags;
347
+ ext_decoder_init_scanargs(argc, argv, &memlimit, &flags);
348
+
349
+ AUX_LZMA_TEST(RETRY_NOMEM(2, lzma_stream_decoder(p, memlimit, flags)));
350
+
351
+ return stream;
352
+ }
353
+
354
+ /*
355
+ * call-seq:
356
+ * initialize(filter1) -> encoder
357
+ * initialize(filter1, filter2) -> encoder
358
+ * initialize(filter1, filter2, filter3) -> encoder
359
+ * initialize(filter1, filter2, filter3, filter4) -> encoder
360
+ *
361
+ * 生の (xzヘッダなどの付かない) LZMA1/LZMA2ストリームを構成する圧縮器を生成します。
362
+ *
363
+ * [RETURN]
364
+ * 圧縮器を返します。
365
+ *
366
+ * [filter1, filter2, filter3, filter4]
367
+ * Filter インスタンスを与えます。
368
+ *
369
+ * Filter インスタンスは、例えば LZMA2 フィルタを生成する場合 Filter.lzma2 を利用します。
370
+ */
371
+ static VALUE rawencoder_init(int argc, VALUE argv[], VALUE stream)
372
+ {
373
+ lzma_stream *p = getstream(stream);
374
+
375
+ lzma_filter filterpack[LZMA_FILTERS_MAX + 1];
376
+ ext_encoder_init_scanargs(stream, argc, argv, filterpack, NULL);
377
+
378
+ AUX_LZMA_TEST(RETRY_NOMEM(2, lzma_raw_encoder(p, filterpack)));
379
+
380
+ return stream;
381
+ }
382
+
383
+ /*
384
+ * call-seq:
385
+ * initialize(filter1) -> decoder
386
+ * initialize(filter1, filter2) -> decoder
387
+ * initialize(filter1, filter2, filter3) -> decoder
388
+ * initialize(filter1, filter2, filter3, filter4) -> decoder
389
+ */
390
+ static VALUE rawdecoder_init(int argc, VALUE argv[], VALUE stream)
391
+ {
392
+ lzma_stream *p = getstream(stream);
393
+
394
+ lzma_filter filterpack[LZMA_FILTERS_MAX + 1];
395
+ ext_encoder_init_scanargs(stream, argc, argv, filterpack, NULL);
396
+
397
+ AUX_LZMA_TEST(RETRY_NOMEM(2, lzma_raw_decoder(p, filterpack)));
398
+
399
+ return stream;
400
+ }
401
+
402
+ void extlzma_init_Stream(void)
403
+ {
404
+ extlzma_cStream = rb_define_class_under(extlzma_mLZMA, "Stream", rb_cObject);
405
+ rb_undef_alloc_func(extlzma_cStream);
406
+ rb_define_method(extlzma_cStream, "code", stream_code, 4);
407
+
408
+ cEncoder = rb_define_class_under(extlzma_cStream, "Encoder", extlzma_cStream);
409
+ rb_define_alloc_func(cEncoder, stream_alloc);
410
+ rb_define_method(cEncoder, "initialize", RUBY_METHOD_FUNC(encoder_init), -1);
411
+ rb_define_alias(cEncoder, "encode", "code");
412
+ rb_define_alias(cEncoder, "compress", "code");
413
+
414
+ cDecoder = rb_define_class_under(extlzma_cStream, "Decoder", extlzma_cStream);
415
+ rb_define_alloc_func(cDecoder, stream_alloc);
416
+ rb_define_method(cDecoder, "initialize", RUBY_METHOD_FUNC(decoder_init), -1);
417
+ rb_define_alias(cDecoder, "decode", "code");
418
+ rb_define_alias(cDecoder, "decompress", "code");
419
+ rb_define_alias(cDecoder, "uncompress", "code");
420
+
421
+ cAutoDecoder = rb_define_class_under(extlzma_cStream, "AutoDecoder", extlzma_cStream);
422
+ rb_define_alloc_func(cAutoDecoder, stream_alloc);
423
+ rb_define_method(cAutoDecoder, "initialize", RUBY_METHOD_FUNC(autodecoder_init), -1);
424
+ rb_define_alias(cDecoder, "decode", "code");
425
+ rb_define_alias(cDecoder, "decompress", "code");
426
+ rb_define_alias(cDecoder, "uncompress", "code");
427
+
428
+ cRawEncoder = rb_define_class_under(extlzma_cStream, "RawEncoder", extlzma_cStream);
429
+ rb_define_alloc_func(cRawEncoder, stream_alloc);
430
+ rb_define_method(cRawEncoder, "initialize", RUBY_METHOD_FUNC(rawencoder_init), -1);
431
+ rb_define_alias(cEncoder, "encode", "code");
432
+ rb_define_alias(cEncoder, "compress", "code");
433
+
434
+ cRawDecoder = rb_define_class_under(extlzma_cStream, "RawDecoder", extlzma_cStream);
435
+ rb_define_alloc_func(cRawDecoder, stream_alloc);
436
+ rb_define_method(cRawDecoder, "initialize", RUBY_METHOD_FUNC(rawdecoder_init), -1);
437
+ rb_define_alias(cDecoder, "decode", "code");
438
+ rb_define_alias(cDecoder, "decompress", "code");
439
+ rb_define_alias(cDecoder, "uncompress", "code");
440
+ }
@@ -0,0 +1,93 @@
1
+ #include "extlzma2.h"
2
+
3
+ static inline VALUE crc_calc(void *(*update)(va_list *vp), int argc, VALUE argv[])
4
+ {
5
+ VALUE src, crc;
6
+ rb_scan_args(argc, argv, "11", &src, &crc);
7
+ rb_check_type(src, RUBY_T_STRING);
8
+ return (VALUE)aux_thread_call_without_gvl(update,
9
+ (const uint8_t *)RSTRING_PTR(src),
10
+ RSTRING_LEN(src),
11
+ NIL_P(crc) ? INT2FIX(0) : crc);
12
+ }
13
+
14
+ static VALUE crc32_update(va_list *vp)
15
+ {
16
+ const uint8_t *ptr = va_arg(*vp, const uint8_t *);
17
+ size_t size = va_arg(*vp, size_t);
18
+ VALUE crc = va_arg(*vp, VALUE);
19
+ return UINT2NUM(lzma_crc32(ptr, size, NUM2UINT(crc)));
20
+ }
21
+
22
+ /*
23
+ * call-seq:
24
+ * LZMA::Utils.crc32(string, crc = 0)
25
+ *
26
+ * liblzmaに含まれるlzma_crc32を呼び出します。
27
+ */
28
+ static VALUE utils_crc32(int argc, VALUE argv[], VALUE self)
29
+ {
30
+ return crc_calc((void *(*)(va_list *))crc32_update, argc, argv);
31
+ }
32
+
33
+ static VALUE crc64_update(va_list *vp)
34
+ {
35
+ const uint8_t *ptr = va_arg(*vp, const uint8_t *);
36
+ size_t size = va_arg(*vp, size_t);
37
+ VALUE crc = va_arg(*vp, VALUE);
38
+ return ULL2NUM(lzma_crc64(ptr, size, NUM2ULL(crc)));
39
+ }
40
+
41
+ /*
42
+ * call-seq:
43
+ * LZMA::Utils.crc64(string, crc = 0)
44
+ *
45
+ * liblzmaに含まれるlzma_crc64を呼び出します。
46
+ */
47
+ static VALUE utils_crc64(int argc, VALUE argv[], VALUE self)
48
+ {
49
+ return crc_calc((void *(*)(va_list *))crc64_update, argc, argv);
50
+ }
51
+
52
+ /*
53
+ * call-seq:
54
+ * lookup_error(lzma_ret) -> exception class
55
+ */
56
+ static VALUE utils_lookup_error(VALUE mod, VALUE status)
57
+ {
58
+ return extlzma_lookup_error(NUM2INT(status));
59
+ }
60
+
61
+ /*
62
+ * call-seq:
63
+ * stream_buffer_bound(uncompressed_size) -> worst encoded size
64
+ */
65
+ static VALUE utils_stream_buffer_bound(VALUE mod, VALUE size)
66
+ {
67
+ return SIZET2NUM(lzma_stream_buffer_bound(NUM2SIZET(size)));
68
+ }
69
+
70
+ /*
71
+ * call-seq:
72
+ * block_buffer_bound(uncompressed_size) -> worst encoded size
73
+ */
74
+ static VALUE utils_block_buffer_bound(VALUE mod, VALUE size)
75
+ {
76
+ return SIZET2NUM(lzma_block_buffer_bound(NUM2SIZET(size)));
77
+ }
78
+
79
+ static VALUE mUtils;
80
+
81
+ void extlzma_init_Utils(void)
82
+ {
83
+ mUtils = rb_define_module_under(extlzma_mLZMA, "Utils");
84
+
85
+ rb_extend_object(extlzma_mLZMA, mUtils);
86
+ rb_extend_object(mUtils, mUtils); // 自分に対してもモジュールメソッドを利用できるようにする
87
+
88
+ rb_define_method(mUtils, "crc32", RUBY_METHOD_FUNC(utils_crc32), -1);
89
+ rb_define_method(mUtils, "crc64", RUBY_METHOD_FUNC(utils_crc64), -1);
90
+ rb_define_method(mUtils, "lookup_error", RUBY_METHOD_FUNC(utils_lookup_error), 1);
91
+ rb_define_method(mUtils, "stream_buffer_bound", RUBY_METHOD_FUNC(utils_stream_buffer_bound), 1);
92
+ rb_define_method(mUtils, "block_buffer_bound", RUBY_METHOD_FUNC(utils_block_buffer_bound), 1);
93
+ }
data/extlzma2.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/extlzma2/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'extlzma2'
7
+ spec.version = LZMA::VERSION
8
+ spec.authors = ['Ishotihadus']
9
+ spec.email = ['hanachan.pao@gmail.com']
10
+
11
+ spec.summary = 'A Ruby binding of liblzma'
12
+ spec.description = 'A Ruby binding of liblzma that is compatible with Ruby 3.2'
13
+ spec.homepage = 'https://github.com/Ishotihadus/extlzma2'
14
+ spec.required_ruby_version = '>= 2.6.0'
15
+
16
+ spec.metadata['homepage_uri'] = spec.homepage
17
+ spec.metadata['source_code_uri'] = spec.homepage
18
+
19
+ spec.files = Dir.chdir(__dir__) do
20
+ `git ls-files -z`.split("\x0").reject do |f|
21
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
22
+ end
23
+ end
24
+
25
+ spec.require_paths = ['lib']
26
+ spec.extensions = ['ext/extlzma2/extconf.rb']
27
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stringio'
4
+
5
+ module LZMA
6
+ module Aux
7
+ def self.encode(src, encoder)
8
+ if src.is_a?(String)
9
+ s = Encoder.new(encoder, String.new)
10
+ s << src
11
+ s.close
12
+ return s.outport
13
+ end
14
+
15
+ s = Encoder.new(encoder, (src || String.new))
16
+ return s unless block_given?
17
+
18
+ begin
19
+ yield(s)
20
+ ensure
21
+ s.close
22
+ end
23
+ end
24
+
25
+ def self.decode(src, decoder)
26
+ return decode(StringIO.new(src), decoder, &:read) if src.is_a?(String)
27
+
28
+ s = Decoder.new(decoder, src)
29
+ return s unless block_given?
30
+
31
+ begin
32
+ yield(s)
33
+ ensure
34
+ begin
35
+ s.close
36
+ rescue StandardError
37
+ nil
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stringio'
4
+
5
+ module LZMA
6
+ Decoder = Struct.new(:context, :inport, :readbuf, :workbuf, :status) do |klass|
7
+ klass::BLOCKSIZE = 256 * 1024 # 256 KiB
8
+
9
+ def initialize(context, inport)
10
+ super(context, inport, String.new, StringIO.new(String.new), :ready)
11
+ end
12
+
13
+ def read(size = nil, buf = String.new)
14
+ buf.clear
15
+ size = size.to_i if size
16
+ return buf if size == 0
17
+
18
+ tmp = String.new
19
+ while !eof && (size.nil? || size > 0)
20
+ fetch or break if workbuf.eof?
21
+
22
+ workbuf.read(size, tmp) or break
23
+ buf << tmp
24
+ size -= tmp.bytesize if size
25
+ end
26
+
27
+ buf
28
+ end
29
+
30
+ def eof
31
+ !status && workbuf.eof?
32
+ end
33
+
34
+ alias_method :eof?, :eof
35
+
36
+ def close
37
+ self.status = nil
38
+ workbuf.rewind
39
+ workbuf.string.clear
40
+ nil
41
+ end
42
+
43
+ private
44
+
45
+ def fetch
46
+ return nil unless status == :ready
47
+
48
+ while workbuf.eof
49
+ inport.read(self.class::BLOCKSIZE, readbuf) if readbuf.empty?
50
+
51
+ workbuf.string.clear
52
+ workbuf.rewind
53
+ s = if readbuf.empty?
54
+ context.code(nil, workbuf.string, self.class::BLOCKSIZE, LZMA::FINISH)
55
+ else
56
+ context.code(readbuf, workbuf.string, self.class::BLOCKSIZE, 0)
57
+ end
58
+
59
+ case s
60
+ when LZMA::OK
61
+ # pass
62
+ when LZMA::STREAM_END
63
+ self.status = :finished
64
+ break
65
+ else
66
+ Utils.raise_err s
67
+ end
68
+ end
69
+
70
+ self
71
+ end
72
+ end
73
+ end