extlzma2 2.0.0

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