extlz4 0.2.4.2

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,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
+ }