digest-kangarootwelve 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d66d4525e67457535b7ab5b8232c3c44955e147a
4
- data.tar.gz: 6aca633b39fef2afba603e14aa37095e10b1fb7f
3
+ metadata.gz: 30d451f9dc1fc4f3e7e87a42fc2c0d3c21339300
4
+ data.tar.gz: a8ac32cff2303adf783383fcfaace5fd6ab27e3e
5
5
  SHA512:
6
- metadata.gz: 4ba12fd8c227b4d5384456adc00b5326828e82c792dd967516b41b20b09885c62f19a133a8e2f3c028160ac822686e35db00a9a60ba8fc6eb967a8e0d5dc2eca
7
- data.tar.gz: 96fcb84f82a6025bbed1b30ed5e295ac8432e51d3fd3d4c047cb1a351f214516facd2d358022f396ca75972a10ad65fc44df19baf8ecafbc973cf4599c7fa3dd
6
+ metadata.gz: 32e3bd67264b5eb97b781f403da7501237cd63cc2e10ac2f1e214998143a7cf6b070ecf2ee416c6a443827fb5867b4c4c8569d56ec715e5dc561034165bddc4c
7
+ data.tar.gz: 7ff11f64ed95a86df337e4518416ce0b27178f545a5b90d9c1192eaf7b5bca5f5f95aa2feb6266877e83424f9f7d40e94880db5d9905b5738c6930e1420889a5
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # digest-kangarootwelve-ruby
2
2
 
3
- An implementation of KangarooTwelve for Ruby that works on top of Digest::Base.
3
+ The digest-kangarootwelve gem is an implementation of KangarooTwelve for Ruby
4
+ that works on top of Digest::Base.
4
5
 
5
6
  It allows hashing with different digest lengths and different customization
6
7
  strings.
@@ -29,51 +30,40 @@ Or install it yourself as:
29
30
 
30
31
  ## Example Usage
31
32
 
32
- require 'digest/kangarootwelve'
33
+ Digest::KangarooTwelve[32].digest("abc")
34
+ => "\xAB\x17O2\x8CU\xA5Q\v\v \x97\x91\xBF\x8B`\xE8\x01\xA7\xCF\xC2\xAAB\x04-\xCB\x8FT\x7F\xBE:}"
33
35
 
34
- klass = Digest::KangarooTwelve[32]
35
- => Digest::KangarooTwelve_32
36
-
37
- hash = klass.new
38
- => #<Digest::KangarooTwelve_32|32|65536||1ac2d450fc3b4205d19da7bfca1b37513c0803577ac7167f06fe2ce1f0ef39e5>
39
-
40
- hash.hexdigest("abc")
36
+ Digest::KangarooTwelve[32].hexdigest("abc")
41
37
  => "ab174f328c55a5510b0b209791bf8b60e801a7cfc2aa42042dcb8f547fbe3a7d"
42
38
 
43
- Digest::KangarooTwelve[32].new.hexdigest("abc")
39
+ Digest::KangarooTwelve[32].new.update("a").update("b").update("c").hexdigest
44
40
  => "ab174f328c55a5510b0b209791bf8b60e801a7cfc2aa42042dcb8f547fbe3a7d"
45
41
 
46
- Digest::KangarooTwelve.default
47
- => Digest::KangarooTwelve_64
48
-
49
- Digest::KangarooTwelve.implement(digest_length: 32, customization: "secret")
50
- => Digest::KangarooTwelve_32_65536_736563726574
51
-
52
42
  Digest::KangarooTwelve.implement(name: "SecretHash", digest_length: 32, customization: "secret")
53
43
  => Digest::SecretHash
54
44
 
55
45
  Digest::KangarooTwelve.implement(n: "SecretHash", d: 32, c: "secret")
56
46
  => Digest::SecretHash
57
47
 
58
- Digest::SecretHash.new.hexdigest("abc")
48
+ Digest::SecretHash.hexdigest("abc")
59
49
  => "dc1fd53f85402e2b34fa92bd87593dd9c3fe6cc49d9db6c05dc0cf26c6a7e03f"
60
50
 
61
- Digest::KangarooTwelve.implement(name: nil, digest_length: 48)
62
- => #<Class:0x000000015fe8e8>
51
+ Digest::KangarooTwelve.default
52
+ => Digest::KangarooTwelve_64
63
53
 
64
54
  ## Details
65
55
 
66
- For details on how to use the methods, please examine the comments in
67
- `ext/digest/kangarootwelve/ext.c`, or try to run
68
- `ri 'Digest::KangarooTwelve'` or
69
- `ri 'Digest::KangarooTwelve::<method_name>'`. I'll try to provide a more
70
- readable format of the documentation for the API soon.
71
-
72
- You can use the implementation classes produced by `[]`, `default` or
73
- `implement`, just like any other implementation class in `Digest`
74
- (e.g. `Digest::SHA1`, `Digest::SHA512`), since they are also derived from
75
- `Digest::Base`, and are programmed to work the way an implementation class
76
- that's based on the `Digest` framework should.
56
+ The implementation classes produced by `[]`, `default` or
57
+ `implement` can be used just like any other implementation class in `Digest`
58
+ (like `Digest::SHA1` or `Digest::SHA512`), since the implementation classes are
59
+ based on `Digest::Base`.
60
+
61
+ For details on how to use these methods, please examine the comments in
62
+ `ext/digest/kangarootwelve/ext.c`, or run `ri` with
63
+ `ri 'Digest::KangarooTwelve'`, or `ri 'Digest::KangarooTwelve::<method_name>'`.
64
+
65
+ You can also visit the automatically generated API documentation pages provided
66
+ by RubyDoc.info in http://www.rubydoc.info/gems/digest-kangarootwelve/.
77
67
 
78
68
  ## Contributing
79
69
 
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
  spec.authors = ["konsolebox"]
11
11
  spec.email = ["konsolebox@gmail.com"]
12
12
  spec.summary = "KangarooTwelve for Ruby"
13
- spec.description = "An implementation of KangarooTwelve for Ruby that works on top of Digest::Base."
13
+ spec.description = "A KangarooTwelve library that works on top of Digest::Base."
14
14
  spec.homepage = "https://github.com/konsolebox/digest-kangarootwelve-ruby"
