extbrotli 0.0.1.PROTOTYPE

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +28 -0
  3. data/README.md +67 -0
  4. data/Rakefile +158 -0
  5. data/contrib/brotli/LICENSE +202 -0
  6. data/contrib/brotli/README.md +18 -0
  7. data/contrib/brotli/dec/bit_reader.c +55 -0
  8. data/contrib/brotli/dec/bit_reader.h +256 -0
  9. data/contrib/brotli/dec/context.h +260 -0
  10. data/contrib/brotli/dec/decode.c +1573 -0
  11. data/contrib/brotli/dec/decode.h +160 -0
  12. data/contrib/brotli/dec/dictionary.h +9494 -0
  13. data/contrib/brotli/dec/huffman.c +325 -0
  14. data/contrib/brotli/dec/huffman.h +77 -0
  15. data/contrib/brotli/dec/port.h +148 -0
  16. data/contrib/brotli/dec/prefix.h +756 -0
  17. data/contrib/brotli/dec/state.c +149 -0
  18. data/contrib/brotli/dec/state.h +185 -0
  19. data/contrib/brotli/dec/streams.c +99 -0
  20. data/contrib/brotli/dec/streams.h +100 -0
  21. data/contrib/brotli/dec/transform.h +315 -0
  22. data/contrib/brotli/dec/types.h +36 -0
  23. data/contrib/brotli/enc/backward_references.cc +769 -0
  24. data/contrib/brotli/enc/backward_references.h +50 -0
  25. data/contrib/brotli/enc/bit_cost.h +147 -0
  26. data/contrib/brotli/enc/block_splitter.cc +418 -0
  27. data/contrib/brotli/enc/block_splitter.h +78 -0
  28. data/contrib/brotli/enc/brotli_bit_stream.cc +884 -0
  29. data/contrib/brotli/enc/brotli_bit_stream.h +149 -0
  30. data/contrib/brotli/enc/cluster.h +290 -0
  31. data/contrib/brotli/enc/command.h +140 -0
  32. data/contrib/brotli/enc/context.h +185 -0
  33. data/contrib/brotli/enc/dictionary.h +9485 -0
  34. data/contrib/brotli/enc/dictionary_hash.h +4125 -0
  35. data/contrib/brotli/enc/encode.cc +715 -0
  36. data/contrib/brotli/enc/encode.h +196 -0
  37. data/contrib/brotli/enc/encode_parallel.cc +354 -0
  38. data/contrib/brotli/enc/encode_parallel.h +37 -0
  39. data/contrib/brotli/enc/entropy_encode.cc +492 -0
  40. data/contrib/brotli/enc/entropy_encode.h +88 -0
  41. data/contrib/brotli/enc/fast_log.h +179 -0
  42. data/contrib/brotli/enc/find_match_length.h +87 -0
  43. data/contrib/brotli/enc/hash.h +686 -0
  44. data/contrib/brotli/enc/histogram.cc +76 -0
  45. data/contrib/brotli/enc/histogram.h +100 -0
  46. data/contrib/brotli/enc/literal_cost.cc +172 -0
  47. data/contrib/brotli/enc/literal_cost.h +38 -0
  48. data/contrib/brotli/enc/metablock.cc +544 -0
  49. data/contrib/brotli/enc/metablock.h +88 -0
  50. data/contrib/brotli/enc/port.h +151 -0
  51. data/contrib/brotli/enc/prefix.h +85 -0
  52. data/contrib/brotli/enc/ringbuffer.h +108 -0
  53. data/contrib/brotli/enc/static_dict.cc +441 -0
  54. data/contrib/brotli/enc/static_dict.h +40 -0
  55. data/contrib/brotli/enc/static_dict_lut.h +12063 -0
  56. data/contrib/brotli/enc/streams.cc +127 -0
  57. data/contrib/brotli/enc/streams.h +129 -0
  58. data/contrib/brotli/enc/transform.h +250 -0
  59. data/contrib/brotli/enc/write_bits.h +91 -0
  60. data/ext/extbrotli.cc +24 -0
  61. data/ext/extbrotli.h +73 -0
  62. data/ext/extconf.rb +35 -0
  63. data/ext/lldecoder.c +220 -0
  64. data/ext/llencoder.cc +433 -0
  65. data/gemstub.rb +21 -0
  66. data/lib/extbrotli.rb +243 -0
  67. data/lib/extbrotli/version.rb +3 -0
  68. metadata +140 -0
