extlz4 0.2.4.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ blockapi.o: blockapi.c extlz4.h
2
+ extlz4.o: extlz4.c extlz4.h
3
+ frameapi.o: frameapi.c extlz4.h hashargs.h
4
+ hashargs.o: hashargs.c hashargs.h
@@ -0,0 +1,60 @@
1
+ #!ruby
2
+ #vim: set fileencoding:utf-8
3
+
4
+ require "mkmf"
5
+
6
+ module MyExtensions
7
+ refine Object do
8
+ # ruby-2.3 から追加されたメソッドの確認と追加
9
+
10
+ unless Object.method_defined?(:append_cppflags)
11
+ def append_cppflags(flags)
12
+ return false unless try_cppflags(flags)
13
+ $CPPFLAGS << " #{flags}"
14
+ true
15
+ end
16
+ end
17
+
18
+ unless Object.method_defined?(:append_cflags)
19
+ def append_cflags(flags)
20
+ return false unless try_cflags(flags)
21
+ $CFLAGS << " #{flags}"
22
+ true
23
+ end
24
+ end
25
+
26
+ unless Object.method_defined?(:append_ldflags)
27
+ def append_ldflags(flags)
28
+ return false unless try_ldflags(flags)
29
+ $LDFLAGS << " #{flags}"
30
+ true
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ using MyExtensions
37
+
38
+
39
+ # TODO: システムにインストールされた lz4 がある場合、バージョンを確認してより新しければそちらを利用する
40
+
41
+ append_cppflags "-I$(srcdir)/../contrib/lz4/lib"
42
+
43
+ if RbConfig::CONFIG["arch"] =~ /mingw/
44
+ append_ldflags "-static-libgcc"
45
+ else
46
+ if try_compile(%q(__attribute__ ((visibility("default"))) void testfunc(void) { }))
47
+ if append_cflags "-fvisibility=hidden"
48
+ localsymbol = true
49
+ end
50
+ end
51
+ end
52
+
53
+ if localsymbol
54
+ $defs << %q('-DRBEXT_API=__attribute__ ((visibility("default")))') <<
55
+ %q(-DRBEXT_VISIBILITY=1)
56
+ else
57
+ $defs << %q(-DRBEXT_API=)
58
+ end
59
+
60
+ create_makefile File.join(RUBY_VERSION[/\d+\.\d+/], "extlz4")
@@ -0,0 +1,69 @@
1
+ #include "extlz4.h"
2
+ #include <lz4.h>
3
+
4
+ VALUE extlz4_eError;
5
+
6
+ /*
7
+ * version information
8
+ */
9
+
10
+ static VALUE
11
+ libver_major(VALUE ver)
12
+ {
13
+ return INT2FIX(LZ4_VERSION_MAJOR);
14
+ }
15
+
16
+ static VALUE
17
+ libver_minor(VALUE ver)
18
+ {
19
+ return INT2FIX(LZ4_VERSION_MINOR);
20
+ }
21
+
22
+ static VALUE
23
+ libver_release(VALUE ver)
24
+ {
25
+ return INT2FIX(LZ4_VERSION_RELEASE);
26
+ }
27
+
28
+ static VALUE
29
+ libver_to_s(VALUE ver)
30
+ {
31
+ return rb_sprintf("%d.%d.%d", LZ4_VERSION_MAJOR, LZ4_VERSION_MINOR, LZ4_VERSION_RELEASE);
32
+ }
33
+
34
+ /*
35
+ * initialize library
36
+ */
37
+
38
+ VALUE extlz4_mLZ4;
39
+
40
+ RBEXT_API void
41
+ Init_extlz4(void)
42
+ {
43
+ extlz4_mLZ4 = rb_define_module("LZ4");
44
+
45
+ /*
46
+ * Document-const: LZ4::LIBVERSION
47
+ *
48
+ * This constant value means api version number of original lz4 library as array'd integers.
49
+ *
50
+ * And it's has any singleton methods, so they are +#major+, +#minor+, +#release+ and +#to_s+.
51
+ *
52
+ * 実体が配列である理由は、比較を行いやすくすることを意図しているためです。
53
+ */
54
+ VALUE ver = rb_ary_new3(3,
55
+ INT2FIX(LZ4_VERSION_MAJOR),
56
+ INT2FIX(LZ4_VERSION_MINOR),
57
+ INT2FIX(LZ4_VERSION_RELEASE));
58
+ rb_define_singleton_method(ver, "major", libver_major, 0);
59
+ rb_define_singleton_method(ver, "minor", libver_minor, 0);
60
+ rb_define_singleton_method(ver, "release", libver_release, 0);
61
+ rb_define_singleton_method(ver, "to_s", libver_to_s, 0);
62
+ rb_obj_freeze(ver);
63
+ rb_define_const(extlz4_mLZ4, "LIBVERSION", ver);
64
+
65
+ extlz4_eError = rb_define_class_under(extlz4_mLZ4, "Error", rb_eRuntimeError);
66
+
67
+ extlz4_init_blockapi();
68
+ extlz4_init_frameapi();
69
+ }
@@ -0,0 +1,109 @@
1
+ #ifndef EXTLZ4_H
2
+ #define EXTLZ4_H 1
3
+
4
+ #include <ruby.h>
5
+ #include <ruby/thread.h>
6
+ #include <stdarg.h>
7
+ #include <stdlib.h>
8
+ #include <errno.h>
9
+
10
+ #ifndef RB_OBJ_FROZEN
11
+ # define RB_OBJ_FROZEN OBJ_FROZEN
12
+ #endif
13
+
14
+ extern VALUE extlz4_mLZ4; /* module LZ4 */
15
+ extern VALUE extlz4_eError; /* class LZ4::Error < ::RuntimeError */
16
+
17
+ extern void extlz4_init_blockapi(void);
18
+ extern void extlz4_init_frameapi(void);
19
+
20
+ #define AUX_FUNCALL(RECV, METHOD, ...) \
21
+ ({ \
22
+ VALUE args__[] = { __VA_ARGS__ }; \
23
+ rb_funcall2((RECV), (METHOD), \
24
+ sizeof(args__) / sizeof(args__[0]), args__); \
25
+ }) \
26
+
27
+ static inline void *
28
+ aux_thread_call_without_gvl(void *(*func)(va_list *), void (*cancel)(va_list *), ...)
29
+ {
30
+ va_list va1, va2;
31
+ va_start(va1, cancel);
32
+ va_start(va2, cancel);
33
+ void *s = rb_thread_call_without_gvl((void *(*)(void *))func, &va1, (void (*)(void *))cancel, &va2);
34
+ va_end(va1);
35
+ va_end(va2);
36
+ return s;
37
+ }
38
+
39
+ static inline void
40
+ aux_str_reserve(VALUE str, size_t size)
41
+ {
42
+ if (rb_str_capacity(str) < size) {
43
+ rb_str_modify_expand(str, size - RSTRING_LEN(str));
44
+ if (rb_str_capacity(str) < size) {
45
+ errno = ENOMEM;
46
+ rb_sys_fail("output buffer is not reallocated");
47
+ }
48
+ } else {
49
+ rb_str_modify(str);
50
+ }
51
+ }
52
+ /*
53
+ * rb_str_drop_bytes は共有オブジェクトを生成して返すので、
54
+ * それとは違う、破壊的メソッドとしての関数。
55
+ */
56
+ static inline VALUE
57
+ aux_str_drop_bytes(VALUE str, size_t dropsize)
58
+ {
59
+ char *p;
60
+ size_t size;
61
+ RSTRING_GETMEM(str, p, size);
62
+ if (dropsize > size) {
63
+ dropsize = size;
64
+ } else {
65
+ memmove(p, p + dropsize, size - dropsize);
66
+ }
67
+ rb_str_set_len(str, size - dropsize);
68
+ return str;
69
+ }
70
+
71
+ static inline char *
72
+ aux_str_getmem(VALUE str, char **ptr, size_t *size)
73
+ {
74
+ if (rb_type_p(str, RUBY_T_STRING)) {
75
+ RSTRING_GETMEM(str, *ptr, *size);
76
+ } else {
77
+ *ptr = NULL;
78
+ *size = 0;
79
+ }
80
+
81
+ return *ptr;
82
+ }
83
+
84
+ static inline void *
85
+ checkref(VALUE obj, void *p)
86
+ {
87
+ if (!p) {
88
+ rb_raise(rb_eRuntimeError,
89
+ "not initialized object or invalid reference - #<%s:%p>",
90
+ rb_obj_classname(obj), (void *)obj);
91
+ }
92
+ return p;
93
+ }
94
+
95
+ static inline void *
96
+ getrefp(VALUE obj, const rb_data_type_t *type)
97
+ {
98
+ void *p;
99
+ TypedData_Get_Struct(obj, void, type, p);
100
+ return p;
101
+ }
102
+
103
+ static inline void *
104
+ getref(VALUE obj, const rb_data_type_t *type)
105
+ {
106
+ return checkref(obj, getrefp(obj, type));
107
+ }
108
+
109
+ #endif /* !EXTLZ4_H */
@@ -0,0 +1,780 @@
1
+ #include "extlz4.h"
2
+ #include <lz4frame.h>
3
+ #include <lz4frame_static.h>
4
+ #include "hashargs.h"
5
+
6
+ static ID id_op_lshift;
7
+ static ID id_read;
8
+
9
+ enum {
10
+ FLAG_LEGACY = 1 << 0,
11
+ FLAG_BLOCK_LINK = 1 << 1,
12
+ FLAG_BLOCK_SUM = 1 << 2,
13
+ FLAG_STREAM_SIZE = 1 << 3,
14
+ FLAG_STREAM_SUM = 1 << 4,
15
+
16
+ WORK_BUFFER_SIZE = 256 * 1024, /* 256 KiB */
17
+ AUX_LZ4FRAME_HEADER_MAX = 19,
18
+
19
+ AUX_LZ4F_BLOCK_SIZE_MAX = 4 * 1024 * 1024, /* 4 MiB : maximum block size of LZ4 Frame */
20
+ AUX_LZ4F_FINISH_SIZE = 16, /* from lz4frame.c */
21
+
22
+ AUX_LZ4F_PARTIAL_READ_SIZE = 256 * 1024, /* 256 KiB */
23
+ };
24
+
25
+ /*** auxiliary and common functions ***/
26
+
27
+ static inline void
28
+ aux_lz4f_check_error(size_t err)
29
+ {
30
+ if (LZ4F_isError(err)) {
31
+ rb_raise(extlz4_eError,
32
+ "%s (0x%04x)",
33
+ LZ4F_getErrorName(err), -(int)err);
34
+ }
35
+ }
36
+
37
+ static void *
38
+ aux_LZ4F_compressUpdate_nogvl(va_list *p)
39
+ {
40
+ LZ4F_compressionContext_t encoder = va_arg(*p, LZ4F_compressionContext_t);
41
+ char *dest = va_arg(*p, char *);
42
+ size_t destsize = va_arg(*p, size_t);
43
+ const char *src = va_arg(*p, const char *);
44
+ size_t srcsize = va_arg(*p, size_t);
45
+ LZ4F_compressOptions_t *opts = va_arg(*p, LZ4F_compressOptions_t *);
46
+
47
+ return (void *)LZ4F_compressUpdate(encoder, dest, destsize, src, srcsize, opts);
48
+ }
49
+
50
+ static size_t
51
+ aux_LZ4F_compressUpdate(LZ4F_compressionContext_t encoder,
52
+ char *dest, size_t destsize, const char *src, size_t srcsize,
53
+ LZ4F_compressOptions_t *opts)
54
+ {
55
+ return (size_t)aux_thread_call_without_gvl(aux_LZ4F_compressUpdate_nogvl, NULL,
56
+ encoder, dest, destsize, src, srcsize, opts);
57
+ }
58
+
59
+ static int
60
+ aux_frame_level(const LZ4F_preferences_t *p)
61
+ {
62
+ return p->compressionLevel;
63
+ }
64
+
65
+ static int
66
+ aux_frame_blocksize(const LZ4F_frameInfo_t *info)
67
+ {
68
+ int bsid = info->blockSizeID;
69
+ if (bsid == 0) {
70
+ bsid = LZ4F_max4MB;
71
+ }
72
+ return 1 << (bsid * 2 + 8);
73
+ }
74
+
75
+ static int
76
+ aux_frame_blocklink(const LZ4F_frameInfo_t *info)
77
+ {
78
+ return info->blockMode == LZ4F_blockLinked;
79
+ }
80
+
81
+ static int
82
+ aux_frame_checksum(const LZ4F_frameInfo_t *info)
83
+ {
84
+ return info->contentChecksumFlag == LZ4F_contentChecksumEnabled;
85
+ }
86
+
87
+ /*** class LZ4::Encoder ***/
88
+
89
+ struct encoder
90
+ {
91
+ VALUE outport;
92
+ VALUE workbuf;
93
+ LZ4F_preferences_t prefs;
94
+ LZ4F_compressionContext_t encoder;
95
+ };
96
+
97
+ static void
98
+ encoder_mark(void *pp)
99
+ {
100
+ struct encoder *p = pp;
101
+ rb_gc_mark(p->outport);
102
+ rb_gc_mark(p->workbuf);
103
+ }
104
+
105
+ static void
106
+ encoder_free(void *pp)
107
+ {
108
+ struct encoder *p = pp;
109
+ if (p->encoder) {
110
+ LZ4F_freeCompressionContext(p->encoder);
111
+ }
112
+ memset(p, 0, sizeof(*p));
113
+ xfree(p);
114
+ }
115
+
116
+ static const rb_data_type_t encoder_type = {
117
+ .wrap_struct_name = "extlz4.LZ4.Encoder",
118
+ .function.dmark = encoder_mark,
119
+ .function.dfree = encoder_free,
120
+ /* .function.dsize = encoder_size, */
121
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
122
+ };
123
+
124
+ static VALUE
125
+ fenc_alloc(VALUE mod)
126
+ {
127
+ struct encoder *p;
128
+ VALUE obj = TypedData_Make_Struct(mod, struct encoder, &encoder_type, p);
129
+ p->outport = Qnil;
130
+ p->workbuf = Qnil;
131
+ return obj;
132
+ }
133
+
134
+ static inline int
135
+ fenc_init_args_blocksize(size_t size)
136
+ {
137
+ if (size == 0) {
138
+ return LZ4F_default;
139
+ } else if (size <= 64 * 1024) {
140
+ return LZ4F_max64KB;
141
+ } else if (size <= 256 * 1024) {
142
+ return LZ4F_max256KB;
143
+ } else if (size <= 1 * 1024 * 1024) {
144
+ return LZ4F_max1MB;
145
+ } else {
146
+ return LZ4F_max4MB;
147
+ }
148
+ }
149
+
150
+ static inline void
151
+ fenc_init_args(int argc, VALUE argv[], VALUE *outport, LZ4F_preferences_t *prefs)
152
+ {
153
+ VALUE level, opts;
154
+ rb_scan_args(argc, argv, "02:", outport, &level, &opts);
155
+
156
+ memset(prefs, 0, sizeof(*prefs));
157
+
158
+ if (NIL_P(*outport)) {
159
+ *outport = rb_str_buf_new(0);
160
+ }
161
+
162
+ prefs->compressionLevel = NIL_P(level) ? 1 : NUM2INT(level);
163
+
164
+ if (!NIL_P(opts)) {
165
+ VALUE blocksize, blocklink, checksum;
166
+ RBX_SCANHASH(opts, Qnil,
167
+ RBX_SCANHASH_ARGS("blocksize", &blocksize, Qnil),
168
+ RBX_SCANHASH_ARGS("blocklink", &blocklink, Qfalse),
169
+ RBX_SCANHASH_ARGS("checksum", &checksum, Qtrue));
170
+ // prefs->autoFlush = TODO;
171
+ prefs->frameInfo.blockSizeID = NIL_P(blocksize) ? LZ4F_default : fenc_init_args_blocksize(NUM2INT(blocksize));
172
+ prefs->frameInfo.blockMode = RTEST(blocklink) ? LZ4F_blockLinked : LZ4F_blockIndependent;
173
+ prefs->frameInfo.contentChecksumFlag = RTEST(checksum) ? LZ4F_contentChecksumEnabled : LZ4F_noContentChecksum;
174
+ } else {
175
+ prefs->frameInfo.blockSizeID = LZ4F_default;
176
+ prefs->frameInfo.blockMode = LZ4F_blockIndependent;
177
+ prefs->frameInfo.contentChecksumFlag = LZ4F_contentChecksumEnabled;
178
+ }
179
+ }
180
+
181
+ static struct encoder *
182
+ getencoderp(VALUE enc)
183
+ {
184
+ return getrefp(enc, &encoder_type);
185
+ }
186
+
187
+ static struct encoder *
188
+ getencoder(VALUE enc)
189
+ {
190
+ return getref(enc, &encoder_type);
191
+ }
192
+
193
+ /*
194
+ * call-seq:
195
+ * initialize(outport = "".b, level = 1, blocksize: nil, blocklink: false, checksum: true)
196
+ */
197
+ static VALUE
198
+ fenc_init(int argc, VALUE argv[], VALUE enc)
199
+ {
200
+ struct encoder *p = getencoder(enc);
201
+ VALUE outport;
202
+ fenc_init_args(argc, argv, &outport, &p->prefs);
203
+
204
+ LZ4F_errorCode_t status;
205
+ status = LZ4F_createCompressionContext(&p->encoder, LZ4F_VERSION);
206
+ aux_lz4f_check_error(status);
207
+ p->workbuf = rb_str_buf_new(AUX_LZ4F_BLOCK_SIZE_MAX);
208
+ size_t s = LZ4F_compressBegin(p->encoder, RSTRING_PTR(p->workbuf), rb_str_capacity(p->workbuf), &p->prefs);
209
+ aux_lz4f_check_error(s);
210
+ rb_str_set_len(p->workbuf, s);
211
+ rb_funcall2(outport, id_op_lshift, 1, &p->workbuf);
212
+ p->outport = outport;
213
+ rb_obj_infect(p->outport, enc);
214
+ return enc;
215
+ }
216
+
217
+ static inline void
218
+ fenc_update(struct encoder *p, VALUE src, LZ4F_compressOptions_t *opts)
219
+ {
220
+ rb_check_type(src, RUBY_T_STRING);
221
+ const char *srcp = RSTRING_PTR(src);
222
+ const char *srctail = srcp + RSTRING_LEN(src);
223
+ while (srcp < srctail) {
224
+ size_t srcsize = srctail - srcp;
225
+ if (srcsize > AUX_LZ4F_BLOCK_SIZE_MAX) { srcsize = AUX_LZ4F_BLOCK_SIZE_MAX; }
226
+ size_t destsize = LZ4F_compressBound(srcsize, &p->prefs);
227
+ aux_str_reserve(p->workbuf, destsize);
228
+ char *destp = RSTRING_PTR(p->workbuf);
229
+ size_t size = aux_LZ4F_compressUpdate(p->encoder, destp, destsize, srcp, srcsize, opts);
230
+ aux_lz4f_check_error(size);
231
+ rb_str_set_len(p->workbuf, size);
232
+ rb_funcall2(p->outport, id_op_lshift, 1, &p->workbuf);
233
+ srcp += srcsize;
234
+ }
235
+ }
236
+
237
+ /*
238
+ * call-seq:
239
+ * write(src) -> self
240
+ */
241
+ static VALUE
242
+ fenc_write(int argc, VALUE argv[], VALUE enc)
243
+ {
244
+ struct encoder *p = getencoder(enc);
245
+ VALUE src;
246
+ rb_scan_args(argc, argv, "1", &src);
247
+ rb_obj_infect(enc, src);
248
+ rb_obj_infect(enc, p->workbuf);
249
+ rb_obj_infect(p->outport, enc);
250
+ fenc_update(p, src, NULL);
251
+ return enc;
252
+ }
253
+
254
+ static VALUE
255
+ fenc_push(VALUE enc, VALUE src)
256
+ {
257
+ struct encoder *p = getencoder(enc);
258
+ rb_obj_infect(enc, src);
259
+ rb_obj_infect(enc, p->workbuf);
260
+ rb_obj_infect(p->outport, enc);
261
+ fenc_update(p, src, NULL);
262
+ return enc;
263
+ }
264
+
265
+ static VALUE
266
+ fenc_flush(VALUE enc)
267
+ {
268
+ struct encoder *p = getencoder(enc);
269
+ size_t destsize = AUX_LZ4F_BLOCK_SIZE_MAX + AUX_LZ4F_FINISH_SIZE;
270
+ aux_str_reserve(p->workbuf, destsize);
271
+ char *destp = RSTRING_PTR(p->workbuf);
272
+ size_t size = LZ4F_flush(p->encoder, destp, destsize, NULL);
273
+ aux_lz4f_check_error(size);
274
+ rb_str_set_len(p->workbuf, size);
275
+ rb_funcall2(p->outport, id_op_lshift, 1, &p->workbuf);
276
+
277
+ return enc;
278
+ }
279
+
280
+ static VALUE
281
+ fenc_close(VALUE enc)
282
+ {
283
+ struct encoder *p = getencoder(enc);
284
+ size_t destsize = AUX_LZ4F_BLOCK_SIZE_MAX + AUX_LZ4F_FINISH_SIZE;
285
+ aux_str_reserve(p->workbuf, destsize);
286
+ char *destp = RSTRING_PTR(p->workbuf);
287
+ size_t size = LZ4F_compressEnd(p->encoder, destp, destsize, NULL);
288
+ aux_lz4f_check_error(size);
289
+ rb_str_set_len(p->workbuf, size);
290
+ rb_funcall2(p->outport, id_op_lshift, 1, &p->workbuf);
291
+
292
+ return enc;
293
+ }
294
+
295
+ static VALUE
296
+ fenc_getoutport(VALUE enc)
297
+ {
298
+ return getencoder(enc)->outport;
299
+ }
300
+
301
+ static VALUE
302
+ fenc_setoutport(VALUE enc, VALUE outport)
303
+ {
304
+ return getencoder(enc)->outport = outport;
305
+ }
306
+
307
+ static VALUE
308
+ fenc_prefs_level(VALUE enc)
309
+ {
310
+ return INT2NUM(aux_frame_level(&getencoder(enc)->prefs));
311
+ }
312
+
313
+ static int
314
+ fenc_blocksize(struct encoder *p)
315
+ {
316
+ return aux_frame_blocksize(&p->prefs.frameInfo);
317
+ }
318
+
319
+ static VALUE
320
+ fenc_prefs_blocksize(VALUE enc)
321
+ {
322
+ return INT2NUM(fenc_blocksize(getencoder(enc)));
323
+ }
324
+
325
+ static VALUE
326
+ fenc_prefs_blocklink(VALUE enc)
327
+ {
328
+ return aux_frame_blocklink(&getencoder(enc)->prefs.frameInfo) ? Qtrue : Qfalse;
329
+ }
330
+
331
+ static VALUE
332
+ fenc_prefs_checksum(VALUE enc)
333
+ {
334
+ return aux_frame_checksum(&getencoder(enc)->prefs.frameInfo) ? Qtrue : Qfalse;
335
+ }
336
+
337
+ static VALUE
338
+ fenc_inspect(VALUE enc)
339
+ {
340
+ struct encoder *p = getencoderp(enc);
341
+ if (p) {
342
+ return rb_sprintf("#<%s:%p outport=#<%s:%p>, level=%d, blocksize=%d, blocklink=%s, checksum=%s>",
343
+ rb_obj_classname(enc), (void *)enc,
344
+ rb_obj_classname(p->outport), (void *)p->outport,
345
+ p->prefs.compressionLevel, fenc_blocksize(p),
346
+ aux_frame_blocklink(&p->prefs.frameInfo) ? "true" : "false",
347
+ aux_frame_checksum(&p->prefs.frameInfo) ? "true" : "false");
348
+ } else {
349
+ return rb_sprintf("#<%s:%p **INVALID REFERENCE**>",
350
+ rb_obj_classname(enc), (void *)enc);
351
+ }
352
+ }
353
+
354
+ /*** class LZ4::Decoder ***/
355
+
356
+ struct decoder
357
+ {
358
+ VALUE inport;
359
+ VALUE readbuf; /* read buffer from inport */
360
+ VALUE inbuf;
361
+ VALUE outbuf;
362
+ size_t outoff; /* offset in outbuf */
363
+ size_t status; /* status code of LZ4F_decompress */
364
+ LZ4F_frameInfo_t info;
365
+ LZ4F_decompressionContext_t decoder;
366
+ };
367
+
368
+ static void
369
+ decoder_mark(void *pp)
370
+ {
371
+ struct decoder *p = pp;
372
+ rb_gc_mark(p->inport);
373
+ rb_gc_mark(p->readbuf);
374
+ rb_gc_mark(p->inbuf);
375
+ rb_gc_mark(p->outbuf);
376
+ }
377
+
378
+ static void
379
+ decoder_free(void *pp)
380
+ {
381
+ struct decoder *p = pp;
382
+ if (p->decoder) {
383
+ LZ4F_freeDecompressionContext(p->decoder);
384
+ }
385
+ memset(p, 0, sizeof(*p));
386
+ xfree(p);
387
+ }
388
+
389
+ static const rb_data_type_t decoder_type = {
390
+ .wrap_struct_name = "extlz4.LZ4.Decoder",
391
+ .function.dmark = decoder_mark,
392
+ .function.dfree = decoder_free,
393
+ /* .function.dsize = decoder_size, */
394
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
395
+ };
396
+
397
+ static VALUE
398
+ fdec_alloc(VALUE mod)
399
+ {
400
+ struct decoder *p;
401
+ VALUE obj = TypedData_Make_Struct(mod, struct decoder, &decoder_type, p);
402
+ p->inport = Qnil;
403
+ p->readbuf = Qnil;
404
+ p->inbuf = Qnil;
405
+ p->outbuf = Qnil;
406
+ p->outoff = 0;
407
+ p->status = 0;
408
+ return obj;
409
+ }
410
+
411
+ static struct decoder *
412
+ getdecoderp(VALUE dec)
413
+ {
414
+ return getrefp(dec, &decoder_type);
415
+ }
416
+
417
+ static struct decoder *
418
+ getdecoder(VALUE dec)
419
+ {
420
+ return getref(dec, &decoder_type);
421
+ }
422
+
423
+ static inline VALUE
424
+ aux_read(VALUE obj, size_t size, VALUE buf)
425
+ {
426
+ if (NIL_P(buf) || RB_OBJ_FROZEN(buf)) {
427
+ buf = rb_str_buf_new(size);
428
+ }
429
+
430
+ if (NIL_P(AUX_FUNCALL(obj, id_read, SIZET2NUM(size), buf))) {
431
+ return Qnil;
432
+ } else {
433
+ if (RSTRING_LEN(buf) > size) {
434
+ rb_raise(rb_eRuntimeError, "most read (%d, but expected to %d)", (int)RSTRING_LEN(buf), (int)size);
435
+ }
436
+ return buf;
437
+ }
438
+ }
439
+
440
+ /*
441
+ * call-seq:
442
+ * initialize(inport) -> self
443
+ *
444
+ * [inport]
445
+ * An I/O (liked) object for data read from LZ4 Frame.
446
+ *
447
+ * This object need +.read+ method.
448
+ */
449
+ static VALUE
450
+ fdec_init(int argc, VALUE argv[], VALUE dec)
451
+ {
452
+ struct decoder *p = getdecoder(dec);
453
+ VALUE inport;
454
+ //VALUE readblocksize;
455
+ rb_scan_args(argc, argv, "1", &inport);
456
+ LZ4F_errorCode_t err = LZ4F_createDecompressionContext(&p->decoder, LZ4F_VERSION);
457
+ aux_lz4f_check_error(err);
458
+ rb_obj_infect(dec, inport);
459
+ p->inport = inport;
460
+ p->readbuf = rb_str_buf_new(0);
461
+ char *readp;
462
+ size_t readsize;
463
+ size_t zero = 0;
464
+ size_t s = 4; /* magic number size of lz4 frame */
465
+ int i;
466
+ for (i = 0; i < 2; i ++) {
467
+ /*
468
+ * first step: check magic number
469
+ * second step: check frame information
470
+ */
471
+ aux_read(inport, s, p->readbuf);
472
+ aux_str_getmem(p->readbuf, &readp, &readsize);
473
+ if (!readp || readsize < s) {
474
+ rb_raise(extlz4_eError,
475
+ "unexpected EOF (read error) - #<%s:%p>",
476
+ rb_obj_classname(inport), (const void *)inport);
477
+ }
478
+ s = LZ4F_decompress(p->decoder, NULL, &zero, readp, &readsize, NULL);
479
+ aux_lz4f_check_error(s);
480
+ }
481
+ p->status = s;
482
+ s = LZ4F_getFrameInfo(p->decoder, &p->info, NULL, &zero);
483
+ aux_lz4f_check_error(s);
484
+ p->outbuf = rb_str_tmp_new(1 << (8 + p->info.blockSizeID * 2));
485
+ rb_str_set_len(p->outbuf, 0);
486
+
487
+ return dec;
488
+ }
489
+
490
+ static inline void
491
+ fdec_read_args(int argc, VALUE argv[], size_t *size, VALUE *buf)
492
+ {
493
+ switch (argc) {
494
+ case 0:
495
+ *size = ~(size_t)0;
496
+ *buf = rb_str_buf_new(0);
497
+ break;
498
+ case 1:
499
+ *size = NUM2SIZET(argv[0]);
500
+ *buf = rb_str_buf_new(*size);
501
+ break;
502
+ case 2:
503
+ *size = NUM2SIZET(argv[0]);
504
+ *buf = argv[1];
505
+ rb_check_type(*buf, RUBY_T_STRING);
506
+ rb_str_modify(*buf);
507
+ rb_str_set_len(*buf, 0);
508
+ break;
509
+ default:
510
+ rb_error_arity(argc, 0, 2);
511
+ }
512
+ }
513
+
514
+ static void
515
+ fdec_read_fetch(VALUE dec, struct decoder *p)
516
+ {
517
+ if (NIL_P(p->inbuf)) {
518
+ p->inbuf = rb_str_tmp_new(256);
519
+ rb_str_set_len(p->inbuf, 0);
520
+ }
521
+
522
+ while (RSTRING_LEN(p->inbuf) < p->status) {
523
+ p->readbuf = aux_read(p->inport, p->status - RSTRING_LEN(p->inbuf), p->readbuf);
524
+ if (NIL_P(p->readbuf)) {
525
+ rb_raise(rb_eRuntimeError,
526
+ "unexpected EOF (read error) - #<%s:%p>",
527
+ rb_obj_classname(p->inport), (const void *)p->inport);
528
+ }
529
+ rb_check_type(p->readbuf, RUBY_T_STRING);
530
+ rb_str_buf_cat(p->inbuf, RSTRING_PTR(p->readbuf), RSTRING_LEN(p->readbuf));
531
+ }
532
+
533
+ char *inp;
534
+ size_t insize;
535
+ aux_str_getmem(p->inbuf, &inp, &insize);
536
+ char *outp = RSTRING_PTR(p->outbuf);
537
+ size_t outsize = rb_str_capacity(p->outbuf);
538
+ p->status = LZ4F_decompress(p->decoder, outp, &outsize, inp, &insize, NULL);
539
+ aux_lz4f_check_error(p->status);
540
+ memmove(RSTRING_PTR(p->inbuf), RSTRING_PTR(p->inbuf) + insize, RSTRING_LEN(p->inbuf) - insize);
541
+ rb_str_set_len(p->inbuf, RSTRING_LEN(p->inbuf) - insize);
542
+ rb_str_set_len(p->outbuf, outsize);
543
+ p->outoff = 0;
544
+ rb_thread_check_ints();
545
+ }
546
+
547
+ static size_t
548
+ fdec_read_decode(VALUE dec, struct decoder *p, char *dest, size_t size)
549
+ {
550
+ const char *const desthead = dest;
551
+ uintptr_t desttail = (uintptr_t)dest + size;
552
+
553
+ while ((uintptr_t)dest < desttail) {
554
+ if ((ssize_t)p->status < 1 && RSTRING_LEN(p->outbuf) < 1) {
555
+ break;
556
+ }
557
+
558
+ if (p->status > 0 && p->outoff >= RSTRING_LEN(p->outbuf)) {
559
+ fdec_read_fetch(dec, p);
560
+ }
561
+
562
+ if (size < RSTRING_LEN(p->outbuf) - p->outoff) {
563
+ memcpy(dest, RSTRING_PTR(p->outbuf) + p->outoff, size);
564
+ p->outoff += size;
565
+ dest += size;
566
+ break;
567
+ } else {
568
+ size_t s = RSTRING_LEN(p->outbuf) - p->outoff;
569
+ memcpy(dest, RSTRING_PTR(p->outbuf) + p->outoff, s);
570
+ p->outoff = 0;
571
+ rb_str_set_len(p->outbuf, 0);
572
+ dest += s;
573
+ size -= s;
574
+ }
575
+ }
576
+
577
+ return dest - desthead;
578
+ }
579
+
580
+ /*
581
+ * call-seq:
582
+ * read -> string
583
+ * read(size) -> string
584
+ * read(size, buffer) -> buffer
585
+ */
586
+ static VALUE
587
+ fdec_read(int argc, VALUE argv[], VALUE dec)
588
+ {
589
+ struct decoder *p = getdecoder(dec);
590
+ size_t size;
591
+ VALUE dest;
592
+ fdec_read_args(argc, argv, &size, &dest);
593
+ if (size == 0) {
594
+ rb_obj_infect(dest, dec);
595
+ return dest;
596
+ }
597
+
598
+ if (p->status == 0) {
599
+ return Qnil;
600
+ }
601
+
602
+ if ((ssize_t)size > 0) {
603
+ char *destp = RSTRING_PTR(dest);
604
+ size_t destsize = size;
605
+ destsize = fdec_read_decode(dec, p, destp, destsize);
606
+ rb_str_set_len(dest, destsize);
607
+ } else {
608
+ const size_t tmpsize = 4 * 1024 * 1024;
609
+ VALUE tmpbuf = rb_str_tmp_new(tmpsize);
610
+ char *tmp = RSTRING_PTR(tmpbuf);
611
+ size_t s;
612
+ rb_str_set_len(dest, 0);
613
+ while ((s = fdec_read_decode(dec, p, tmp, tmpsize)) > 0) {
614
+ rb_str_buf_cat(dest, tmp, s);
615
+ }
616
+ rb_str_resize(tmpbuf, 0);
617
+ }
618
+
619
+ rb_obj_infect(dest, dec);
620
+
621
+ if (RSTRING_LEN(dest) > 0) {
622
+ return dest;
623
+ } else {
624
+ return Qnil;
625
+ }
626
+ }
627
+
628
+ /*
629
+ * call-seq:
630
+ * getc -> String | nil
631
+ *
632
+ * Read one byte character.
633
+ */
634
+ static VALUE
635
+ fdec_getc(VALUE dec)
636
+ {
637
+ struct decoder *p = getdecoder(dec);
638
+
639
+ if (p->status == 0) {
640
+ return Qnil;
641
+ }
642
+
643
+ char ch;
644
+ size_t s = fdec_read_decode(dec, p, &ch, 1);
645
+ if (s > 0) {
646
+ VALUE v = rb_str_new(&ch, 1);
647
+ rb_obj_infect(v, dec);
648
+ return v;
649
+ } else {
650
+ return Qnil;
651
+ }
652
+ }
653
+
654
+ /*
655
+ * call-seq:
656
+ * getbyte -> Integer | nil
657
+ *
658
+ * Read one byte code integer.
659
+ */
660
+ static VALUE
661
+ fdec_getbyte(VALUE dec)
662
+ {
663
+ struct decoder *p = getdecoder(dec);
664
+
665
+ char ch;
666
+ size_t s = fdec_read_decode(dec, p, &ch, 1);
667
+ if (s > 0) {
668
+ return INT2FIX(ch);
669
+ } else {
670
+ return Qnil;
671
+ }
672
+ }
673
+
674
+ static VALUE
675
+ fdec_close(VALUE dec)
676
+ {
677
+ struct decoder *p = getdecoder(dec);
678
+ p->status = 0;
679
+ // TODO: destroy decoder
680
+ return dec;
681
+ }
682
+
683
+ static VALUE
684
+ fdec_eof(VALUE dec)
685
+ {
686
+ struct decoder *p = getdecoder(dec);
687
+ if (p->status == 0) {
688
+ return Qtrue;
689
+ } else {
690
+ return Qfalse;
691
+ }
692
+ }
693
+
694
+ static VALUE
695
+ fdec_inport(VALUE dec)
696
+ {
697
+ return getdecoder(dec)->inport;
698
+ }
699
+
700
+ static int
701
+ fdec_blocksize(struct decoder *p)
702
+ {
703
+ return aux_frame_blocksize(&p->info);
704
+ }
705
+
706
+ static VALUE
707
+ fdec_prefs_blocksize(VALUE dec)
708
+ {
709
+ return INT2NUM(fdec_blocksize(getdecoder(dec)));
710
+ }
711
+
712
+ static VALUE
713
+ fdec_prefs_blocklink(VALUE dec)
714
+ {
715
+ return aux_frame_blocklink(&getdecoder(dec)->info) ? Qtrue : Qfalse;
716
+ }
717
+
718
+ static VALUE
719
+ fdec_prefs_checksum(VALUE dec)
720
+ {
721
+ return aux_frame_checksum(&getdecoder(dec)->info) ? Qtrue : Qfalse;
722
+ }
723
+
724
+ static VALUE
725
+ fdec_inspect(VALUE dec)
726
+ {
727
+ struct decoder *p = getdecoderp(dec);
728
+ if (p) {
729
+ return rb_sprintf("#<%s:%p inport=#<%s:%p>, blocksize=%d, blocklink=%s, checksum=%s>",
730
+ rb_obj_classname(dec), (void *)dec,
731
+ rb_obj_classname(p->inport), (void *)p->inport,
732
+ fdec_blocksize(p),
733
+ aux_frame_blocklink(&p->info) ? "true" : "false",
734
+ aux_frame_checksum(&p->info) ? "true" : "false");
735
+ } else {
736
+ return rb_sprintf("#<%s:%p **INVALID REFERENCE**>",
737
+ rb_obj_classname(dec), (void *)dec);
738
+ }
739
+ }
740
+
741
+ /*** setup for LZ4::Encoder and LZ4::Decoder ***/
742
+
743
+ void
744
+ extlz4_init_frameapi(void)
745
+ {
746
+ id_op_lshift = rb_intern("<<");
747
+ id_read = rb_intern("read");
748
+
749
+ VALUE cEncoder = rb_define_class_under(extlz4_mLZ4, "Encoder", rb_cObject);
750
+ rb_define_alloc_func(cEncoder, fenc_alloc);
751
+ rb_define_method(cEncoder, "initialize", RUBY_METHOD_FUNC(fenc_init), -1);
752
+ rb_define_method(cEncoder, "write", RUBY_METHOD_FUNC(fenc_write), -1);
753
+ rb_define_method(cEncoder, "<<", RUBY_METHOD_FUNC(fenc_push), 1);
754
+ rb_define_method(cEncoder, "flush", RUBY_METHOD_FUNC(fenc_flush), 0);
755
+ rb_define_method(cEncoder, "close", RUBY_METHOD_FUNC(fenc_close), 0);
756
+ rb_define_alias(cEncoder, "finish", "close");
757
+ rb_define_method(cEncoder, "outport", RUBY_METHOD_FUNC(fenc_getoutport), 0);
758
+ rb_define_method(cEncoder, "outport=", RUBY_METHOD_FUNC(fenc_setoutport), 1);
759
+ rb_define_method(cEncoder, "prefs_level", RUBY_METHOD_FUNC(fenc_prefs_level), 0);
760
+ rb_define_method(cEncoder, "prefs_blocksize", RUBY_METHOD_FUNC(fenc_prefs_blocksize), 0);
761
+ rb_define_method(cEncoder, "prefs_blocklink", RUBY_METHOD_FUNC(fenc_prefs_blocklink), 0);
762
+ rb_define_method(cEncoder, "prefs_checksum", RUBY_METHOD_FUNC(fenc_prefs_checksum), 0);
763
+ rb_define_method(cEncoder, "inspect", RUBY_METHOD_FUNC(fenc_inspect), 0);
764
+
765
+ VALUE cDecoder = rb_define_class_under(extlz4_mLZ4, "Decoder", rb_cObject);
766
+ rb_define_alloc_func(cDecoder, fdec_alloc);
767
+ rb_define_method(cDecoder, "initialize", RUBY_METHOD_FUNC(fdec_init), -1);
768
+ rb_define_method(cDecoder, "read", RUBY_METHOD_FUNC(fdec_read), -1);
769
+ rb_define_method(cDecoder, "getc", RUBY_METHOD_FUNC(fdec_getc), 0);
770
+ rb_define_method(cDecoder, "getbyte", RUBY_METHOD_FUNC(fdec_getbyte), 0);
771
+ rb_define_method(cDecoder, "close", RUBY_METHOD_FUNC(fdec_close), 0);
772
+ rb_define_alias(cDecoder, "finish", "close");
773
+ rb_define_method(cDecoder, "eof", RUBY_METHOD_FUNC(fdec_eof), 0);
774
+ rb_define_method(cDecoder, "inport", RUBY_METHOD_FUNC(fdec_inport), 0);
775
+ rb_define_alias(cDecoder, "eof?", "eof");
776
+ rb_define_method(cDecoder, "prefs_blocksize", RUBY_METHOD_FUNC(fdec_prefs_blocksize), 0);
777
+ rb_define_method(cDecoder, "prefs_blocklink", RUBY_METHOD_FUNC(fdec_prefs_blocklink), 0);
778
+ rb_define_method(cDecoder, "prefs_checksum", RUBY_METHOD_FUNC(fdec_prefs_checksum), 0);
779
+ rb_define_method(cDecoder, "inspect", RUBY_METHOD_FUNC(fdec_inspect), 0);
780
+ }