15
15
  spec.license = "MIT"
16
16
 
@@ -23,14 +23,15 @@
23
23
  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
24
  */
25
25
 
26
- #include "KangarooTwelve.h"
27
26
  #include <ruby.h>
28
27
  #include <ruby/digest.h>
29
28
 
29
+ #include "KangarooTwelve.h"
30
+ #include "utils.h"
31
+
30
32
  #define KT_DEFAULT_DIGEST_LENGTH 64 /* 512 bits */
31
- #define KT_DEFAULT_BLOCK_LENGTH 8192 * 8 /* chunkSize * Parallellism */
33
+ #define KT_BLOCK_LENGTH 8192 /* chunkSize */
32
34
  #define KT_MIN_DIGEST_LENGTH 1
33
- #define KT_MIN_BLOCK_LENGTH 64
34
35
 
35
36
  #define KT_DIGEST_API_VERSION_IS_SUPPORTED(version) (version == 3)
36
37
 
@@ -44,7 +45,9 @@ static ID _id_auto;
44
45
  static ID _id_block_length;
45
46
  static ID _id_b;
46
47
  static ID _id_customization;
48
+ static ID _id_customization_hex;
47
49
  static ID _id_c;
50
+ static ID _id_ch;
48
51
  static ID _id_default;
49
52
  static ID _id_digest_length;
50
53
  static ID _id_d;
@@ -55,11 +58,10 @@ static ID _id_new;
55
58
  static ID _id_n;
56
59
  static ID _id_unpack;
57
60
 
58
- static VALUE _module_Digest;
59
- static VALUE _class_Digest_Base;
60
- static VALUE _module_Digest_KangarooTwelve;
61
- static VALUE _class_Digest_KangarooTwelve_Impl;
62
- static VALUE _class_Digest_KangarooTwelve_Metadata;
61
+ static VALUE _Digest;
62
+ static VALUE _Digest_KangarooTwelve;
63
+ static VALUE _Digest_KangarooTwelve_Impl;
64
+ static VALUE _Digest_KangarooTwelve_Metadata;
63
65
 
64
66
  typedef struct {
65
67
  KangarooTwelve_Instance instance;
@@ -75,10 +77,25 @@ static void check_digest_length(int digest_length)
75
77
  rb_raise(rb_eArgError, "Digest length lesser than minimum (%d): %d", KT_MIN_DIGEST_LENGTH, digest_length);
76
78
  }
77
79
 
78
- static void check_block_length(int block_length)
80
+ static VALUE hex_encode_str(VALUE str)
79
81
  {
80
- if (!(block_length >= KT_MIN_BLOCK_LENGTH))
81
- rb_raise(rb_eArgError, "Block length lesser than minimum (%d): %d", KT_MIN_BLOCK_LENGTH, block_length);
82
+ int len = RSTRING_LEN(str);
83
+ VALUE hex = rb_str_new(0, len * 2);
84
+ hex_encode_str_implied(RSTRING_PTR(str), len, RSTRING_PTR(hex));
85
+ return hex;
86
+ }
87
+
88
+ static VALUE hex_decode_str(VALUE str)
89
+ {
90
+ int len = RSTRING_LEN(str);
91
+ VALUE decoded = rb_str_new(0, calc_hex_decoded_str_length(len));
92
+
93
+ if (! hex_decode_str_implied(RSTRING_PTR(str), len, RSTRING_PTR(decoded))) {
94
+ VALUE inspect = rb_inspect(str);
95
+ rb_raise(rb_eArgError, "Failed to decode hex string %s.", RSTRING_PTR(inspect));
96
+ }
97
+
98
+ return decoded;
82
99
  }
83
100
 
84
101
  static int kangarootwelve_init(void *ctx)
@@ -88,7 +105,7 @@ static int kangarootwelve_init(void *ctx)
88
105
 
89
106
  VALUE klass_or_instance = rb_current_receiver();
90
107
 
91
- if (TYPE(klass_or_instance) == T_CLASS && klass_or_instance == _class_Digest_KangarooTwelve_Impl)
108
+ if (TYPE(klass_or_instance) == T_CLASS && klass_or_instance == _Digest_KangarooTwelve_Impl)
92
109
  rb_raise(rb_eStandardError, "Digest::KangarooTwelve::Impl is a base class and cannot be instantiated.");
93
110
 
94
111
  VALUE digest_length = rb_funcall(klass_or_instance, _id_digest_length, 0);
@@ -143,17 +160,12 @@ static int kangarootwelve_finish(void *ctx, unsigned char *data)
143
160
  }
144
161
  }
145
162
 