data/ext/llencoder.cc ADDED
@@ -0,0 +1,433 @@
1
+ #include "extbrotli.h"
2
+ #include <functional>
3
+ #include <brotli/enc/encode.h>
4
+
5
+ static inline size_t
6
+ aux_brotli_compress_bound(size_t srcsize)
7
+ {
8
+ return srcsize * 1.2 + 10240;
9
+ }
10
+
11
+ static inline size_t
12
+ rb_num2sizet(VALUE n)
13
+ {
14
+ #if SIZEOF_SIZE_T > SIZEOF_LONG && defined(SIZEOF_LONG_LONG)
15
+ return (size_t)NUM2ULL(n);
16
+ #else
17
+ return (size_t)NUM2ULONG(n);
18
+ #endif
19
+ }
20
+
21
+ static inline size_t
22
+ rb_num2ssizet(VALUE n)
23
+ {
24
+ #if SIZEOF_SIZE_T > SIZEOF_LONG && defined(SIZEOF_LONG_LONG)
25
+ return (ssize_t)NUM2LL(n);
26
+ #else
27
+ return (ssize_t)NUM2LONG(n);
28
+ #endif
29
+ }
30
+
31
+ struct aux_ruby_jump_tag
32
+ {
33
+ int tag;
34
+
35
+ aux_ruby_jump_tag(int tag) : tag(tag) { }
36
+
37
+ static void check_and_throw(int tag)
38
+ {
39
+ if (tag) {
40
+ throw aux_ruby_jump_tag(tag);
41
+ }
42
+ }
43
+ };
44
+
45
+ #if 0 && __cplusplus >= 201103L
46
+ static VALUE
47
+ aux_throw_ruby_exception_protected(VALUE pp)
48
+ {
49
+ const std::function<void ()> &func = *reinterpret_cast<const std::function<void (void)> *>(pp);
50
+ func();
51
+ return 0;
52
+ }
53
+
54
+ template <typename T>
55
+ static inline T
56
+ aux_throw_ruby_exception(const std::function<T (void)> &func)
57
+ {
58
+ int tag;
59
+ VALUE s;
60
+ const std::function<void (void)> lambda = [&] { s = func(); };
61
+ rb_protect(aux_throw_ruby_exception_protected, (VALUE)&lambda, &tag);
62
+ aux_ruby_jump_tag::check_and_throw(tag);
63
+ return s;
64
+ }
65
+
66
+ static inline void
67
+ aux_throw_ruby_exception(const std::function<void (void)> &func)
68
+ {
69
+ int tag;
70
+ const std::function<void (void)> lambda = [&] { func(); };
71
+ rb_protect(aux_throw_ruby_exception_protected, (VALUE)&lambda, &tag);
72
+ aux_ruby_jump_tag::check_and_throw(tag);
73
+ }
74
+
75
+ static inline void
76
+ aux_raise_stub(VALUE exc, const char *fmt, ...)
77
+ {
78
+ va_list args;
79
+ VALUE mesg;
80
+
81
+ va_start(args, fmt);
82
+ mesg = rb_vsprintf(fmt, args);
83
+ va_end(args);
84
+ throw rb_exc_new3(exc, mesg);
85
+ }
86
+
87
+ #define rb_check_type(...) \
88
+ aux_throw_ruby_exception([&] { rb_check_type(__VA_ARGS__); }) \
89
+
90
+ #define rb_str_buf_new(...) \
91
+ aux_throw_ruby_exception<VALUE>([&] { return rb_str_buf_new(__VA_ARGS__); }) \
92
+
93
+ #define rb_num2sizet(...) \
94
+ aux_throw_ruby_exception<size_t>([&] { return rb_num2sizet(__VA_ARGS__); }) \
95
+
96
+ #endif
97
+
98
+ static VALUE cLLEncoder;
99
+
100
+ using namespace std;
101
+ using namespace brotli;
102
+
103
+ static int
104
+ getoption_int(VALUE opts, ID key, int min, int max, int default_value)
105
+ {
106
+ VALUE v = rb_hash_lookup(opts, ID2SYM(key));
107
+ if (NIL_P(v)) { return default_value; }
108
+ int n = NUM2INT(v);
109
+ if (n >= min && (n <= max || max <= min)) { return n; }
110
+ if (max > min) {
111
+ rb_raise(rb_eArgError,
112
+ "option ``%s'' is out of range (%d for %d .. %d)",
113
+ rb_id2name(key), n, min, max);
114
+ } else {
115
+ rb_raise(rb_eArgError,
116
+ "option ``%s'' is out of range (%d for %d+)",
117
+ rb_id2name(key), n, min);
118
+ }
119
+ }
120
+
121
+ static void
122
+ setup_encode_params(VALUE opts, BrotliParams &params, VALUE &predict)
123
+ {
124
+ static const ID id_predict = rb_intern("predict");
125
+ static const ID id_mode = rb_intern("mode");
126
+ static const ID id_quality = rb_intern("quality");
127
+ static const ID id_lgwin = rb_intern("lgwin");
128
+ static const ID id_lgblock = rb_intern("lgblock");
129
+
130
+ if (NIL_P(opts)) {
131
+ predict = Qnil;
132
+ params.quality = 9; // FIXME: changed from 11 as constructor.
133
+ return;
134
+ }
135
+ rb_check_type(opts, RUBY_T_HASH);
136
+
137
+ predict = rb_hash_lookup(opts, ID2SYM(id_predict));
138
+ if (!NIL_P(predict)) { rb_check_type(predict, RUBY_T_STRING); }
139
+
140
+ params.mode = (BrotliParams::Mode)getoption_int(opts, id_mode, BrotliParams::MODE_GENERIC, BrotliParams::MODE_FONT, BrotliParams::MODE_GENERIC);
141
+ params.quality = getoption_int(opts, id_quality, 0, 11, 9);
142
+ params.lgwin = getoption_int(opts, id_lgwin, kMinWindowBits, kMaxWindowBits, 22);
143
+ params.lgblock = getoption_int(opts, id_lgblock, kMinInputBlockBits, kMaxInputBlockBits, 0);
144
+ }
145
+
146
+ static void
147
+ llenc_free(void *pp)
148
+ {
149
+ if (pp) {
150
+ BrotliCompressor *p = (BrotliCompressor *)pp;
151
+ delete p;
152
+ }
153
+ }
154
+
155
+ static const rb_data_type_t llencoder_type = {
156
+ /* .wrap_struct_name = */ "extbrotli.lowlevelencoder",
157
+ /* .function.dmark = */ NULL,
158
+ /* .function.dfree = */ llenc_free,
159
+ /* .function.dsize = */ NULL,
160
+ };
161
+
162
+ static inline BrotliCompressor *
163
+ getencoderp(VALUE obj)
164
+ {
165
+ return (BrotliCompressor *)getrefp(obj, &llencoder_type);
166
+ }
167
+
168
+ static inline BrotliCompressor *
169
+ getencoder(VALUE obj)
170
+ {
171
+ return (BrotliCompressor *)getref(obj, &llencoder_type);
172
+ }
173
+
174
+ static VALUE
175
+ llenc_alloc(VALUE mod)
176
+ {
177
+ return TypedData_Wrap_Struct(mod, &llencoder_type, NULL);
178
+ }
179
+
180
+ static void
181
+ llenc_init_args(int argc, VALUE argv[], BrotliParams &params, VALUE &predict)
182
+ {
183
+ VALUE opts;
184
+ rb_scan_args(argc, argv, "0:", &opts);
185
+ setup_encode_params(opts, params, predict);
186
+ }
187
+
188
+ /*
189
+ * call-seq:
190
+ * initialize(opts = {})
191
+ *
192
+ * [opts predict: nil] preset dictionary. string or nil
193
+ * [opts mode: 0] Brotli::MODE_GENERIC, Brotli::MODE_TEXT or Brotli::MODE_FONT
194
+ * [opts quality: 9] given 0 .. 11. lower is high speed, higher is high compression.
195
+ * [opts lgwin: 22] log window size. given 10 .. 24.
196
+ * [opts lgblock: nil] log block size. given 16 .. 24.
197
+ *
198
+ */
199
+ static VALUE
200
+ llenc_init(int argc, VALUE argv[], VALUE enc)
201
+ {
202
+ BrotliCompressor *p = getencoderp(enc);
203
+ if (p) { reiniterror(enc); }
204
+ try {
205
+ VALUE predict;
206
+ BrotliParams params;
207
+ llenc_init_args(argc, argv, params, predict);
208
+ rb_obj_infect(enc, predict);
209
+ DATA_PTR(enc) = p = new BrotliCompressor(params);
210
+ if (!NIL_P(predict)) {
211
+ p->BrotliSetCustomDictionary(RSTRING_LEN(predict), (const uint8_t *)RSTRING_PTR(predict));
212
+ }
213
+ } catch(...) {
214
+ rb_raise(eError, "C++ runtime error - #<%s:%p>", rb_obj_classname(enc), (void *)enc);
215
+ }
216
+ return enc;
217
+ }
218
+
219
+ /*
220
+ * call-seq:
221
+ * encode_metablock(src, dest, destsize, islast) -> dest
222
+ */
223
+ static VALUE
224
+ llenc_encode_metablock(VALUE enc, VALUE src, VALUE dest, VALUE destsize, VALUE islast)
225
+ {
226
+ BrotliCompressor *p = getencoder(enc);
227
+ rb_obj_infect(enc, src);
228
+ rb_check_type(src, RUBY_T_STRING);
229
+ rb_check_type(dest, RUBY_T_STRING);
230
+ rb_str_modify(dest);
231
+ rb_str_set_len(dest, 0);
232
+ size_t destsize1 = NUM2SIZET(destsize);
233
+ rb_str_modify_expand(dest, destsize1);
234
+ bool islast1 = RTEST(islast) ? true : false;
235
+ bool s = p->WriteMetaBlock(RSTRING_LEN(src), (const uint8_t *)RSTRING_PTR(src),
236
+ islast1, &destsize1, (uint8_t *)RSTRING_PTR(dest));
237
+ if (!s) {
238
+ rb_raise(eError,
239
+ "failed BrotliCompressor::WriteMetaBlock() - #<%s:%p>",
240
+ rb_obj_classname(enc), (void *)enc);
241
+ }
242
+ rb_obj_infect(dest, enc);
243
+ rb_str_set_len(dest, destsize1);
244
+ return dest;
245
+ }
246
+
247
+ /*
248
+ * call-seq:
249
+ * encode_metadata(src, dest, destsize, islast) -> dest
250
+ */
251
+ static VALUE
252
+ llenc_encode_metadata(VALUE enc, VALUE src, VALUE dest, VALUE destsize, VALUE islast)
253
+ {
254
+ BrotliCompressor *p = getencoder(enc);
255
+ rb_obj_infect(enc, src);
256
+ rb_check_type(src, RUBY_T_STRING);
257
+ rb_check_type(dest, RUBY_T_STRING);
258
+ rb_str_modify(dest);
259
+ rb_str_set_len(dest, 0);
260
+ size_t destsize1 = NUM2SIZET(destsize);
261
+ rb_str_modify_expand(dest, destsize1);
262
+ bool islast1 = RTEST(islast) ? true : false;
263
+ bool s = p->WriteMetadata(RSTRING_LEN(src), (const uint8_t *)RSTRING_PTR(src),
264
+ islast1, &destsize1, (uint8_t *)RSTRING_PTR(dest));
265
+ if (!s) {
266
+ rb_raise(eError,
267
+ "failed BrotliCompressor::WriteMetadata - #<%s:%p>",
268
+ rb_obj_classname(enc), (void *)enc);
269
+ }
270
+ rb_obj_infect(dest, enc);
271
+ rb_str_set_len(dest, destsize1);
272
+ return dest;
273
+ }
274
+
275
+ /*
276
+ * call-seq:
277
+ * finish(dest, destsize) -> dest
278
+ */
279
+ static VALUE
280
+ llenc_finish(VALUE enc, VALUE dest, VALUE destsize)
281
+ {
282
+ BrotliCompressor *p = getencoder(enc);
283
+ rb_check_type(dest, RUBY_T_STRING);
284
+ rb_str_modify(dest);
285
+ rb_str_set_len(dest, 0);
286
+ size_t destsize1 = NUM2SIZET(destsize);
287
+ rb_str_modify_expand(dest, destsize1);
288
+ bool s = p->FinishStream(&destsize1, (uint8_t *)RSTRING_PTR(dest));
289
+ if (!s) { rb_raise(eError, "failed BrotliCompressor::FinishStream - #<%s:%p>", rb_obj_classname(enc), (void *)enc); }
290
+ rb_obj_infect(dest, enc);
291
+ rb_str_set_len(dest, destsize1);
292
+ return dest;
293
+ }
294
+
295
+ /*
296
+ * call-seq:
297
+ * copy_to_ringbuffer(src) -> self
298
+ */
299
+ static VALUE
300
+ llenc_copy_to_ringbuffer(VALUE enc, VALUE src)
301
+ {
302
+ BrotliCompressor *p = getencoder(enc);
303
+ rb_obj_infect(enc, src);
304
+ rb_check_type(src, RUBY_T_STRING);
305
+ p->CopyInputToRingBuffer(RSTRING_LEN(src), (uint8_t *)RSTRING_PTR(src));
306
+ return enc;
307
+ }
308
+
309
+ /*
310
+ * call-seq:
311
+ * encode_brotlidata(dest, islast, forceflush) -> dest
312
+ */
313
+ static VALUE
314
+ llenc_encode_brotlidata(VALUE enc, VALUE dest, VALUE islast, VALUE forceflush)
315
+ {
316
+ BrotliCompressor *p = getencoder(enc);
317
+ rb_check_type(dest, RUBY_T_STRING);
318
+ rb_str_modify(dest);
319
+ rb_str_set_len(dest, 0);
320
+ size_t destsize = 0;
321
+ char *destp = NULL;
322
+ bool s = p->WriteBrotliData(RTEST(islast), RTEST(forceflush), &destsize, (uint8_t **)&destp);
323
+ if (!s) { rb_raise(eError, "failed BrotliCompressor::WriteBrotliData - #<%s:%p>", rb_obj_classname(enc), (void *)enc); }
324
+ rb_str_modify_expand(dest, destsize);
325
+ memcpy(RSTRING_PTR(dest), destp, destsize);
326
+ rb_obj_infect(dest, enc);
327
+ rb_str_set_len(dest, destsize);
328
+ return dest;
329
+ }
330
+
331
+ static VALUE
332
+ llenc_blocksize(VALUE enc)
333
+ {
334
+ return SIZET2NUM(getencoder(enc)->input_block_size());
335
+ }
336
+
337
+ static inline void
338
+ llenc_s_encode_args(int argc, VALUE argv[], VALUE &src, size_t &srcsize, VALUE &dest, size_t &destsize, VALUE &predict, BrotliParams &params)
339
+ {
340
+ VALUE opts, u, v;
341
+ switch (rb_scan_args(argc, argv, "12:", &src, &u, &v, &opts)) {
342
+ case 1:
343
+ rb_check_type(src, RUBY_T_STRING);
344
+ srcsize = RSTRING_LEN(src);
345
+ destsize = aux_brotli_compress_bound(srcsize);
346
+ dest = rb_str_buf_new(destsize);
347
+ break;
348
+ case 2:
349
+ rb_check_type(src, RUBY_T_STRING);
350
+ srcsize = RSTRING_LEN(src);
351
+ if (rb_type_p(u, RUBY_T_STRING)) {
352
+ destsize = aux_brotli_compress_bound(srcsize);
353
+ rb_check_type(dest = u, RUBY_T_STRING);
354
+ rb_str_modify(dest);
355
+ rb_str_set_len(dest, 0);
356
+ rb_str_modify_expand(dest, destsize);
357
+ } else {
358
+ destsize = rb_num2sizet(u);
359
+ dest = rb_str_buf_new(destsize);
360
+ }
361
+ break;
362
+ case 3:
363
+ rb_check_type(src, RUBY_T_STRING);
364
+ srcsize = RSTRING_LEN(src);
365
+ destsize = rb_num2sizet(u);
366
+ rb_check_type(dest = v, RUBY_T_STRING);
367
+ rb_str_modify(dest);
368
+ rb_str_set_len(dest, 0);
369
+ rb_str_modify_expand(dest, destsize);
370
+ break;
371
+ default:
372
+ rb_error_arity(argc, 1, 3);
373
+ }
374
+
375
+ setup_encode_params(opts, params, predict);
376
+ }
377
+
378
+ /*
379
+ * call-seq:
380
+ * encode(src, dest = "", predict: nil, mode: 0, quality: 9, lgwin: 22, lgblock: nil) -> encoded binary string
381
+ * encode(src, destsize, dest = "", predict: nil, mode: 0, quality: 9, lgwin: 22, lgblock: nil) -> encoded binary string
382
+ */
383
+ static VALUE
384
+ llenc_s_encode(int argc, VALUE argv[], VALUE br)
385
+ {
386
+ try {
387
+ VALUE src, dest, predict;
388
+ size_t srcsize, destsize;
389
+ BrotliParams params;
390
+ llenc_s_encode_args(argc, argv, src, srcsize, dest, destsize, predict, params);
391
+
392
+ const uint8_t *srcp = reinterpret_cast<const uint8_t *>(RSTRING_PTR(src));
393
+ uint8_t *destp = reinterpret_cast<uint8_t *>(RSTRING_PTR(dest));
394
+ int st = BrotliCompressBuffer(params, srcsize, srcp, &destsize, destp);
395
+ if (!st) {
396
+ rb_raise(eError, "failed BrotliCompressBuffer() (status=%d)", st);
397
+ }
398
+ rb_obj_infect(dest, src);
399
+ rb_str_set_len(dest, destsize);
400
+ return dest;
401
+ } catch(VALUE e) {
402
+ rb_exc_raise(e);
403
+ } catch(aux_ruby_jump_tag e) {
404
+ rb_jump_tag(e.tag);
405
+ } catch(...) {
406
+ rb_raise(eError, "C++ runtime error");
407
+ }
408
+ }
409
+
410
+ static VALUE
411
+ llenc_s_compress_bound(VALUE mod, VALUE size)
412
+ {
413
+ return SIZET2NUM(aux_brotli_compress_bound(NUM2SIZET(size)));
414
+ }
415
+
416
+ void
417
+ extbrotli_init_lowlevelencoder(void)
418
+ {
419
+ cLLEncoder = rb_define_class_under(mBrotli, "LowLevelEncoder", rb_cObject);
420
+ rb_define_singleton_method(cLLEncoder, "encode", RUBY_METHOD_FUNC(llenc_s_encode), -1);
421
+ rb_define_singleton_method(cLLEncoder, "compress_bound", RUBY_METHOD_FUNC(llenc_s_compress_bound), 1);
422
+
423
+ rb_define_alloc_func(cLLEncoder, llenc_alloc);
424
+ rb_define_method(cLLEncoder, "initialize", RUBY_METHOD_FUNC(llenc_init), -1);
425
+ rb_define_method(cLLEncoder, "encode_metablock", RUBY_METHOD_FUNC(llenc_encode_metablock), 4);
426
+ rb_define_method(cLLEncoder, "encode_metadata", RUBY_METHOD_FUNC(llenc_encode_metadata), 4);
427
+ rb_define_method(cLLEncoder, "finish", RUBY_METHOD_FUNC(llenc_finish), 2);
428
+ rb_define_method(cLLEncoder, "copy_to_ringbuffer", RUBY_METHOD_FUNC(llenc_copy_to_ringbuffer), 1);
429
+ rb_define_method(cLLEncoder, "encode_brotlidata", RUBY_METHOD_FUNC(llenc_encode_brotlidata), 3);
430
+ rb_define_method(cLLEncoder, "blocksize", RUBY_METHOD_FUNC(llenc_blocksize), 0);
431
+
432
+ rb_include_module(cLLEncoder, mConst);
433
+ }
data/gemstub.rb ADDED
@@ -0,0 +1,21 @@
1
+ #vim: set fileencoding:utf-8
2
+
3
+ require_relative "lib/extbrotli/version"
4
+
5
+ GEMSTUB = Gem::Specification.new do |s|
6
+ s.name = "extbrotli"
7
+ s.version = Brotli::VERSION
8
+ s.summary = "ruby bindings for brotli"
9
+ s.description = <<EOS
10
+ unofficial ruby bindings for brotli <https://github.com/google/brotli> the compression library.
11
+ EOS
12
+ s.homepage = "https://osdn.jp/projects/rutsubo/"
13
+ s.license = "2-clause BSD License"
14
+ s.author = "dearblue"
15
+ s.email = "dearblue@users.osdn.me"
16
+
17
+ s.required_ruby_version = ">= 2.0"
18
+ s.add_development_dependency "rake", "~> 10.0"
19
+ end
20
+
21
+ EXTRA.concat(FileList["contrib/brotli/{LICENSE,README.md,{enc,dec}/**/*.{h,hh,hxx,hpp,c,cc,cxx,cpp,C}}"])