extlzma 0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,105 @@
1
+ #include "extlzma.h"
2
+
3
+ static inline VALUE
4
+ crc_calc(void *(*update)(va_list *vp), int argc, VALUE argv[])
5
+ {
6
+ VALUE src, crc;
7
+ rb_scan_args(argc, argv, "11", &src, &crc);
8
+ rb_check_type(src, RUBY_T_STRING);
9
+ return (VALUE)aux_thread_call_without_gvl(update,
10
+ (const uint8_t *)RSTRING_PTR(src),
11
+ RSTRING_LEN(src),
12
+ NIL_P(crc) ? INT2FIX(0) : crc);
13
+ }
14
+
15
+
16
+ static VALUE
17
+ crc32_update(va_list *vp)
18
+ {
19
+ const uint8_t *ptr = va_arg(*vp, const uint8_t *);
20
+ size_t size = va_arg(*vp, size_t);
21
+ VALUE crc = va_arg(*vp, VALUE);
22
+ return UINT2NUM(lzma_crc32(ptr, size, NUM2UINT(crc)));
23
+ }
24
+
25
+ /*
26
+ * call-seq:
27
+ * LZMA::Utils.crc32(string, crc = 0)
28
+ *
29
+ * liblzmaに含まれるlzma_crc32を呼び出します。
30
+ */
31
+ static VALUE
32
+ utils_crc32(int argc, VALUE argv[], VALUE self)
33
+ {
34
+ return crc_calc((void *(*)(va_list *))crc32_update, argc, argv);
35
+ }
36
+
37
+
38
+ static VALUE
39
+ crc64_update(va_list *vp)
40
+ {
41
+ const uint8_t *ptr = va_arg(*vp, const uint8_t *);
42
+ size_t size = va_arg(*vp, size_t);
43
+ VALUE crc = va_arg(*vp, VALUE);
44
+ return ULL2NUM(lzma_crc64(ptr, size, NUM2ULL(crc)));
45
+ }
46
+
47
+ /*
48
+ * call-seq:
49
+ * LZMA::Utils.crc64(string, crc = 0)
50
+ *
51
+ * liblzmaに含まれるlzma_crc64を呼び出します。
52
+ */
53
+ static VALUE
54
+ utils_crc64(int argc, VALUE argv[], VALUE self)
55
+ {
56
+ return crc_calc((void *(*)(va_list *))crc64_update, argc, argv);
57
+ }
58
+
59
+ /*
60
+ * call-seq:
61
+ * lookup_error(lzma_ret) -> exception class
62
+ */
63
+ static VALUE
64
+ utils_lookup_error(VALUE mod, VALUE status)
65
+ {
66
+ return extlzma_lookup_error(NUM2INT(status));
67
+ }
68
+
69
+ /*
70
+ * call-seq:
71
+ * stream_buffer_bound(uncompressed_size) -> worst encoded size
72
+ */
73
+ static VALUE
74
+ utils_stream_buffer_bound(VALUE mod, VALUE size)
75
+ {
76
+ return SIZET2NUM(lzma_stream_buffer_bound(NUM2SIZET(size)));
77
+ }
78
+
79
+ /*
80
+ * call-seq:
81
+ * block_buffer_bound(uncompressed_size) -> worst encoded size
82
+ */
83
+ static VALUE
84
+ utils_block_buffer_bound(VALUE mod, VALUE size)
85
+ {
86
+ return SIZET2NUM(lzma_block_buffer_bound(NUM2SIZET(size)));
87
+ }
88
+
89
+
90
+ static VALUE mUtils;
91
+
92
+ void
93
+ extlzma_init_Utils(void)
94
+ {
95
+ mUtils = rb_define_module_under(extlzma_mLZMA, "Utils");
96
+
97
+ rb_extend_object(extlzma_mLZMA, mUtils);
98
+ rb_extend_object(mUtils, mUtils); // 自分に対してもモジュールメソッドを利用できるようにする
99
+
100
+ rb_define_method(mUtils, "crc32", RUBY_METHOD_FUNC(utils_crc32), -1);
101
+ rb_define_method(mUtils, "crc64", RUBY_METHOD_FUNC(utils_crc64), -1);
102
+ rb_define_method(mUtils, "lookup_error", RUBY_METHOD_FUNC(utils_lookup_error), 1);
103
+ rb_define_method(mUtils, "stream_buffer_bound", RUBY_METHOD_FUNC(utils_stream_buffer_bound), 1);
104
+ rb_define_method(mUtils, "block_buffer_bound", RUBY_METHOD_FUNC(utils_block_buffer_bound), 1);
105
+ }
@@ -0,0 +1,17 @@
1
+ require_relative "lib/extlzma/version"
2
+
3
+ GEMSTUB = Gem::Specification.new do |s|
4
+ s.name = "extlzma"
5
+ s.version = LZMA::VERSION
6
+ s.summary = "ruby bindings for liblzma in the xz utilities"
7
+ s.description = <<EOS
8
+ ruby bindings for liblzma in the xz utilities <http://tukaani.org/xz/>
9
+ EOS
10
+ s.license = "BSD-2-Clause"
11
+ s.author = "dearblue"
12
+ s.email = "dearblue@users.osdn.me"
13
+ s.homepage = "https://osdn.jp/projects/rutsubo/"
14
+
15
+ s.required_ruby_version = ">= 2.0"
16
+ s.add_development_dependency "rake", "~> 11.0"
17
+ end
@@ -0,0 +1,529 @@
1
+ #vim: set fileencoding:utf-8
2
+
3
+ begin
4
+ libname = File.basename(__FILE__, ".rb") << ".so"
5
+ require_relative libname
6
+ rescue LoadError
7
+ ver = RUBY_VERSION.slice(/\d+\.\d+/)
8
+ require_relative "#{ver}/#{libname}"
9
+ end
10
+
11
+ require_relative "extlzma/version"
12
+ require "stringio"
13
+
14
+ module LZMA
15
+ #
16
+ # call-seq:
17
+ # encode(string_data, preset = LZMA::PRESET_DEFAULT, opts = {}) -> encoded_xz_data
18
+ # encode(string_data, filter...) -> encoded_xz_data
19
+ # encode(output_stream = nil, preset = LZMA::PRESET_DEFAULT, opts = {}) -> stream_encoder
20
+ # encode(output_stream, filter...) -> stream_encoder
21
+ # encode(output_stream = nil, preset = LZMA::PRESET_DEFAULT, opts = {}) { |encoder| ... } -> yield return value
22
+ # encode(output_stream, filter...) { |encoder| ... } -> yield return value
23
+ #
24
+ # データを圧縮、または圧縮器を生成します。
25
+ #
26
+ # 圧縮されたデータ列は xz ファイルフォーマットとなるため、コマンドラインの xz などで伸張させることが可能です。
27
+ #
28
+ # [RETURN encoded_xz_data]
29
+ # xz データストリームとしての String インスタンスです。
30
+ # [RETURN stream_encoder]
31
+ # xz データストリームを生成する圧縮器を返します。
32
+ # [RETURN output_stream]
33
+ # 引数として渡した <tt>output_stream</tt> そのものを返します。
34
+ # [string_data]
35
+ # 圧縮元となるデータを String インスタンスで渡します。
36
+ # liblzma はエンコーディング情報を無視し、そのままのバイト列をバイナリデータと見立て処理を行います。
37
+ # [preset = LZMA::PRESET_DEFAULT]
38
+ # 圧縮プリセット値を指定します (圧縮レベルのようなものです)。詳細は LZMA::Filter::LZMA2.new を見てください。
39
+ # [opts]
40
+ # LZMA::Filter::LZMA2.new に渡される可変引数です。詳細は LZMA::Filter::LZMA2.new を見てください。
41
+ # [filter]
42
+ # LZMA::Filter で定義されているクラスのインスタンスを指定します。最大4つまで指定することが出来ます。
43
+ # [output_stream]
44
+ # 圧縮データの受け皿となるオブジェクトを指定します。
45
+ #
46
+ # <tt>.<<</tt> メソッドが呼ばれます。
47
+ # [YIELD RETURN]
48
+ # 無視されます。
49
+ # [YIELD encoder]
50
+ # 圧縮器が渡されます。
51
+ #
52
+ # [EXCEPTIONS]
53
+ # (NO DOCUMENT)
54
+ #
55
+ def self.encode(src = nil, *args, &block)
56
+ Aux.encode(src, Stream.encoder(*args), &block)
57
+ end
58
+
59
+ #
60
+ # call-seq:
61
+ # decode(string_data) -> decoded data
62
+ # decode(string_data, filter...) -> decoded data
63
+ # decode(input_stream) -> decoder
64
+ # decode(input_stream, filter...) -> decoder
65
+ # decode(input_stream) { |decoder| ... }-> yield return value
66
+ # decode(input_stream, filter...) { |decoder| ... }-> yield return value
67
+ #
68
+ # 圧縮されたデータを伸張します。
69
+ #
70
+ # [RETURN decoded data]
71
+ # xz データストリームとしての String インスタンスです。
72
+ # [RETURN decoder]
73
+ # [RETURN yield return value]
74
+ # [string_data]
75
+ # 圧縮されたデータを与えます。圧縮されたデータの形式は xz と lzma です。これらはあらかじめ区別する必要なく与えることが出来ます。
76
+ # [options]
77
+ # LZMA::Filter::LZMA2.new に渡される可変引数です。詳細は LZMA::Filter::LZMA2.new を見てください。
78
+ # [EXCEPTIONS]
79
+ # (NO DOCUMENT)
80
+ #
81
+ def self.decode(src, *args, &block)
82
+ Aux.decode(src, Stream.auto_decoder(*args), &block)
83
+ end
84
+
85
+ #
86
+ # call-seq:
87
+ # LZMA.raw_encode(src) -> encoded data
88
+ # LZMA.raw_encode(src, filter...) -> encoded data
89
+ # LZMA.raw_encode(outport = nil) -> encoder
90
+ # LZMA.raw_encode(outport, filter...) -> encoder
91
+ # LZMA.raw_encode(outport = nil) { |encoder| ... } -> yield return value
92
+ # LZMA.raw_encode(outport, filter...) { |encoder| ... } -> yield return value
93
+ #
94
+ # データを圧縮します。圧縮されたデータ列は生の lzma1/lzma2 のデータ列であるため、伸張する際に適切なフィルタを与える必要があります。
95
+ #
96
+ # xz ファイルフォーマットヘッダや整合値を持たないため、これらが不要な場合は有用かもしれません。
97
+ #
98
+ # [RETURN encoded data]
99
+ # 生の lzma1/lzma2 のデータ列となる String インスタンスです。
100
+ # [src]
101
+ # 圧縮元となるデータを String インスタンスで渡します。
102
+ # [filter]
103
+ # LZMA::Filter のインスタンスを与えます。最大4つまで指定可能です。
104
+ #
105
+ # 省略時は lzma2 フィルタが指定されたとみなします。
106
+ # [EXCEPTIONS]
107
+ # (NO DOCUMENT)
108
+ #
109
+ def self.raw_encode(src, *args, &block)
110
+ Aux.encode(src, Stream.raw_encoder(*args), &block)
111
+ end
112
+
113
+ #
114
+ # call-seq:
115
+ # LZMA.raw_decode(encoded_data) -> decoded data
116
+ # LZMA.raw_decode(encoded_data, filter...) -> decoded data
117
+ # LZMA.raw_decode(inport) -> raw decoder
118
+ # LZMA.raw_decode(inport, filter...) -> raw decoder
119
+ # LZMA.raw_decode(inport) { |decoder| ... } -> yield return value
120
+ # LZMA.raw_decode(inport, filter...) { |decoder| ... } -> yield return value
121
+ #
122
+ # 圧縮されたデータを伸張します。圧縮した際に用いたフィルタをそのままの順番・数で与える必要があります。
123
+ #
124
+ # [RETURN decoded data]
125
+ # 伸張されたデータ列となる String インスタンスです。
126
+ # [src]
127
+ # 圧縮された生の lzma1/lzma2 の String インスタンスを渡します。
128
+ # [options]
129
+ # LZMA::Filter のインスタンスを与えます。最大4つまで指定可能です。
130
+ #
131
+ # 省略時は lzma2 フィルタが指定されたとみなします。
132
+ # [EXCEPTIONS]
133
+ # (NO DOCUMENT)
134
+ #
135
+ def self.raw_decode(src, *args, &block)
136
+ Aux.decode(src, Stream.raw_decoder(*args), &block)
137
+ end
138
+
139
+ def self.lzma1(*args)
140
+ LZMA::Filter::LZMA1.new(*args)
141
+ end
142
+
143
+ def self.lzma2(*args)
144
+ LZMA::Filter::LZMA2.new(*args)
145
+ end
146
+
147
+ def self.delta(*args)
148
+ LZMA::Filter::Delta.new(*args)
149
+ end
150
+
151
+ class Encoder < Struct.new(:context, :outport, :writebuf, :workbuf, :status)
152
+ BLOCKSIZE = 256 * 1024 # 256 KiB
153
+
154
+ def initialize(context, outport)
155
+ super(context, outport,
156
+ StringIO.new("".force_encoding(Encoding::BINARY)),
157
+ "".force_encoding(Encoding::BINARY), [1])
158
+ self.class.method(:finalizer_regist).(self, context, outport, writebuf, workbuf, status)
159
+ end
160
+
161
+ def write(buf)
162
+ writebuf.rewind
163
+ writebuf.string.clear
164
+ writebuf << buf
165
+ until writebuf.string.empty?
166
+ s = context.code(writebuf.string, workbuf, BLOCKSIZE, 0)
167
+ unless s == 0
168
+ Utils.raise_err s
169
+ end
170
+ outport << workbuf
171
+ workbuf.clear
172
+ end
173
+
174
+ self
175
+ end
176
+
177
+ alias << write
178
+
179
+ def close
180
+ if eof?
181
+ raise "already closed stream - #{inspect}"
182
+ end
183
+
184
+ self.class.method(:finalizer_close).(context, outport, workbuf)
185
+
186
+ status[0] = nil
187
+
188
+ nil
189
+ end
190
+
191
+ def eof
192
+ !status[0]
193
+ end
194
+
195
+ alias eof? eof
196
+
197
+ class << self
198
+ private
199
+ def finalizer_regist(obj, context, outport, writebuf, workbuf, status)
200
+ ObjectSpace.define_finalizer(obj, finalizer_make(context, outport, writebuf, workbuf, status))
201
+ end
202
+
203
+ private
204
+ def finalizer_make(context, outport, writebuf, workbuf, status)
205
+ proc do
206
+ if status[0]
207
+ until writebuf.string.empty?
208
+ s = context.code(writebuf.string, workbuf, BLOCKSIZE, 0)
209
+ Utils.raise_err s unless s == LZMA::OK
210
+ outport << workbuf
211
+ workbuf.clear
212
+ end
213
+
214
+ finalizer_close(context, outport, workbuf)
215
+
216
+ status[0] = nil
217
+ end
218
+ end
219
+ end
220
+
221
+ private
222
+ def finalizer_close(context, outport, workbuf)
223
+ while true
224
+ workbuf.clear
225
+ s = context.code(nil, workbuf, BLOCKSIZE, LZMA::FINISH)
226
+ outport << workbuf
227
+ break if s == LZMA::STREAM_END
228
+ Utils.raise_err s unless s == LZMA::OK
229
+ end
230
+ end
231
+ end
232
+ end
233
+
234
+ class Decoder < Struct.new(:context, :inport, :readbuf, :workbuf, :status)
235
+ BLOCKSIZE = 256 * 1024 # 256 KiB
236
+
237
+ def initialize(context, inport)
238
+ super context, inport,
239
+ "".force_encoding(Encoding::BINARY),
240
+ StringIO.new("".force_encoding(Encoding::BINARY)),
241
+ :ready
242
+ end
243
+
244
+ def read(size = nil, buf = "".force_encoding(Encoding::BINARY))
245
+ buf.clear
246
+ size = size.to_i if size
247
+ return buf if size == 0
248
+
249
+ tmp = "".force_encoding(Encoding::BINARY)
250
+ while !eof && (size.nil? || size > 0)
251
+ if workbuf.eof?
252
+ fetch or break
253
+ end
254
+
255
+ workbuf.read(size, tmp) or break
256
+ buf << tmp
257
+ size -= tmp.bytesize if size
258
+ end
259
+
260
+ (buf.empty? ? nil : buf)
261
+ end
262
+
263
+ def eof
264
+ !status && workbuf.eof?
265
+ end
266
+
267
+ alias eof? eof
268
+
269
+ def close
270
+ self.status = nil
271
+ workbuf.rewind
272
+ workbuf.string.clear
273
+ nil
274
+ end
275
+
276
+ private
277
+ def fetch
278
+ return nil unless status == :ready
279
+
280
+ while workbuf.eof
281
+ if readbuf.empty?
282
+ inport.read(BLOCKSIZE, readbuf)
283
+ end
284
+
285
+ workbuf.string.clear
286
+ workbuf.rewind
287
+ if readbuf.empty?
288
+ s = context.code(nil, workbuf.string, BLOCKSIZE, LZMA::FINISH)
289
+ else
290
+ s = context.code(readbuf, workbuf.string, BLOCKSIZE, 0)
291
+ end
292
+
293
+ case s
294
+ when LZMA::OK
295
+ when LZMA::STREAM_END
296
+ self.status = :finished
297
+ break
298
+ else
299
+ Utils.raise_err s
300
+ end
301
+ end
302
+
303
+ self
304
+ end
305
+ end
306
+
307
+ class Stream
308
+ def self.encoder(*args, **opts)
309
+ case
310
+ when args.empty?
311
+ Encoder.new(Filter::LZMA2.new(LZMA::PRESET_DEFAULT), **opts)
312
+ when args.size == 1 && args[0].kind_of?(Numeric)
313
+ Encoder.new(Filter::LZMA2.new(args[0]), **opts)
314
+ else
315
+ Encoder.new(*args, **opts)
316
+ end
317
+ end
318
+
319
+ def self.decoder(*args)
320
+ case
321
+ when args.empty?
322
+ Decoder.new(Filter::LZMA2.new(LZMA::PRESET_DEFAULT))
323
+ when args.size == 1 && args[0].kind_of?(Numeric)
324
+ Decoder.new(Filter::LZMA2.new(args[0]))
325
+ else
326
+ Decoder.new(*args)
327
+ end
328
+ end
329
+
330
+ def self.auto_decoder(*args)
331
+ AutoDecoder.new(*args)
332
+ end
333
+
334
+ def self.raw_encoder(*args)
335
+ case
336
+ when args.size == 0
337
+ RawEncoder.new(Filter::LZMA2.new(LZMA::PRESET_DEFAULT))
338
+ when args.size == 1 && args[0].kind_of?(Numeric)
339
+ RawEncoder.new(Filter::LZMA2.new(args[0]))
340
+ else
341
+ RawEncoder.new(*args)
342
+ end
343
+ end
344
+
345
+ def self.raw_decoder(*args)
346
+ case
347
+ when args.size == 0
348
+ RawDecoder.new(Filter::LZMA2.new(LZMA::PRESET_DEFAULT))
349
+ when args.size == 1 && args[0].kind_of?(Numeric)
350
+ RawDecoder.new(Filter::LZMA2.new(args[0]))
351
+ else
352
+ RawDecoder.new(*args)
353
+ end
354
+ end
355
+ end
356
+
357
+ class Filter
358
+ def self.lzma1(*args)
359
+ LZMA1.new(*args)
360
+ end
361
+
362
+ def self.lzma2(*args)
363
+ LZMA2.new(*args)
364
+ end
365
+
366
+ def self.delta(*args)
367
+ Delta.new(*args)
368
+ end
369
+ end
370
+
371
+ module Utils
372
+ extend self
373
+
374
+ def crc32_digest(seq, init = 0)
375
+ [Utils.crc32(seq, init)].pack("N")
376
+ end
377
+
378
+ def crc32_hexdigest(seq, init = 0)
379
+ "%08X" % Utils.crc32(seq, init)
380
+ end
381
+
382
+ def crc64_digest(seq, init = 0)
383
+ [Utils.crc64(seq, init)].pack("Q>")
384
+ end
385
+
386
+ def crc64_hexdigest(seq, init = 0)
387
+ "%016X" % Utils.crc64(seq, init)
388
+ end
389
+
390
+ #
391
+ # CRC32 を Digest のように生成できるようになります。
392
+ #
393
+ class CRC32 < Struct.new(:state, :init)
394
+ def initialize(state = 0)
395
+ state = state.to_i
396
+ super(state, state)
397
+ end
398
+
399
+ #
400
+ # call-seq:
401
+ # LZMA::Utils::CRC32#update(data) -> self
402
+ #
403
+ def update(data)
404
+ self.state = Utils.crc32(data, state)
405
+ self
406
+ end
407
+
408
+ alias << update
409
+
410
+ def finish
411
+ self
412
+ end
413
+
414
+ def reset
415
+ self.state = init
416
+ self
417
+ end
418
+
419
+ def digest
420
+ [state].pack("N")
421
+ end
422
+
423
+ def hexdigest
424
+ "%08x" % state
425
+ end
426
+
427
+ alias to_s hexdigest
428
+
429
+ def to_str
430
+ "CRC32 <#{hexdigest}>"
431
+ end
432
+
433
+ alias inspect to_str
434
+ end
435
+
436
+ #
437
+ # CRC64 を Digest のように生成できるようになります。
438
+ #
439
+ class CRC64 < Struct.new(:state, :init)
440
+ def initialize(state = 0)
441
+ state = state.to_i
442
+ super(state, state)
443
+ end
444
+
445
+ #
446
+ # call-seq:
447
+ # LZMA::Utils::CRC64#update(data) -> self
448
+ #
449
+ def update(data)
450
+ self.state = Utils.crc64(data, state)
451
+ self
452
+ end
453
+
454
+ alias << update
455
+
456
+ def finish
457
+ self
458
+ end
459
+
460
+ def reset
461
+ self.state = init
462
+ self
463
+ end
464
+
465
+ def digest
466
+ [state].pack("Q>")
467
+ end
468
+
469
+ def hexdigest
470
+ "%016x" % state
471
+ end
472
+
473
+ alias to_s hexdigest
474
+
475
+ def to_str
476
+ "CRC64 <#{hexdigest}>"
477
+ end
478
+
479
+ alias inspect to_str
480
+ end
481
+
482
+ def raise_err(lzma_ret, mesg = nil)
483
+ et = Utils.lookup_error(lzma_ret)
484
+ raise et, mesg, caller
485
+ end
486
+ end
487
+
488
+ extend Utils
489
+
490
+ #
491
+ # extlzma の内部で利用される補助モジュールです。
492
+ #
493
+ # extlzma の利用者が直接利用することは想定していません。
494
+ #
495
+ module Aux
496
+ def self.encode(src, encoder)
497
+ if src.kind_of?(String)
498
+ s = Encoder.new(encoder, "".force_encoding(Encoding::BINARY))
499
+ s << src
500
+ s.close
501
+ return s.outport
502
+ end
503
+
504
+ s = Encoder.new(encoder, (src || "".force_encoding(Encoding::BINARY)))
505
+ return s unless block_given?
506
+
507
+ begin
508
+ yield(s)
509
+ ensure
510
+ s.close
511
+ end
512
+ end
513
+
514
+ def self.decode(src, decoder)
515
+ if src.kind_of?(String)
516
+ return decode(StringIO.new(src), decoder) { |s| s.read }
517
+ end
518
+
519
+ s = Decoder.new(decoder, src)
520
+ return s unless block_given?
521
+
522
+ begin
523
+ yield(s)
524
+ ensure
525
+ s.close rescue nil
526
+ end
527
+ end
528
+ end
529
+ end