146
- static VALUE do_nothing(VALUE self)
147
- {
148
- return Qnil;
149
- }
150
-
151
- static VALUE implement(VALUE name, VALUE digest_length, VALUE block_length, VALUE customization)
163
+ static VALUE implement(VALUE name, VALUE digest_length, VALUE customization)
152
164
  {
153
165
  if (!KT_DIGEST_API_VERSION_IS_SUPPORTED(RUBY_DIGEST_API_VERSION))
154
166
  rb_raise(rb_eRuntimeError, "Digest API version is not supported.");
155
167
 
156
- int digest_length_int, block_length_int;
168
+ int digest_length_int;
157
169
 
158
170
  switch (TYPE(digest_length)) {
159
171
  case T_NIL:
@@ -167,18 +179,6 @@ static VALUE implement(VALUE name, VALUE digest_length, VALUE block_length, VALU
167
179
  rb_raise(rb_eTypeError, "Invalid value type for digest length.");
168
180
  }
169
181
 
170
- switch (TYPE(block_length)) {
171
- case T_NIL:
172
- block_length_int = KT_DEFAULT_BLOCK_LENGTH;
173
- break;
174
- case T_FIXNUM:
175
- block_length_int = FIX2INT(block_length);
176
- check_block_length(block_length_int);
177
- break;
178
- default:
179
- rb_raise(rb_eTypeError, "Invalid value type for block length.");
180
- }
181
-
182
182
  switch (TYPE(customization)) {
183
183
  case T_NIL:
184
184
  case T_STRING:
@@ -198,12 +198,7 @@ static VALUE implement(VALUE name, VALUE digest_length, VALUE block_length, VALU
198
198
 
199
199
  if (id == _id_auto) {
200
200
  if (customization != Qnil) {
201
- VALUE customization_hex_in_array = rb_funcall(customization, _id_unpack, 1, rb_str_new_literal("H*"));
202
- VALUE customization_hex = rb_ary_pop(customization_hex_in_array);
203
- impl_class_name = rb_sprintf("KangarooTwelve_%d_%d_%s", digest_length_int, block_length_int,
204
- StringValueCStr(customization_hex));
205
- } else if (block_length_int != KT_DEFAULT_BLOCK_LENGTH) {
206
- impl_class_name = rb_sprintf("KangarooTwelve_%d_%d", digest_length_int, block_length_int);
201
+ impl_class_name = hex_encode_str(customization);
207
202
  } else {
208
203
  impl_class_name = rb_sprintf("KangarooTwelve_%d", digest_length_int);
209
204
  }
@@ -231,10 +226,10 @@ static VALUE implement(VALUE name, VALUE digest_length, VALUE block_length, VALU
231
226
  VALUE impl_class;
232
227
 
233
228
  if (impl_class_name == Qnil) {
234
- impl_class = rb_funcall(rb_cClass, _id_new, 1, _class_Digest_KangarooTwelve_Impl);
229
+ impl_class = rb_funcall(rb_cClass, _id_new, 1, _Digest_KangarooTwelve_Impl);
235
230
  } else {
236
- if (rb_const_defined(_module_Digest, impl_class_name_id)) {
237
- impl_class = rb_const_get(_module_Digest, impl_class_name_id);
231
+ if (rb_const_defined(_Digest, impl_class_name_id)) {
232
+ impl_class = rb_const_get(_Digest, impl_class_name_id);
238
233
 
239
234
  if (TYPE(impl_class) != T_CLASS) {
240
235
  rb_raise(rb_eTypeError,
@@ -242,7 +237,7 @@ static VALUE implement(VALUE name, VALUE digest_length, VALUE block_length, VALU
242
237
  StringValueCStr(impl_class_name));
243
238
  }
244
239
 
245
- if (rb_class_superclass(impl_class) != _class_Digest_KangarooTwelve_Impl) {
240
+ if (rb_class_superclass(impl_class) != _Digest_KangarooTwelve_Impl) {
246
241
  rb_raise(rb_eTypeError,
247
242
  "Digest::%s was already defined but not derived from Digest::KangarooTwelve::Impl.",
248
243
  StringValueCStr(impl_class_name));
@@ -266,37 +261,19 @@ static VALUE implement(VALUE name, VALUE digest_length, VALUE block_length, VALU
266
261
  digest_length_int);
267
262
  }
268
263
 
269
- VALUE prev_block_length = rb_ivar_get(impl_class, _id_block_length);
270
-
271
- if (TYPE(prev_block_length) != T_FIXNUM) {
272
- rb_raise(rb_eRuntimeError,
273
- "Previous definition of Digest::%s has invalid block length value type.",
274
- StringValueCStr(impl_class_name));
275
- }
276
-
277
- int prev_block_length_int = FIX2INT(prev_block_length);
278
-
279
- if (prev_block_length_int != block_length_int) {
280
- rb_raise(rb_eTypeError,
281
- "Digest::%s was already defined but has different block length (%d instead of %d).",
282
- StringValueCStr(impl_class_name),
283
- prev_block_length_int,
284
- block_length_int);
285
- }
286
-
287
264
  return impl_class;
288
265
  }
289
266
 
290
- impl_class = rb_define_class_id_under(_module_Digest, impl_class_name_id, _class_Digest_KangarooTwelve_Impl);
267
+ impl_class = rb_define_class_id_under(_Digest, impl_class_name_id, _Digest_KangarooTwelve_Impl);
291
268
  }
292
269
 
293
270
  VALUE metadata_obj;
294
271
  rb_digest_metadata_t *metadata;
295
- metadata_obj = Data_Make_Struct(_class_Digest_KangarooTwelve_Metadata, rb_digest_metadata_t, 0, -1, metadata);
272
+ metadata_obj = Data_Make_Struct(_Digest_KangarooTwelve_Metadata, rb_digest_metadata_t, 0, -1, metadata);
296
273
 
297
274
  metadata->api_version = RUBY_DIGEST_API_VERSION;
298
275
  metadata->digest_len = digest_length_int;
299
- metadata->block_len = block_length_int;
276
+ metadata->block_len = KT_BLOCK_LENGTH;
300
277
  metadata->ctx_size = sizeof(KT_CONTEXT);
301
278
  metadata->init_func = kangarootwelve_init;
302
279
  metadata->update_func = kangarootwelve_update;
@@ -304,7 +281,7 @@ static VALUE implement(VALUE name, VALUE digest_length, VALUE block_length, VALU
304
281
 
305
282
  rb_ivar_set(impl_class, _id_metadata, metadata_obj);
306
283
  rb_ivar_set(impl_class, _id_digest_length, INT2FIX(digest_length_int));
307
- rb_ivar_set(impl_class, _id_block_length, INT2FIX(block_length_int));
284
+ rb_ivar_set(impl_class, _id_block_length, INT2FIX(KT_BLOCK_LENGTH));
308
285
  rb_ivar_set(impl_class, _id_customization, customization);
309
286
 
310
287
  return impl_class;
@@ -314,16 +291,25 @@ static VALUE implement(VALUE name, VALUE digest_length, VALUE block_length, VALU
314
291
  * Document-module: Digest::KangarooTwelve
315
292
  *
316
293
  * The Digest::KangarooTwelve module is the main component of the KangarooTwelve
317
- * extension.
294
+ * extension for Ruby.
295
+ *
296
+ * It contains singleton methods that can be used to create implementation
297
+ * classes. These implementation classes can then be used to create instances
298
+ * for producing hashes.
299
+ *
300
+ * Example:
318
301
  *
319
- * To create a hashing object, one must use one of the singleton methods in this
320
- * module to produce an implementation class, which can then be used to create
321
- * the hashing object.
302
+ * <tt>Digest::KangarooTwelve[32].new.update("one").update("two").digest</tt>
322
303
  *
323
- * An example to this is <code>hash = Digest::KangarooTwelve[32].new</code>.
304
+ * The implementation classes can also be used to directly produce hashes.
324
305
  *
325
- * The produced implementation class and the hash object can be used just like
326
- * any other implementation classes and instances in Digest.
306
+ * Example:
307
+ *
308
+ * <tt>Digest::KangarooTwelve[32].hexdigest("1234")</tt>
309
+ *
310
+ * The produced implementation classes and their instances can be used just like
311
+ * any other classes and instances in the Digest namespace that's based on
312
+ * Digest::Base.
327
313
  */
328
314
 
329
315
  /*
@@ -332,12 +318,12 @@ static VALUE implement(VALUE name, VALUE digest_length, VALUE block_length, VALU
332
318
  * Returns the default implementation class which has a digest length of 64
333
319
  * bytes, and doesn't have a customization string.
334
320
  */
335
- VALUE rbx_Digest_KangarooTwelve_singleton_default(VALUE self)
321
+ static VALUE _Digest_KangarooTwelve_singleton_default(VALUE self)
336
322
  {
337
323
  VALUE default_ = rb_ivar_get(self, _id_default);
338
324
 
339
325
  if (NIL_P(default_)) {
340
- default_ = implement(ID2SYM(_id_auto), INT2FIX(KT_DEFAULT_DIGEST_LENGTH), Qnil, Qnil);
326
+ default_ = implement(ID2SYM(_id_auto), INT2FIX(KT_DEFAULT_DIGEST_LENGTH), Qnil);
341
327
  rb_ivar_set(self, _id_default, default_);
342
328
  }
343
329
 
@@ -360,15 +346,12 @@ VALUE rbx_Digest_KangarooTwelve_singleton_default(VALUE self)
360
346
  * module.
361
347
  *
362
348
  * The default value for this option is +:auto+, and it implies automatic
363
- * generation of the name. The generated name is in the format of
364
- * Digest::KangarooTwelve_<digest_length> if neither a customization string
365
- * nor a custom block length is specified.
349
+ * generation of the name. The generated name is in the form of
350
+ * Digest::KangarooTwelve_<digest_length> if a customization string is not
351
+ * specified.
366
352
  *
367
- * If a customization string is specified, the format would become
368
- * Digest::KangarooTwelve_<digest_length>_<block_length>_<cust_str_hex>.
369
- *
370
- * If no customization string is specified but a custom block is, the format
371
- * would be Digest::KangarooTwelve_<digest_length>_<block_length>.
353
+ * If a customization string is specified, the format would be
354
+ * Digest::KangarooTwelve_<digest_length>_<cust_str_hex>.
372
355
  *
373
356
  * Specifying a string would make the method produce Digest::<string>.
374
357
  *
@@ -381,30 +364,25 @@ VALUE rbx_Digest_KangarooTwelve_singleton_default(VALUE self)
381
364
  *
382
365
  * See Digest::KangarooTwelve::DEFAULT_DIGEST_LENGTH for the default value.
383
366
  *
384
- * :b, :block_length ::
385
- * Specifies a custom block length. This doesn't affect the digest results
386
- * and is mostly just used for tweaking performance or memory usage. Examine
387
- * the update function in KangarooTwelve.c to know if this is needed to be
388
- * configured.
389
- *
390
- * See Digest::KangarooTwelve::DEFAULT_BLOCK_LENGTH for the default value.
391
- *
392
367
  * :c, :customization ::
393
368
  * Specifies the customization string. Adding a customization string changes
394
369
  * the resulting digest of every input.
395
370
  *
396
- * Calling the method with no argument would be the same as calling the
371
+ * :ch, :customization_hex ::
372
+ * Specifies the customization string in hex mode.
373
+ *
374
+ * Calling the method with no argument is the same as calling the
397
375
  * Digest::KangarooTwelve::default method.
398
376
  */
399
- VALUE rbx_Digest_KangarooTwelve_singleton_implement(int argc, VALUE *argv, VALUE self)
377
+ static VALUE _Digest_KangarooTwelve_singleton_implement(int argc, VALUE *argv, VALUE self)
400
378
  {
401
- VALUE opts, name, digest_length, block_length, customization;
379
+ VALUE opts, name, digest_length, customization;
402
380
 
403
381
  rb_scan_args(argc, argv, "0:", &opts);
404
382
 
405
383
  if (NIL_P(opts)) {
406
384
  name = ID2SYM(_id_auto);
407
- digest_length = block_length = customization = Qnil;
385
+ digest_length = customization = Qnil;
408
386
  } else {
409
387
  name = rb_hash_lookup2(opts, ID2SYM(_id_n), Qundef);
410
388
 
@@ -416,18 +394,29 @@ VALUE rbx_Digest_KangarooTwelve_singleton_implement(int argc, VALUE *argv, VALUE
416
394
  if (digest_length == Qundef)
417
395
  digest_length = rb_hash_lookup2(opts, ID2SYM(_id_digest_length), Qnil);
418
396
 
419
- block_length = rb_hash_lookup2(opts, ID2SYM(_id_b), Qundef);
420
-
421
- if (block_length == Qundef)
422
- block_length = rb_hash_lookup2(opts, ID2SYM(_id_block_length), Qnil);
423
-
424
397
  customization = rb_hash_lookup2(opts, ID2SYM(_id_c), Qundef);
425
398
 
426
399
  if (customization == Qundef)
427
- customization = rb_hash_lookup2(opts, ID2SYM(_id_customization), Qnil);
400
+ customization = rb_hash_lookup2(opts, ID2SYM(_id_customization), Qundef);
401
+
402
+ if (customization == Qundef) {
403
+ VALUE customization_hex = rb_hash_lookup2(opts, ID2SYM(_id_customization_hex), Qundef);
404
+
405
+ if (customization_hex == Qundef)
406
+ customization_hex = rb_hash_lookup2(opts, ID2SYM(_id_ch), Qundef);
407
+
408
+ if (customization_hex == Qundef) {
409
+ customization = Qnil;
410
+ } else {
411
+ if (TYPE(customization_hex) != T_STRING)
412
+ rb_raise(rb_eTypeError, "Customization argument not a string.");
413
+
414
+ customization = hex_decode_str(customization_hex);
415
+ }
416
+ }
428
417
  }
429
418
 
430
- return implement(name, digest_length, block_length, customization);
419
+ return implement(name, digest_length, customization);
431
420
  }
432
421
 
433
422
  /*
@@ -440,9 +429,9 @@ VALUE rbx_Digest_KangarooTwelve_singleton_implement(int argc, VALUE *argv, VALUE
440
429
  * Digest::KangarooTwelve_<digest_length>, and can be directly referenced after
441
430
  * this method is called.
442
431
  */
443
- VALUE rbx_Digest_KangarooTwelve_singleton_implement_simple(VALUE self, VALUE digest_length)
432
+ static VALUE _Digest_KangarooTwelve_singleton_implement_simple(VALUE self, VALUE digest_length)
444
433
  {
445
- return implement(ID2SYM(_id_auto), digest_length, Qnil, Qnil);
434
+ return implement(ID2SYM(_id_auto), digest_length, Qnil);
446
435
  }
447
436
 
448
437
  /*
@@ -459,12 +448,12 @@ VALUE rbx_Digest_KangarooTwelve_singleton_implement_simple(VALUE self, VALUE dig
459
448
  *
460
449
  * Creates a new object instance of the implementation class.
461
450
  */
462
- VALUE rbx_Digest_KangarooTwelve_Impl_singleton_new(VALUE self)
451
+ static VALUE _Digest_KangarooTwelve_Impl_singleton_new(VALUE self)
463
452
  {
464
- if (self == _class_Digest_KangarooTwelve_Impl)
453
+ if (self == _Digest_KangarooTwelve_Impl)
465
454
  rb_raise(rb_eRuntimeError, "Digest::KangarooTwelve::Impl is an abstract class.");
466
455
 
467
- if (rb_obj_class(rb_ivar_get(self, _id_metadata)) != _class_Digest_KangarooTwelve_Metadata)
456
+ if (rb_obj_class(rb_ivar_get(self, _id_metadata)) != _Digest_KangarooTwelve_Metadata)
468
457
  rb_raise(rb_eRuntimeError, "Metadata not set or invalid. Please do not manually inherit KangarooTwelve.");
469
458
 
470
459
  return rb_call_super(0, 0);
@@ -475,9 +464,9 @@ VALUE rbx_Digest_KangarooTwelve_Impl_singleton_new(VALUE self)
475
464
  *
476
465
  * Returns configured digest length of the implementation class.
477
466
  */
478
- VALUE rbx_Digest_KangarooTwelve_Impl_singleton_digest_length(VALUE self)
467
+ static VALUE _Digest_KangarooTwelve_Impl_singleton_digest_length(VALUE self)
479
468
  {
480
- if (self == _class_Digest_KangarooTwelve_Impl)
469
+ if (self == _Digest_KangarooTwelve_Impl)
481
470
  rb_raise(rb_eRuntimeError, "Digest::KangarooTwelve::Impl is an abstract class.");
482
471
 
483
472
  return rb_ivar_get(self, _id_digest_length);
@@ -486,14 +475,11 @@ VALUE rbx_Digest_KangarooTwelve_Impl_singleton_digest_length(VALUE self)
486
475
  /*
487
476
  * call-seq: block_length -> int
488
477
  *
489
- * Returns configured block length of the implementation class.
478
+ * Returns 8192.
490
479
  */
491
- VALUE rbx_Digest_KangarooTwelve_Impl_singleton_block_length(VALUE self)
480
+ static VALUE _Digest_KangarooTwelve_Impl_singleton_block_length(VALUE self)
492
481
  {
493
- if (self == _class_Digest_KangarooTwelve_Impl)
494
- rb_raise(rb_eRuntimeError, "Digest::KangarooTwelve::Impl is an abstract class.");
495
-
496
- return rb_ivar_get(self, _id_block_length);
482
+ return INT2FIX(KT_BLOCK_LENGTH);
497
483
  }
498
484
 
499
485
  /*
@@ -501,20 +487,34 @@ VALUE rbx_Digest_KangarooTwelve_Impl_singleton_block_length(VALUE self)
501
487
  *
502
488
  * Returns configured customization string of the implementation class.
503
489
  */
504
- VALUE rbx_Digest_KangarooTwelve_Impl_singleton_customization(VALUE self)
490
+ static VALUE _Digest_KangarooTwelve_Impl_singleton_customization(VALUE self)
505
491
  {
506
- if (self == _class_Digest_KangarooTwelve_Impl)
492
+ if (self == _Digest_KangarooTwelve_Impl)
507
493
  rb_raise(rb_eRuntimeError, "Digest::KangarooTwelve::Impl is an abstract class.");
508
494
 
509
495
  return rb_ivar_get(self, _id_customization);
510
496
  }
511
497
 
498
+ /*
499
+ * call-seq: customization_hex -> string or nil
500
+ *
501
+ * Returns configured customization string of the implementation class in hex.
502
+ */
503
+ static VALUE _Digest_KangarooTwelve_Impl_singleton_customization_hex(VALUE self)
504
+ {
505
+ if (self == _Digest_KangarooTwelve_Impl)
506
+ rb_raise(rb_eRuntimeError, "Digest::KangarooTwelve::Impl is an abstract class.");
507
+
508
+ VALUE customization = rb_ivar_get(self, _id_customization);
509
+ return hex_encode_str(customization);
510
+ }
511
+
512
512
  /*
513
513
  * call-seq: customization -> string or nil
514
514
  *
515
515
  * Returns configured customization string of the implementation object.
516
516
  */
517
- VALUE rbx_Digest_KangarooTwelve_Impl_customization(VALUE self)
517
+ static VALUE _Digest_KangarooTwelve_Impl_customization(VALUE self)
518
518
  {
519
519
  VALUE customization;
520
520
 
@@ -529,12 +529,23 @@ VALUE rbx_Digest_KangarooTwelve_Impl_customization(VALUE self)
529
529
  return customization;
530
530
  }
531
531
 
532
+ /*
533
+ * call-seq: customization_hex -> string or nil
534
+ *
535
+ * Returns configured customization string of the implementation object in hex.
536
+ */
537
+ static VALUE _Digest_KangarooTwelve_Impl_customization_hex(VALUE self)
538
+ {
539
+ VALUE customization = rb_funcall(self, _id_customization, 0);
540
+ return hex_encode_str(customization);
541
+ }
542
+
532
543
  /*
533
544
  * call-seq: inspect -> string
534
545
  *
535
- * Returns a string in the format of #<implementation_class_name|digest_length|block_length|customization_string|digest>
546
+ * Returns a string in the form of #<impl_class_name|digest_length|hex_cust_string|hex_digest>
536
547
  */
537
- VALUE rbx_Digest_KangarooTwelve_inspect(VALUE self)
548
+ static VALUE _Digest_KangarooTwelve_Impl_inspect(VALUE self)
538
549
  {
539
550
  VALUE klass = rb_obj_class(self);
540
551
  VALUE klass_name = rb_class_name(klass);
@@ -543,12 +554,11 @@ VALUE rbx_Digest_KangarooTwelve_inspect(VALUE self)
543
554
  klass_name = rb_inspect(klass);
544
555
 
545
556
  VALUE digest_length = rb_funcall(self, _id_digest_length, 0);
546
- VALUE block_length = rb_funcall(self, _id_block_length, 0);
547
- VALUE customization = rb_funcall(self, _id_customization, 0);
557
+ VALUE customization_hex = rb_funcall(self, _id_customization_hex, 0);
548
558
  VALUE hexdigest = rb_funcall(self, _id_hexdigest, 0);
549
559
 
550
- VALUE args[] = { klass_name, digest_length, block_length, customization, hexdigest };
551
- return rb_str_format(sizeof(args), args, rb_str_new_literal("#<%s|%d|%d|%s|%s>"));
560
+ VALUE args[] = { klass_name, digest_length, customization_hex, hexdigest };
561
+ return rb_str_format(sizeof(args), args, rb_str_new_literal("#<%s:%d|%s|%s>"));
552
562
  }
553
563
 
554
564
  /*
@@ -562,7 +572,9 @@ void Init_kangarootwelve()
562
572
  DEFINE_ID(auto)
563
573
  DEFINE_ID(block_length)
564
574
  DEFINE_ID(b)
575
+ DEFINE_ID(ch)
565
576
  DEFINE_ID(customization)
577
+ DEFINE_ID(customization_hex)
566
578
  DEFINE_ID(c)
567
579
  DEFINE_ID(default)
568
580
  DEFINE_ID(digest_length)
@@ -575,66 +587,77 @@ void Init_kangarootwelve()
575
587
  DEFINE_ID(unpack)
576
588
 
577
589
  rb_require("digest");
578
- _module_Digest = rb_path2class("Digest");
579
- _class_Digest_Base = rb_path2class("Digest::Base");
590
+ _Digest = rb_path2class("Digest");
580
591
 
581
592
  #if 0
582
- _module_Digest = rb_define_module("Digest"); /* Tell RDoc about Digest since it doesn't recognize rb_path2class. */
593
+ _Digest = rb_define_module("Digest"); /* Tell RDoc about Digest since it doesn't recognize rb_path2class. */
583
594
  #endif
584
595
 
585
596
  /*
586
- * module Digest::KangarooTwelve
597
+ * Document-module: Digest::KangarooTwelve
587
598
  */
588
599
 
589
- _module_Digest_KangarooTwelve = rb_define_module_under(_module_Digest, "KangarooTwelve");
600
+ _Digest_KangarooTwelve = rb_define_module_under(_Digest, "KangarooTwelve");
590
601
 
591
- rb_define_singleton_method(_module_Digest_KangarooTwelve, "default",
592
- rbx_Digest_KangarooTwelve_singleton_default, 0);
593
- rb_define_singleton_method(_module_Digest_KangarooTwelve, "implement",
594
- rbx_Digest_KangarooTwelve_singleton_implement, -1);
595
- rb_define_singleton_method(_module_Digest_KangarooTwelve, "[]",
596
- rbx_Digest_KangarooTwelve_singleton_implement_simple, 1);
602
+ rb_define_singleton_method(_Digest_KangarooTwelve, "default",
603
+ _Digest_KangarooTwelve_singleton_default, 0);
604
+ rb_define_singleton_method(_Digest_KangarooTwelve, "implement",
605
+ _Digest_KangarooTwelve_singleton_implement, -1);
606
+ rb_define_singleton_method(_Digest_KangarooTwelve, "[]",
607
+ _Digest_KangarooTwelve_singleton_implement_simple, 1);
597
608
 
598
609
  /*
599
- * Currently 65536 bytes (8192 x 8)
610
+ * Document-const: Digest::KangarooTwelve::BLOCK_LENGTH
611
+ *
612
+ * 8192 bytes
600
613
  */
601
614
 
602
- rb_define_const(_module_Digest_KangarooTwelve, "DEFAULT_BLOCK_LENGTH", INT2FIX(KT_DEFAULT_BLOCK_LENGTH));
615
+ /* 8192 bytes */
616
+
617
+ rb_define_const(_Digest_KangarooTwelve, "BLOCK_LENGTH", INT2FIX(KT_BLOCK_LENGTH));
603
618
 
604
619
  /*
620
+ * Document-const: Digest::KangarooTwelve::DEFAULT_DIGEST_LENGTH
621
+ *
605
622
  * 64 bytes (512 bits)
606
623
  */
607
624
 
608
- rb_define_const(_module_Digest_KangarooTwelve, "DEFAULT_DIGEST_LENGTH", INT2FIX(KT_DEFAULT_DIGEST_LENGTH));
625
+ /* 64 bytes (512 bits) */
626
+
627
+ rb_define_const(_Digest_KangarooTwelve, "DEFAULT_DIGEST_LENGTH", INT2FIX(KT_DEFAULT_DIGEST_LENGTH));
609
628
 
610
629
  /*
611
- * class Digest::KangarooTwelve::Impl < Digest::Base
630
+ * Document-class: Digest::KangarooTwelve::Impl
612
631
  */
613
632
 
614
- _class_Digest_KangarooTwelve_Impl = rb_define_class_under(_module_Digest_KangarooTwelve, "Impl",
615
- _class_Digest_Base);
616
-
617
- rb_define_singleton_method(_class_Digest_KangarooTwelve_Impl, "new",
618
- rbx_Digest_KangarooTwelve_Impl_singleton_new, 0);
619
- rb_define_singleton_method(_class_Digest_KangarooTwelve_Impl, "block_length",
620
- rbx_Digest_KangarooTwelve_Impl_singleton_block_length, 0);
621
- rb_define_singleton_method(_class_Digest_KangarooTwelve_Impl, "digest_length",
622
- rbx_Digest_KangarooTwelve_Impl_singleton_digest_length, 0);
623
- rb_define_singleton_method(_class_Digest_KangarooTwelve_Impl, "customization",
624
- rbx_Digest_KangarooTwelve_Impl_singleton_customization, 0);
625
-
626
- rb_define_method(_class_Digest_KangarooTwelve_Impl, "customization",
627
- rbx_Digest_KangarooTwelve_Impl_customization, 0);
628
- rb_define_method(_class_Digest_KangarooTwelve_Impl, "inspect",
629
- rbx_Digest_KangarooTwelve_inspect, 0);
633
+ _Digest_KangarooTwelve_Impl = rb_define_class_under(_Digest_KangarooTwelve, "Impl",
634
+ rb_path2class("Digest::Base"));
635
+
636
+ rb_define_singleton_method(_Digest_KangarooTwelve_Impl, "new",
637
+ _Digest_KangarooTwelve_Impl_singleton_new, 0);
638
+ rb_define_singleton_method(_Digest_KangarooTwelve_Impl, "block_length",
639
+ _Digest_KangarooTwelve_Impl_singleton_block_length, 0);
640
+ rb_define_singleton_method(_Digest_KangarooTwelve_Impl, "digest_length",
641
+ _Digest_KangarooTwelve_Impl_singleton_digest_length, 0);
642
+ rb_define_singleton_method(_Digest_KangarooTwelve_Impl, "customization",
643
+ _Digest_KangarooTwelve_Impl_singleton_customization, 0);
644
+ rb_define_singleton_method(_Digest_KangarooTwelve_Impl, "customization_hex",
645
+ _Digest_KangarooTwelve_Impl_singleton_customization_hex, 0);
646
+
647
+ rb_define_method(_Digest_KangarooTwelve_Impl, "customization",
648
+ _Digest_KangarooTwelve_Impl_customization, 0);
649
+ rb_define_method(_Digest_KangarooTwelve_Impl, "customization_hex",
650
+ _Digest_KangarooTwelve_Impl_customization_hex, 0);
651
+ rb_define_method(_Digest_KangarooTwelve_Impl, "inspect",
652
+ _Digest_KangarooTwelve_Impl_inspect, 0);
630
653
 
631
654
  /*
632
- * class Digest::KangarooTwelve::Metadata < Data
655
+ * Document-class: Digest::KangarooTwelve::Metadata
633
656
  *
634
657
  * This class represents the internal metadata produced for the
635
658
  * implementation classes.
636
659
  */
637
660
 
638
- _class_Digest_KangarooTwelve_Metadata = rb_define_class_under(_module_Digest_KangarooTwelve, "Metadata",
661
+ _Digest_KangarooTwelve_Metadata = rb_define_class_under(_Digest_KangarooTwelve, "Metadata",
639
662
  rb_path2class("Data"));
640
663
  }
@@ -0,0 +1,99 @@
1
+ /*
2
+ * These works are licensed under the Creative Commons Attribution-ShareAlike
3
+ * 3.0 Unported License. To view a copy of this license, visit
4
+ * http://creativecommons.org/licenses/by-sa/3.0/ or send a letter to Creative
5
+ * Commons, PO Box 1866, Mountain View, CA 94042, USA.
6
+ */
7
+
8
+ #ifndef UTILS_H
9
+ #define UTILS_H
10
+
11
+ /*
12
+ * A simplified hex encoder based on Yannuth's answer in StackOverflow
13
+ * (https://stackoverflow.com/a/17147874/445221).
14
+ *
15
+ * Length of `dest[]` is implied to be twice of `len`.
16
+ */
17
+ static void hex_encode_str_implied(const unsigned char *src, size_t len, unsigned char *dest)
18
+ {
19
+ static const unsigned char table[] = "0123456789abcdef";
20
+
21
+ for (; len > 0; --len) {
22
+ unsigned char c = *src++;
23
+ *dest++ = table[c >> 4];
24
+ *dest++ = table[c & 0x0f];
25
+ }
26
+ }
27
+
28
+ /*
29
+ * Decodes hex string.
30
+ *
31
+ * Length of `dest[]` is implied to be calculated with calc_hex_decoded_str_length.
32
+ *
33
+ * Returns nonzero is successful.
34
+ */
35
+ static int hex_decode_str_implied(const unsigned char *src, size_t len, unsigned char *dest)
36
+ {
37
+ if (len % 2) {
38
+ unsigned char low = *src++;
39
+
40
+ if (low >= '0' && low <= '9') {
41
+ low -= '0';
42
+ } else if (low >= 'A' && low <= 'F') {
43
+ low -= 'A' - 10;
44
+ } else if (low >= 'a' && low <= 'f') {
45
+ low -= 'a' - 10;
46
+ } else {
47
+ return 0;
48
+ }
49
+
50
+ *dest++ = low;
51
+ --len;
52
+ }
53
+
54
+ for (; len > 0; len -= 2) {
55
+ unsigned char high = *src++;
56
+
57
+ if (high >= '0' && high <= '9') {
58
+ high -= '0';
59
+ } else if (high >= 'A' && high <= 'F') {
60
+ high -= 'A' - 10;
61
+ } else if (high >= 'a' && high <= 'f') {
62
+ high -= 'a' - 10;
63
+ } else {
64
+ return 0;
65
+ }
66
+
67
+ unsigned char low = *src++;
68
+
69
+ if (low >= '0' && low <= '9') {
70
+ low -= '0';
71
+ } else if (low >= 'A' && low <= 'F') {
72
+ low -= 'A' - 10;
73
+ } else if (low >= 'a' && low <= 'f') {
74
+ low -= 'a' - 10;
75
+ } else {
76
+ return 0;
77
+ }
78
+
79
+ *dest++ = high << 4 | low;
80
+ }
81
+
82
+ return -1;
83
+ }
84
+
85
+ /*
86
+ * Calculates length of string that would store decoded hex.
87
+ */
88
+ static size_t calc_hex_decoded_str_length(size_t hex_encoded_length)
89
+ {
90
+ if (hex_encoded_length == 0)
91
+ return 0;
92
+
93
+ if (hex_encoded_length % 2)
94
+ ++hex_encoded_length;
95
+
96
+ return hex_encoded_length / 2;
97
+ }
98
+
99
+ #endif
@@ -1,5 +1,5 @@
1
1
  module Digest
2
2
  class KangarooTwelve
3
- VERSION = "0.0.2"
3
+ VERSION = "0.1.0"
4
4
  end
5
5
  end
@@ -1,5 +1,4 @@
1
1
  require 'minitest/autorun'
2
- require 'yaml'
3
2
 
4
3
  TEST_DIR = File.dirname(__FILE__)
5
4
  require File.join(TEST_DIR, %w{ .. lib digest kangarootwelve })
@@ -17,6 +16,10 @@ def get_repeated_0xff(length)
17
16
  [str].cycle(cycles).to_a.join[0...length]
18
17
  end
19
18
 
19
+ def hex_encode(str)
20
+ str.unpack('H*').pop
21
+ end
22
+
20
23
  describe Digest::KangarooTwelve do
21
24
  it "produces implementation classes" do
22
25
  Digest::KangarooTwelve[32].superclass.must_equal Digest::KangarooTwelve::Impl
@@ -42,25 +45,50 @@ describe Digest::KangarooTwelve do
42
45
  Digest::KangarooTwelve.implement(digest_length: 48).new.digest_length.must_equal Digest::KangarooTwelve_48.digest_length
43
46
  end
44
47
 
45
- it "produces a hash" do
46
- Digest::KangarooTwelve.default.new.digest("")
48
+ it "produces hashes" do
49
+ Digest::KangarooTwelve.default.digest("").class.must_equal String
50
+ Digest::KangarooTwelve.default.hexdigest("").class.must_equal String
51
+ Digest::KangarooTwelve.default.new.digest("").class.must_equal String
52
+ Digest::KangarooTwelve.default.new.hexdigest("").class.must_equal String
53
+ end
54
+
55
+ it "produces similar output with its digest and hexdigest methods" do
56
+ digest_a = Digest::KangarooTwelve[32].digest("abcd")
57
+ digest_a.class.must_equal String
58
+ digest_b = Digest::KangarooTwelve[32].new.digest("abcd")
59
+ digest_b.class.must_equal String
60
+ digest_a.must_equal digest_b
61
+ hex_digest_a = Digest::KangarooTwelve[32].hexdigest("abcd")
62
+ hex_digest_a.class.must_equal String
63
+ hex_digest_b = Digest::KangarooTwelve[32].new.hexdigest("abcd")
64
+ hex_digest_b.class.must_equal String
65
+ hex_digest_a.must_equal hex_digest_b
66
+ hex_digest_a.must_equal hex_encode(digest_a)
47
67
  end
48
68
 
49
69
  it "works with customization strings" do
50
- Digest::KangarooTwelve.implement(customization: "abc").new.digest("")
70
+ Digest::KangarooTwelve.implement(customization: "abcd").new.digest("")
51
71
  end
52
72
 
53
73
  it "produces implementations with small and long option names" do
54
- a = Digest::KangarooTwelve.implement(n: "KangarooTwelveTestA", d: 48, b: 512, c: "abc")
55
- b = Digest::KangarooTwelve.implement(name: "KangarooTwelveTestB", digest_length: 48, block_length: 512, customization: "abc")
74
+ a = Digest::KangarooTwelve.implement(n: "KangarooTwelveTestA", d: 48, c: "abcd")
75
+ b = Digest::KangarooTwelve.implement(name: "KangarooTwelveTestB", digest_length: 48, customization: "abcd")
76
+ c = Digest::KangarooTwelve.implement(name: "KangarooTwelveTestC", digest_length: 48, customization_hex: "61626364")
77
+ d = Digest::KangarooTwelve.implement(name: "KangarooTwelveTestD", digest_length: 48, ch: "61626364")
56
78
  a.name.must_equal "Digest::KangarooTwelveTestA"
57
79
  b.name.must_equal "Digest::KangarooTwelveTestB"
58
80
  a.digest_length.must_equal 48
59
- a.digest_length.must_equal b.digest_length
60
- a.block_length.must_equal 512
61
- a.block_length.must_equal b.block_length
62
- a.customization.must_equal "abc"
63
- a.customization.must_equal b.customization
81
+ b.digest_length.must_equal a.digest_length
82
+ a.customization.must_equal "abcd"
83
+ b.customization.must_equal a.customization
84
+ c.customization.must_equal a.customization
85
+ d.customization.must_equal a.customization
86
+ end
87
+
88
+ it "has a declared block length of 8192 bytes" do
89
+ Digest::KangarooTwelve::BLOCK_LENGTH == 8192
90
+ Digest::KangarooTwelve.default.block_length == 8192
91
+ Digest::KangarooTwelve.default.new.block_length == 8192
64
92
  end
65
93
 
66
94
  it "produces valid hashes" do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: digest-kangarootwelve
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - konsolebox
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-08-12 00:00:00.000000000 Z
11
+ date: 2017-08-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -52,7 +52,7 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '5.8'
55
- description: An implementation of KangarooTwelve for Ruby that works on top of Digest::Base.
55
+ description: A KangarooTwelve library that works on top of Digest::Base.
56
56
  email:
57
57
  - konsolebox@gmail.com
58
58
  executables: []
@@ -86,6 +86,7 @@ files:
86
86
  - ext/digest/kangarootwelve/brg_endian.h
87
87
  - ext/digest/kangarootwelve/ext.c
88
88
  - ext/digest/kangarootwelve/extconf.rb
89
+ - ext/digest/kangarootwelve/utils.h
89
90
  - lib/digest/kangarootwelve/version.rb
90
91
  - test/test.rb
91
92
  homepage: https://github.com/konsolebox/digest-kangarootwelve-ruby
@@ -108,7 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
108
109
  version: '0'
109
110
  requirements: []
110
111
  rubyforge_project:
111
- rubygems_version: 2.6.8
112
+ rubygems_version: 2.6.12
112
113
  signing_key:
113
114
  specification_version: 4
114
115
  summary: KangarooTwelve for Ruby