iodine 0.7.60 → 0.8.0

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.
Files changed (160) hide show
  1. checksums.yaml +4 -4
  2. data/.clang-format +15 -0
  3. data/.rspec +1 -0
  4. data/.yardopts +1 -3
  5. data/CHANGELOG.md +132 -12
  6. data/Gemfile +5 -7
  7. data/LICENSE.txt +1 -1
  8. data/README.md +147 -671
  9. data/Rakefile +9 -16
  10. data/examples/chatroom.nru +9 -0
  11. data/examples/client +74 -0
  12. data/examples/config-hello.nru +9 -0
  13. data/examples/config-hello.ru +11 -0
  14. data/examples/config-hijack.ru +13 -0
  15. data/examples/config-shootout.ru +43 -0
  16. data/examples/config.nru +120 -0
  17. data/examples/config.rb +37 -0
  18. data/examples/config.ru +42 -53
  19. data/examples/fake +24 -0
  20. data/examples/hello_neorack +137 -0
  21. data/examples/routes.nru +29 -0
  22. data/examples/routes.ru +42 -0
  23. data/exe/iodine +218 -258
  24. data/ext/iodine/extconf.rb +27 -100
  25. data/ext/iodine/fio-stl.h +128336 -0
  26. data/ext/iodine/fio-stl.md +26396 -0
  27. data/ext/iodine/iodine.c +213 -1393
  28. data/ext/iodine/iodine.h +250 -55
  29. data/ext/iodine/iodine_arg_helper.h +329 -0
  30. data/ext/iodine/iodine_caller.h +234 -25
  31. data/ext/iodine/iodine_cli.h +434 -0
  32. data/ext/iodine/iodine_compression.h +312 -0
  33. data/ext/iodine/iodine_connection.h +3484 -34
  34. data/ext/iodine/iodine_core.h +363 -0
  35. data/ext/iodine/iodine_crypto.h +1441 -0
  36. data/ext/iodine/iodine_defer.h +225 -4
  37. data/ext/iodine/iodine_json.h +738 -4
  38. data/ext/iodine/iodine_listener.h +263 -0
  39. data/ext/iodine/iodine_log.h +297 -0
  40. data/ext/iodine/iodine_minimap.h +543 -0
  41. data/ext/iodine/iodine_mustache.h +461 -4
  42. data/ext/iodine/iodine_pubsub_eng.h +729 -0
  43. data/ext/iodine/iodine_pubsub_history.h +547 -0
  44. data/ext/iodine/iodine_pubsub_msg.h +228 -0
  45. data/ext/iodine/iodine_redis.h +322 -0
  46. data/ext/iodine/iodine_store.h +418 -16
  47. data/ext/iodine/iodine_threads.h +279 -0
  48. data/ext/iodine/iodine_tls.h +515 -6
  49. data/ext/iodine/iodine_utils.h +923 -0
  50. data/iodine.gemspec +37 -42
  51. data/lib/iodine/benchmark.rb +1690 -0
  52. data/lib/iodine/documentation.rb +3085 -0
  53. data/lib/iodine/version.rb +17 -1
  54. data/lib/iodine.rb +54 -255
  55. data/logo.svg +36 -0
  56. data/mustache.md +55 -0
  57. data/sig/iodine.rbs +4 -0
  58. metadata +58 -184
  59. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
  60. data/.github/workflows/ruby.yml +0 -42
  61. data/.gitignore +0 -21
  62. data/LIMITS.md +0 -41
  63. data/SPEC-PubSub-Draft.md +0 -159
  64. data/SPEC-WebSocket-Draft.md +0 -239
  65. data/bin/console +0 -22
  66. data/bin/info.md +0 -353
  67. data/bin/mustache_bench.rb +0 -100
  68. data/bin/poc/Gemfile.lock +0 -23
  69. data/bin/poc/README.md +0 -37
  70. data/bin/poc/config.ru +0 -66
  71. data/bin/poc/gemfile +0 -1
  72. data/bin/poc/www/index.html +0 -57
  73. data/examples/async_task.ru +0 -92
  74. data/examples/bates/README.md +0 -3
  75. data/examples/bates/config.ru +0 -342
  76. data/examples/bates/david+bold.pdf +0 -0
  77. data/examples/bates/public/drop-pdf.png +0 -0
  78. data/examples/bates/public/index.html +0 -600
  79. data/examples/echo.ru +0 -59
  80. data/examples/etag.ru +0 -16
  81. data/examples/hello.ru +0 -29
  82. data/examples/pubsub_engine.ru +0 -81
  83. data/examples/rack3.ru +0 -12
  84. data/examples/redis.ru +0 -70
  85. data/examples/shootout.ru +0 -73
  86. data/examples/sub-protocols.ru +0 -90
  87. data/examples/tcp_client.rb +0 -66
  88. data/examples/x-sendfile.ru +0 -14
  89. data/ext/iodine/fio.c +0 -11995
  90. data/ext/iodine/fio.h +0 -6371
  91. data/ext/iodine/fio_cli.c +0 -431
  92. data/ext/iodine/fio_cli.h +0 -189
  93. data/ext/iodine/fio_json_parser.h +0 -688
  94. data/ext/iodine/fio_siphash.c +0 -157
  95. data/ext/iodine/fio_siphash.h +0 -37
  96. data/ext/iodine/fio_tls.h +0 -129
  97. data/ext/iodine/fio_tls_missing.c +0 -649
  98. data/ext/iodine/fio_tls_openssl.c +0 -1106
  99. data/ext/iodine/fio_tmpfile.h +0 -50
  100. data/ext/iodine/fiobj.h +0 -44
  101. data/ext/iodine/fiobj4fio.h +0 -21
  102. data/ext/iodine/fiobj_ary.c +0 -333
  103. data/ext/iodine/fiobj_ary.h +0 -139
  104. data/ext/iodine/fiobj_data.c +0 -1187
  105. data/ext/iodine/fiobj_data.h +0 -167
  106. data/ext/iodine/fiobj_hash.c +0 -409
  107. data/ext/iodine/fiobj_hash.h +0 -176
  108. data/ext/iodine/fiobj_json.c +0 -622
  109. data/ext/iodine/fiobj_json.h +0 -68
  110. data/ext/iodine/fiobj_mem.h +0 -71
  111. data/ext/iodine/fiobj_mustache.c +0 -317
  112. data/ext/iodine/fiobj_mustache.h +0 -62
  113. data/ext/iodine/fiobj_numbers.c +0 -344
  114. data/ext/iodine/fiobj_numbers.h +0 -127
  115. data/ext/iodine/fiobj_str.c +0 -433
  116. data/ext/iodine/fiobj_str.h +0 -172
  117. data/ext/iodine/fiobject.c +0 -620
  118. data/ext/iodine/fiobject.h +0 -655
  119. data/ext/iodine/hpack.h +0 -1923
  120. data/ext/iodine/http.c +0 -2743
  121. data/ext/iodine/http.h +0 -1002
  122. data/ext/iodine/http1.c +0 -910
  123. data/ext/iodine/http1.h +0 -29
  124. data/ext/iodine/http1_parser.h +0 -878
  125. data/ext/iodine/http_internal.c +0 -1279
  126. data/ext/iodine/http_internal.h +0 -237
  127. data/ext/iodine/http_mime_parser.h +0 -350
  128. data/ext/iodine/iodine_caller.c +0 -230
  129. data/ext/iodine/iodine_connection.c +0 -941
  130. data/ext/iodine/iodine_defer.c +0 -420
  131. data/ext/iodine/iodine_fiobj2rb.h +0 -120
  132. data/ext/iodine/iodine_helpers.c +0 -282
  133. data/ext/iodine/iodine_helpers.h +0 -12
  134. data/ext/iodine/iodine_http.c +0 -1181
  135. data/ext/iodine/iodine_http.h +0 -23
  136. data/ext/iodine/iodine_json.c +0 -302
  137. data/ext/iodine/iodine_mustache.c +0 -567
  138. data/ext/iodine/iodine_pubsub.c +0 -580
  139. data/ext/iodine/iodine_pubsub.h +0 -26
  140. data/ext/iodine/iodine_rack_io.c +0 -273
  141. data/ext/iodine/iodine_rack_io.h +0 -20
  142. data/ext/iodine/iodine_store.c +0 -148
  143. data/ext/iodine/iodine_tcp.c +0 -346
  144. data/ext/iodine/iodine_tcp.h +0 -13
  145. data/ext/iodine/iodine_tls.c +0 -261
  146. data/ext/iodine/mustache_parser.h +0 -1546
  147. data/ext/iodine/redis_engine.c +0 -957
  148. data/ext/iodine/redis_engine.h +0 -79
  149. data/ext/iodine/resp_parser.h +0 -317
  150. data/ext/iodine/websocket_parser.h +0 -506
  151. data/ext/iodine/websockets.c +0 -743
  152. data/ext/iodine/websockets.h +0 -185
  153. data/lib/iodine/connection.rb +0 -61
  154. data/lib/iodine/json.rb +0 -42
  155. data/lib/iodine/mustache.rb +0 -113
  156. data/lib/iodine/pubsub.rb +0 -55
  157. data/lib/iodine/rack_utils.rb +0 -43
  158. data/lib/iodine/tls.rb +0 -16
  159. data/lib/rack/handler/iodine.rb +0 -33
  160. data/logo.png +0 -0
@@ -0,0 +1,923 @@
1
+ #ifndef H___IODINE_UTILS___H
2
+ #define H___IODINE_UTILS___H
3
+ #include "iodine.h"
4
+
5
+ /* *****************************************************************************
6
+ URL encoding Helpers
7
+ ***************************************************************************** */
8
+
9
+ /** Decodes percent encoding, including the `%uxxxx` javascript extension and
10
+ * converting `+` to spaces. */
11
+ FIO_IFUNC VALUE iodine_utils_encode_with_encoding(
12
+ int argc,
13
+ VALUE *argv,
14
+ VALUE self,
15
+ int (*writer)(fio_str_info_s *dest,
16
+ fio_string_realloc_fn reallocate,
17
+ const void *encoded,
18
+ size_t len)) {
19
+ if (!argc || argc > 2)
20
+ rb_raise(rb_eArgError, "Wrong number of arguments (%d)", argc);
21
+ rb_check_type(argv[0], RUBY_T_STRING);
22
+ if (!RSTRING_LEN(argv[0]))
23
+ return argv[0];
24
+ rb_encoding *enc = NULL;
25
+ if (argc == 2)
26
+ enc = (TYPE(argv[1]) == T_STRING) ? rb_enc_find(RSTRING_PTR(argv[1]))
27
+ : rb_enc_get(argv[1]);
28
+ if (!enc)
29
+ enc = IodineUTF8Encoding;
30
+ FIO_STR_INFO_TMP_VAR(tmp, 512);
31
+ char *org = tmp.buf;
32
+ writer(&tmp,
33
+ FIO_STRING_ALLOC_COPY,
34
+ RSTRING_PTR(argv[0]),
35
+ RSTRING_LEN(argv[0]));
36
+ self = rb_str_new(tmp.buf, tmp.len);
37
+ rb_enc_associate(self, enc);
38
+ if (org != tmp.buf)
39
+ FIO_STRING_FREE2(tmp);
40
+ return self;
41
+ }
42
+
43
+ FIO_IFUNC VALUE
44
+ iodine_utils_encode_internal(VALUE mod,
45
+ VALUE arg,
46
+ int (*writer)(fio_str_info_s *dest,
47
+ fio_string_realloc_fn reallocate,
48
+ const void *encoded,
49
+ size_t len)) {
50
+ rb_check_type(arg, RUBY_T_STRING);
51
+ if (!RSTRING_LEN(arg))
52
+ return arg;
53
+ FIO_STR_INFO_TMP_VAR(tmp, 512);
54
+ const char *org = tmp.buf;
55
+ writer(&tmp, FIO_STRING_ALLOC_COPY, RSTRING_PTR(arg), RSTRING_LEN(arg));
56
+ arg = rb_str_new(tmp.buf, tmp.len);
57
+ rb_enc_associate(arg, IodineUTF8Encoding);
58
+ if (org != tmp.buf)
59
+ FIO_STRING_FREE2(tmp);
60
+ return arg;
61
+ }
62
+ FIO_IFUNC VALUE
63
+ iodine_utils_encode1_internal(VALUE mod,
64
+ VALUE arg,
65
+ int (*writer)(fio_str_info_s *dest,
66
+ fio_string_realloc_fn reallocate,
67
+ const void *encoded,
68
+ size_t len)) {
69
+ rb_check_type(arg, RUBY_T_STRING);
70
+ if (!RSTRING_LEN(arg))
71
+ return arg;
72
+ FIO_STR_INFO_TMP_VAR(tmp, 512);
73
+ const char *org = tmp.buf;
74
+ writer(&tmp, FIO_STRING_ALLOC_COPY, RSTRING_PTR(arg), RSTRING_LEN(arg));
75
+ rb_str_set_len(arg, 0);
76
+ rb_str_cat(arg, tmp.buf, tmp.len);
77
+ rb_enc_associate(arg, IodineUTF8Encoding);
78
+ if (org != tmp.buf)
79
+ FIO_STRING_FREE2(tmp);
80
+ return arg;
81
+ }
82
+
83
+ /** Encodes a String using percent encoding (i.e., URI encoding). */
84
+ FIO_SFUNC VALUE iodine_utils_encode_url(VALUE mod, VALUE arg) {
85
+ return iodine_utils_encode_internal(mod, arg, fio_string_write_url_enc);
86
+ }
87
+
88
+ /** Encodes a String using percent encoding (i.e., URI encoding). */
89
+ FIO_SFUNC VALUE iodine_utils_encode_url1(VALUE mod, VALUE arg) {
90
+ return iodine_utils_encode1_internal(mod, arg, fio_string_write_url_enc);
91
+ }
92
+
93
+ /** Encodes a String using percent encoding (i.e., URI encoding). */
94
+ FIO_SFUNC VALUE iodine_utils_encode_path(VALUE mod, VALUE arg) {
95
+ return iodine_utils_encode_internal(mod, arg, fio_string_write_url_enc);
96
+ }
97
+
98
+ /** Encodes a String in place using percent encoding (i.e., URI encoding). */
99
+ FIO_SFUNC VALUE iodine_utils_encode_path1(VALUE mod, VALUE arg) {
100
+ return iodine_utils_encode1_internal(mod, arg, fio_string_write_url_enc);
101
+ }
102
+
103
+ /** Decodes percent encoding, including the `%uxxxx` javascript extension and
104
+ * converting `+` to spaces. */
105
+ FIO_SFUNC VALUE iodine_utils_decode_url(int argc, VALUE *argv, VALUE self) {
106
+ return iodine_utils_encode_with_encoding(argc,
107
+ argv,
108
+ self,
109
+ fio_string_write_url_dec);
110
+ }
111
+
112
+ /** Decodes percent encoding in place, including the `%uxxxx` javascript
113
+ * extension and converting `+` to spaces. */
114
+ FIO_SFUNC VALUE iodine_utils_decode_url1(VALUE mod, VALUE arg) {
115
+ return iodine_utils_encode1_internal(mod, arg, fio_string_write_url_dec);
116
+ }
117
+
118
+ /** Decodes percent encoding, including the `%uxxxx` javascript extension. */
119
+ FIO_SFUNC VALUE iodine_utils_decode_path(int argc, VALUE *argv, VALUE self) {
120
+ return iodine_utils_encode_with_encoding(argc,
121
+ argv,
122
+ self,
123
+ fio_string_write_path_dec);
124
+ }
125
+
126
+ /** Decodes percent encoding in place, including the `%uxxxx` javascript. */
127
+ FIO_SFUNC VALUE iodine_utils_decode_path1(VALUE mod, VALUE arg) {
128
+ return iodine_utils_encode1_internal(mod, arg, fio_string_write_path_dec);
129
+ }
130
+
131
+ /** Escapes String using HTML escape encoding. */
132
+ FIO_SFUNC VALUE iodine_utils_encode_html(VALUE mod, VALUE arg) {
133
+ return iodine_utils_encode_internal(mod, arg, fio_string_write_html_escape);
134
+ }
135
+
136
+ /**
137
+ * Escapes String in place using HTML escape encoding.
138
+ *
139
+ * Note: this function significantly increases the number of escaped characters
140
+ * when compared to the native implementation.
141
+ */
142
+ FIO_SFUNC VALUE iodine_utils_encode_html1(VALUE mod, VALUE arg) {
143
+ return iodine_utils_encode1_internal(mod, arg, fio_string_write_html_escape);
144
+ }
145
+
146
+ /** Decodes an HTML escaped String. */
147
+ FIO_SFUNC VALUE iodine_utils_decode_html(int argc, VALUE *argv, VALUE self) {
148
+ return iodine_utils_encode_with_encoding(argc,
149
+ argv,
150
+ self,
151
+ fio_string_write_html_unescape);
152
+ }
153
+
154
+ /** Decodes an HTML escaped String in place. */
155
+ FIO_SFUNC VALUE iodine_utils_decode_html1(VALUE mod, VALUE arg) {
156
+ return iodine_utils_encode1_internal(mod,
157
+ arg,
158
+ fio_string_write_html_unescape);
159
+ }
160
+
161
+ /* *****************************************************************************
162
+ Time to String Helpers
163
+ ***************************************************************************** */
164
+
165
+ FIO_IFUNC time_t iodine_utils_rb2time(VALUE rtm) {
166
+ rtm = (rtm == Qnil ? LONG2NUM(0)
167
+ : rb_funcallv(rtm, rb_intern("to_i"), 0, NULL));
168
+ return NUM2LONG(rtm) ? NUM2LONG(rtm) : fio_time_real().tv_sec;
169
+ }
170
+
171
+ /** Takes a Time object and returns a String conforming to RFC 2109. */
172
+ FIO_SFUNC VALUE iodine_utils_rfc2109(VALUE mod, VALUE rtm) {
173
+ time_t time_requested = iodine_utils_rb2time(rtm);
174
+ VALUE str = rb_str_buf_new(34);
175
+ rb_str_set_len(str, fio_time2rfc2109(RSTRING_PTR(str), time_requested));
176
+ rb_enc_associate(str, IodineUTF8Encoding);
177
+ return str;
178
+ (void)mod;
179
+ }
180
+ /** Takes a Time object and returns a String conforming to RFC 2822. */
181
+ FIO_SFUNC VALUE iodine_utils_rfc2822(VALUE mod, VALUE rtm) {
182
+ time_t time_requested = iodine_utils_rb2time(rtm);
183
+ VALUE str = rb_str_buf_new(34);
184
+ rb_str_set_len(str, fio_time2rfc2822(RSTRING_PTR(str), time_requested));
185
+ rb_enc_associate(str, IodineUTF8Encoding);
186
+ return str;
187
+ (void)mod;
188
+ }
189
+ /** Takes a Time object and returns a String conforming to RFC 7231. */
190
+ FIO_SFUNC VALUE iodine_utils_rfc7231(VALUE mod, VALUE rtm) {
191
+ time_t time_requested = iodine_utils_rb2time(rtm);
192
+ VALUE str = rb_str_buf_new(34);
193
+ rb_str_set_len(str, fio_time2rfc7231(RSTRING_PTR(str), time_requested));
194
+ rb_enc_associate(str, IodineUTF8Encoding);
195
+ return str;
196
+ (void)mod;
197
+ }
198
+
199
+ /* *****************************************************************************
200
+ String Secure Compare
201
+ ***************************************************************************** */
202
+ // clang-format off
203
+ /**
204
+ Securely compares two String objects to see if they are equal.
205
+
206
+ Designed to be secure against timing attacks when both String objects are of the same length.
207
+
208
+ require 'iodine'
209
+ require 'rack'
210
+ require 'benchmark'
211
+ def prove_secure_compare(name, mthd, length = 4096)
212
+ a = 0;
213
+ b = 0;
214
+ str1 = Array.new(length) { 'a' } .join; str2 = Array.new(length) { 'a' } .join;
215
+ bm = Benchmark.measure do
216
+ 1024.times do
217
+ tmp = Benchmark.measure {4096.times {mthd.call(str1, str2)}}
218
+ str1[0] = 'b'
219
+ tmp2 = Benchmark.measure {4096.times {mthd.call(str1, str2)}}
220
+ str1[0] = 'a'
221
+ tmp = tmp2.total - tmp.total
222
+ a += 1 if tmp >= 0
223
+ b += 1 if tmp <= 0
224
+ end
225
+ end
226
+ puts "#{name} timing ratio #{a}:#{b}\n#{bm.to_s}\n"
227
+ end
228
+ prove_secure_compare("String == (short string)", (Proc.new {|a,b| a == b } ), 47);
229
+ prove_secure_compare("Iodine::Utils.secure_compare (short string)", Iodine::Utils.method(:secure_compare), 47)
230
+ prove_secure_compare("Rack::Utils.secure_compare (short string)", Rack::Utils.method(:secure_compare), 47)
231
+ prove_secure_compare("String == (long string)", (Proc.new {|a,b| a == b } ), 1024)
232
+ prove_secure_compare("Iodine::Utils.secure_compare (long string)", Iodine::Utils.method(:secure_compare), 1024)
233
+ # prove_secure_compare("Rack::Utils.secure_compare (short string)", Rack::Utils.method(:secure_compare), 1024) # VERY slow
234
+
235
+ */
236
+ FIO_SFUNC VALUE iodine_utils_is_eq(VALUE mod, VALUE a, VALUE b) { // clang-format on
237
+ rb_check_type(a, RUBY_T_STRING);
238
+ rb_check_type(b, RUBY_T_STRING);
239
+ if (RSTRING_LEN(a) != RSTRING_LEN(b))
240
+ return RUBY_Qfalse;
241
+ return fio_ct_is_eq(RSTRING_PTR(a), RSTRING_PTR(b), RSTRING_LEN(a))
242
+ ? RUBY_Qtrue
243
+ : RUBY_Qfalse;
244
+ }
245
+
246
+ /* *****************************************************************************
247
+ Randomness and Friends
248
+ ***************************************************************************** */
249
+
250
+ FIO_DEFINE_RANDOM128_FN(static, iodine_random, 10, 0)
251
+
252
+ FIO_SFUNC VALUE iodine_utils_hmac512(VALUE self, VALUE secret, VALUE massage) {
253
+ rb_check_type(secret, RUBY_T_STRING);
254
+ rb_check_type(massage, RUBY_T_STRING);
255
+ fio_buf_info_s k = IODINE_RSTR_INFO(secret);
256
+ fio_buf_info_s m = IODINE_RSTR_INFO(massage);
257
+ fio_u512 h = fio_sha512_hmac(k.buf, k.len, m.buf, m.len);
258
+ FIO_STR_INFO_TMP_VAR(out, 128);
259
+ fio_string_write_base64enc(&out, NULL, h.u8, 64, 0);
260
+ return rb_str_new(out.buf, out.len);
261
+ }
262
+
263
+ FIO_SFUNC VALUE iodine_utils_hmac256(VALUE self, VALUE secret, VALUE massage) {
264
+ rb_check_type(secret, RUBY_T_STRING);
265
+ rb_check_type(massage, RUBY_T_STRING);
266
+ fio_buf_info_s k = IODINE_RSTR_INFO(secret);
267
+ fio_buf_info_s m = IODINE_RSTR_INFO(massage);
268
+ fio_u256 h = fio_sha256_hmac(k.buf, k.len, m.buf, m.len);
269
+ FIO_STR_INFO_TMP_VAR(out, 64);
270
+ fio_string_write_base64enc(&out, NULL, h.u8, 32, 0);
271
+ return rb_str_new(out.buf, out.len);
272
+ }
273
+
274
+ FIO_SFUNC VALUE iodine_utils_sha256(VALUE self, VALUE data) {
275
+ rb_check_type(data, RUBY_T_STRING);
276
+ fio_u256 h = fio_sha256(RSTRING_PTR(data), RSTRING_LEN(data));
277
+ return rb_str_new((const char *)h.u8, 32);
278
+ }
279
+
280
+ FIO_SFUNC VALUE iodine_utils_sha512(VALUE self, VALUE data) {
281
+ rb_check_type(data, RUBY_T_STRING);
282
+ fio_u512 h = fio_sha512(RSTRING_PTR(data), RSTRING_LEN(data));
283
+ return rb_str_new((const char *)h.u8, 64);
284
+ }
285
+
286
+ FIO_SFUNC VALUE iodine_utils_sha3_256(VALUE self, VALUE data) {
287
+ rb_check_type(data, RUBY_T_STRING);
288
+ uint8_t out[32];
289
+ fio_sha3_256(out, RSTRING_PTR(data), RSTRING_LEN(data));
290
+ return rb_str_new((const char *)out, 32);
291
+ }
292
+
293
+ FIO_SFUNC VALUE iodine_utils_sha3_512(VALUE self, VALUE data) {
294
+ rb_check_type(data, RUBY_T_STRING);
295
+ uint8_t out[64];
296
+ fio_sha3_512(out, RSTRING_PTR(data), RSTRING_LEN(data));
297
+ return rb_str_new((const char *)out, 64);
298
+ }
299
+
300
+ FIO_SFUNC VALUE iodine_utils_sha3_224(VALUE self, VALUE data) {
301
+ rb_check_type(data, RUBY_T_STRING);
302
+ uint8_t out[28];
303
+ fio_sha3_224(out, RSTRING_PTR(data), RSTRING_LEN(data));
304
+ return rb_str_new((const char *)out, 28);
305
+ }
306
+
307
+ FIO_SFUNC VALUE iodine_utils_sha3_384(VALUE self, VALUE data) {
308
+ rb_check_type(data, RUBY_T_STRING);
309
+ uint8_t out[48];
310
+ fio_sha3_384(out, RSTRING_PTR(data), RSTRING_LEN(data));
311
+ return rb_str_new((const char *)out, 48);
312
+ }
313
+
314
+ /**
315
+ * Computes SHAKE128 extendable-output function.
316
+ *
317
+ * @param data [String] Input data to hash
318
+ * @param length: [Integer] Desired output length in bytes (default: 32)
319
+ * @return [String] Binary string of specified length
320
+ */
321
+ FIO_SFUNC VALUE iodine_utils_shake128(int argc, VALUE *argv, VALUE self) {
322
+ fio_buf_info_s data = FIO_BUF_INFO0;
323
+ int64_t length = 32;
324
+ iodine_rb2c_arg(argc,
325
+ argv,
326
+ IODINE_ARG_BUF(data, 0, NULL, 1),
327
+ IODINE_ARG_NUM(length, 0, "length", 0));
328
+ if (length < 1 || length > 0x0FFFFFFF)
329
+ rb_raise(rb_eArgError, "length must be between 1 and 268435455");
330
+ VALUE out = rb_str_buf_new((long)length);
331
+ rb_str_set_len(out, (long)length);
332
+ fio_shake128(RSTRING_PTR(out), (size_t)length, data.buf, data.len);
333
+ return out;
334
+ (void)self;
335
+ }
336
+
337
+ /**
338
+ * Computes SHAKE256 extendable-output function.
339
+ *
340
+ * @param data [String] Input data to hash
341
+ * @param length: [Integer] Desired output length in bytes (default: 64)
342
+ * @return [String] Binary string of specified length
343
+ */
344
+ FIO_SFUNC VALUE iodine_utils_shake256(int argc, VALUE *argv, VALUE self) {
345
+ fio_buf_info_s data = FIO_BUF_INFO0;
346
+ int64_t length = 64;
347
+ iodine_rb2c_arg(argc,
348
+ argv,
349
+ IODINE_ARG_BUF(data, 0, NULL, 1),
350
+ IODINE_ARG_NUM(length, 0, "length", 0));
351
+ if (length < 1 || length > 0x0FFFFFFF)
352
+ rb_raise(rb_eArgError, "length must be between 1 and 268435455");
353
+ VALUE out = rb_str_buf_new((long)length);
354
+ rb_str_set_len(out, (long)length);
355
+ fio_shake256(RSTRING_PTR(out), (size_t)length, data.buf, data.len);
356
+ return out;
357
+ (void)self;
358
+ }
359
+
360
+ /**
361
+ * Computes SHA-1 hash (20 bytes).
362
+ *
363
+ * WARNING: SHA-1 is cryptographically broken. Use only for legacy protocols
364
+ * that require it (e.g., WebSockets, TOTP compatibility).
365
+ *
366
+ * @param data [String] Input data to hash
367
+ * @return [String] 20-byte binary hash
368
+ */
369
+ FIO_SFUNC VALUE iodine_utils_sha1(VALUE self, VALUE data) {
370
+ rb_check_type(data, RUBY_T_STRING);
371
+ fio_sha1_s h = fio_sha1(RSTRING_PTR(data), (uint64_t)RSTRING_LEN(data));
372
+ return rb_str_new((const char *)h.digest, 20);
373
+ (void)self;
374
+ }
375
+
376
+ /**
377
+ * Computes CRC32 checksum (ITU-T V.42 / ISO 3309 / gzip polynomial 0xEDB88320).
378
+ *
379
+ * Uses a slicing-by-8 algorithm for high throughput. This is the standard CRC32
380
+ * used by gzip, zlib, and Ethernet — NOT the Castagnoli (CRC32-C) variant.
381
+ *
382
+ * Supports incremental computation: pass the previous return value as
383
+ * `initial_crc` to continue a checksum over multiple buffers.
384
+ *
385
+ * @param data [String] Input data to checksum
386
+ * @param initial_crc: [Integer] Starting CRC value (default: 0)
387
+ * @return [Integer] 32-bit CRC32 checksum
388
+ */
389
+ FIO_SFUNC VALUE iodine_utils_crc32(int argc, VALUE *argv, VALUE self) {
390
+ fio_buf_info_s data = FIO_BUF_INFO0;
391
+ uint32_t initial_crc = 0;
392
+ iodine_rb2c_arg(argc,
393
+ argv,
394
+ IODINE_ARG_BUF(data, 0, NULL, 1),
395
+ IODINE_ARG_U32(initial_crc, 0, "initial_crc", 0));
396
+ uint32_t crc = fio_crc32(data.buf, data.len, initial_crc);
397
+ return UINT2NUM(crc);
398
+ (void)self;
399
+ }
400
+
401
+ /**
402
+ * Computes facil.io Risky Hash (non-cryptographic, fast).
403
+ *
404
+ * @param data [String] Input data to hash
405
+ * @param seed: [Integer] Optional seed value (default: 0)
406
+ * @return [Integer] 64-bit hash value
407
+ */
408
+ FIO_SFUNC VALUE iodine_utils_risky_hash(int argc, VALUE *argv, VALUE self) {
409
+ fio_buf_info_s data = FIO_BUF_INFO0;
410
+ uint64_t seed = 0;
411
+ iodine_rb2c_arg(argc,
412
+ argv,
413
+ IODINE_ARG_BUF(data, 0, NULL, 1),
414
+ IODINE_ARG_U64(seed, 0, "seed", 0));
415
+ uint64_t hash = fio_risky_hash(data.buf, data.len, seed);
416
+ return ULL2NUM(hash);
417
+ (void)self;
418
+ }
419
+
420
+ /**
421
+ * Computes facil.io Risky256 Hash (non-cryptographic, 256-bit).
422
+ *
423
+ * @param data [String] Input data to hash
424
+ * @return [String] 32-byte binary hash
425
+ */
426
+ FIO_SFUNC VALUE iodine_utils_risky256(VALUE self, VALUE data) {
427
+ rb_check_type(data, RUBY_T_STRING);
428
+ fio_u256 h = fio_risky256(RSTRING_PTR(data), (uint64_t)RSTRING_LEN(data));
429
+ return rb_str_new((const char *)h.u8, 32);
430
+ (void)self;
431
+ }
432
+
433
+ /**
434
+ * Computes facil.io Risky512 Hash (non-cryptographic, 512-bit).
435
+ *
436
+ * First 256 bits are identical to risky256 (SHAKE-style extension).
437
+ *
438
+ * @param data [String] Input data to hash
439
+ * @return [String] 64-byte binary hash
440
+ */
441
+ FIO_SFUNC VALUE iodine_utils_risky512(VALUE self, VALUE data) {
442
+ rb_check_type(data, RUBY_T_STRING);
443
+ fio_u512 h = fio_risky512(RSTRING_PTR(data), (uint64_t)RSTRING_LEN(data));
444
+ return rb_str_new((const char *)h.u8, 64);
445
+ (void)self;
446
+ }
447
+
448
+ /**
449
+ * Computes facil.io Risky256 HMAC (non-cryptographic, keyed 256-bit).
450
+ *
451
+ * @param key [String] The secret key
452
+ * @param data [String] Input data to hash
453
+ * @return [String] 32-byte binary HMAC
454
+ */
455
+ FIO_SFUNC VALUE iodine_utils_risky256_hmac(VALUE self, VALUE key, VALUE data) {
456
+ rb_check_type(key, RUBY_T_STRING);
457
+ rb_check_type(data, RUBY_T_STRING);
458
+ fio_u256 h = fio_risky256_hmac(RSTRING_PTR(key),
459
+ (size_t)RSTRING_LEN(key),
460
+ RSTRING_PTR(data),
461
+ (size_t)RSTRING_LEN(data));
462
+ return rb_str_new((const char *)h.u8, 32);
463
+ (void)self;
464
+ }
465
+
466
+ /**
467
+ * Computes facil.io Risky512 HMAC (non-cryptographic, keyed 512-bit).
468
+ *
469
+ * @param key [String] The secret key
470
+ * @param data [String] Input data to hash
471
+ * @return [String] 64-byte binary HMAC
472
+ */
473
+ FIO_SFUNC VALUE iodine_utils_risky512_hmac(VALUE self, VALUE key, VALUE data) {
474
+ rb_check_type(key, RUBY_T_STRING);
475
+ rb_check_type(data, RUBY_T_STRING);
476
+ fio_u512 h = fio_risky512_hmac(RSTRING_PTR(key),
477
+ (size_t)RSTRING_LEN(key),
478
+ RSTRING_PTR(data),
479
+ (size_t)RSTRING_LEN(data));
480
+ return rb_str_new((const char *)h.u8, 64);
481
+ (void)self;
482
+ }
483
+
484
+ /**
485
+ * Generates cryptographically secure random bytes using the system CSPRNG.
486
+ *
487
+ * Falls back to the facil.io PRNG if the system CSPRNG fails.
488
+ *
489
+ * @param bytes: [Integer] Number of bytes to generate (default: 32)
490
+ * @return [String] Binary string of random bytes
491
+ */
492
+ FIO_SFUNC VALUE iodine_utils_secure_random(int argc, VALUE *argv, VALUE self) {
493
+ size_t size = 32;
494
+ iodine_rb2c_arg(argc, argv, IODINE_ARG_SIZE_T(size, 0, "bytes", 0));
495
+ if ((size - 1) > 0x0FFFFFFF)
496
+ rb_raise(rb_eRangeError, "`bytes` count is out of range.");
497
+ VALUE r = rb_str_buf_new(size);
498
+ if (fio_rand_bytes_secure(RSTRING_PTR(r), size))
499
+ fio_rand_bytes(RSTRING_PTR(r), size);
500
+ rb_str_set_len(r, size);
501
+ return r;
502
+ (void)self;
503
+ }
504
+
505
+ FIO_SFUNC VALUE iodine_utils_blake2b(int argc, VALUE *argv, VALUE self) {
506
+ fio_buf_info_s data = FIO_BUF_INFO0;
507
+ fio_buf_info_s key = FIO_BUF_INFO0;
508
+ int64_t len = 64;
509
+ iodine_rb2c_arg(argc,
510
+ argv,
511
+ IODINE_ARG_BUF(data, 0, NULL, 1),
512
+ IODINE_ARG_BUF(key, 0, "key", 0),
513
+ IODINE_ARG_NUM(len, 0, "len", 0));
514
+ if (len < 1 || len > 64)
515
+ rb_raise(rb_eArgError, "len must be between 1 and 64");
516
+ uint8_t out[64];
517
+ fio_blake2b_hash(out, (size_t)len, data.buf, data.len, key.buf, key.len);
518
+ return rb_enc_str_new((const char *)out, (long)len, IodineBinaryEncoding);
519
+ (void)self;
520
+ }
521
+
522
+ FIO_SFUNC VALUE iodine_utils_blake2s(int argc, VALUE *argv, VALUE self) {
523
+ fio_buf_info_s data = FIO_BUF_INFO0;
524
+ fio_buf_info_s key = FIO_BUF_INFO0;
525
+ int64_t len = 32;
526
+ iodine_rb2c_arg(argc,
527
+ argv,
528
+ IODINE_ARG_BUF(data, 0, NULL, 1),
529
+ IODINE_ARG_BUF(key, 0, "key", 0),
530
+ IODINE_ARG_NUM(len, 0, "len", 0));
531
+ if (len < 1 || len > 32)
532
+ rb_raise(rb_eArgError, "len must be between 1 and 32");
533
+ uint8_t out[32];
534
+ fio_blake2s_hash(out, (size_t)len, data.buf, data.len, key.buf, key.len);
535
+ return rb_enc_str_new((const char *)out, (long)len, IodineBinaryEncoding);
536
+ (void)self;
537
+ }
538
+
539
+ FIO_SFUNC VALUE iodine_utils_hmac_sha1(VALUE self,
540
+ VALUE secret,
541
+ VALUE massage) {
542
+ rb_check_type(secret, RUBY_T_STRING);
543
+ rb_check_type(massage, RUBY_T_STRING);
544
+ fio_buf_info_s k = IODINE_RSTR_INFO(secret);
545
+ fio_buf_info_s m = IODINE_RSTR_INFO(massage);
546
+ fio_sha1_s h = fio_sha1_hmac(k.buf, k.len, m.buf, m.len);
547
+ FIO_STR_INFO_TMP_VAR(out, 40);
548
+ fio_string_write_base64enc(&out, NULL, h.digest, 20, 0);
549
+ return rb_str_new(out.buf, out.len);
550
+ }
551
+
552
+ FIO_SFUNC VALUE iodine_utils_hmac_poly(VALUE self,
553
+ VALUE secret,
554
+ VALUE massage) {
555
+ rb_check_type(secret, RUBY_T_STRING);
556
+ rb_check_type(massage, RUBY_T_STRING);
557
+ fio_buf_info_s k = IODINE_RSTR_INFO(secret);
558
+ fio_buf_info_s m = IODINE_RSTR_INFO(massage);
559
+ fio_u256 fallback = {0};
560
+ fio_u128 h;
561
+ if (k.len < 256) {
562
+ fio_memcpy255x(fallback.u8, k.buf, k.len);
563
+ if (k.len < 10)
564
+ fallback = fio_sha512(k.buf, k.len).u256[0];
565
+ k.buf = (char *)fallback.u8;
566
+ }
567
+ fio_poly1305_auth(h.u8, m.buf, m.len, NULL, 0, k.buf);
568
+
569
+ FIO_STR_INFO_TMP_VAR(out, 32);
570
+ fio_string_write_base64enc(&out, NULL, h.u8, 16, 0);
571
+ return rb_str_new(out.buf, out.len);
572
+ }
573
+
574
+ FIO_SFUNC VALUE iodine_utils_uuid(int argc, VALUE *argv, VALUE self) {
575
+ VALUE r = Qnil;
576
+ fio_u128 rand = iodine_random128();
577
+ fio_buf_info_s secret = {0};
578
+ fio_buf_info_s info = {0};
579
+ FIO_STR_INFO_TMP_VAR(str, 128);
580
+ iodine_rb2c_arg(argc,
581
+ argv,
582
+ IODINE_ARG_BUF(secret, 0, "secret", 0),
583
+ IODINE_ARG_BUF(info, 0, "info", 0));
584
+ if (secret.buf && info.buf) {
585
+ fio_sha512_s sh2 = fio_sha512_init();
586
+ fio_u1024 mk = {0};
587
+ if (secret.len <= 128) {
588
+ fio_memcpy255x(mk.u8, secret.buf, secret.len);
589
+ mk.u64[15] ^= secret.len;
590
+ for (size_t i = 0; i < 16; ++i)
591
+ mk.u64[i] ^= 0x3636363636363636ULL;
592
+ secret.buf = (char *)mk.u8;
593
+ secret.len = 128;
594
+ }
595
+ fio_sha512_consume(&sh2, secret.buf, secret.len);
596
+ fio_sha512_consume(&sh2, info.buf, info.len);
597
+ fio_u512 tmp = fio_sha512_finalize(&sh2);
598
+ rand.u64[0] = tmp.u64[0] + tmp.u64[1] + tmp.u64[2] + tmp.u64[3];
599
+ rand.u64[1] = tmp.u64[4] + tmp.u64[5] + tmp.u64[6] + tmp.u64[7];
600
+ /* support the vendor specific UUID variant. */
601
+ rand.u8[6] &= 0x0F;
602
+ rand.u8[6] |= 0x80;
603
+ rand.u8[8] &= 0x3F;
604
+ rand.u8[8] |= 0x80;
605
+ } else if (secret.buf || info.buf) {
606
+ if (info.buf)
607
+ secret = info;
608
+ uint64_t tmp = fio_risky_hash(secret.buf, secret.len, 0);
609
+ rand.u64[0] += tmp;
610
+ rand.u64[1] -= tmp;
611
+ goto random_uuid;
612
+ } else {
613
+ random_uuid:
614
+ /* support the random UUID version significant bits. */
615
+ rand.u8[6] &= 0x0F;
616
+ rand.u8[6] |= 0x40;
617
+ rand.u8[8] &= 0x3F;
618
+ rand.u8[8] |= 0x80;
619
+ }
620
+ fio_ltoa16u(str.buf, rand.u32[0], 8);
621
+ str.buf[8] = '-';
622
+ fio_ltoa16u(str.buf + 9, rand.u16[2], 4);
623
+ str.buf[13] = '-';
624
+ fio_ltoa16u(str.buf + 14, rand.u16[3], 4);
625
+ str.buf[18] = '-';
626
+ fio_ltoa16u(str.buf + 19, rand.u16[4], 4);
627
+ str.buf[23] = '-';
628
+ fio_ltoa16u(str.buf + 24, rand.u16[5], 4);
629
+ fio_ltoa16u(str.buf + 28, rand.u32[3], 8);
630
+ str.buf[36] = '0';
631
+ r = rb_str_new(str.buf, 36);
632
+ return r;
633
+ (void)self;
634
+ }
635
+
636
+ /** Generates random data, high entropy, not cryptographically tested. */
637
+ FIO_SFUNC VALUE iodine_utils_random(int argc, VALUE *argv, VALUE self) {
638
+ VALUE r = Qnil;
639
+ size_t size = 16;
640
+ iodine_rb2c_arg(argc, argv, IODINE_ARG_SIZE_T(size, 0, "bytes", 0));
641
+ if ((size - 1) > 0x0FFFFFFF)
642
+ rb_raise(rb_eRangeError, "`bytes` count is out of range.");
643
+ r = rb_str_buf_new(size);
644
+ iodine_random_bytes(RSTRING_PTR(r), size);
645
+ rb_str_set_len(r, size);
646
+ return r;
647
+ (void)self;
648
+ }
649
+
650
+ /**
651
+ * Generates a Time-based One-Time Password (TOTP) code.
652
+ *
653
+ * Returns a 6-digit TOTP code as an Integer, compatible with Google
654
+ * Authenticator and similar apps.
655
+ *
656
+ * # Generate TOTP for current time window
657
+ * code = Iodine::Utils.totp(secret: my_secret)
658
+ *
659
+ * # Generate TOTP with custom interval (default is 30 seconds)
660
+ * code = Iodine::Utils.totp(secret: my_secret, interval: 60)
661
+ *
662
+ * # Generate TOTP for a different time window (offset in interval units)
663
+ * code = Iodine::Utils.totp(secret: my_secret, offset: -1) # previous
664
+ * window
665
+ *
666
+ * Parameters:
667
+ * - `secret:` (required) - The shared secret key (raw bytes or Base32 decoded)
668
+ * - `offset:` (optional) - Time offset in interval units (default: 0)
669
+ * - `interval:` (optional) - Time window in seconds (default: 30)
670
+ *
671
+ * Returns: Integer - A 6-digit TOTP code
672
+ */
673
+ FIO_SFUNC VALUE iodine_utils_totp(int argc, VALUE *argv, VALUE self) {
674
+ fio_buf_info_s secret = {0};
675
+ int64_t offset = 0;
676
+ size_t interval = 0;
677
+
678
+ iodine_rb2c_arg(argc,
679
+ argv,
680
+ IODINE_ARG_BUF(secret, 0, "secret", 1),
681
+ IODINE_ARG_NUM(offset, 0, "offset", 0),
682
+ IODINE_ARG_SIZE_T(interval, 0, "interval", 0));
683
+ if (!interval)
684
+ interval = 30;
685
+
686
+ uint32_t otp = fio_otp(secret, .offset = offset, .interval = interval);
687
+ return UINT2NUM(otp);
688
+ (void)self;
689
+ }
690
+
691
+ /**
692
+ * Generates a new TOTP secret suitable for Google Authenticator.
693
+ *
694
+ * # Generate a secret with default length (20 bytes)
695
+ * secret = Iodine::Utils.totp_secret
696
+ *
697
+ * # Generate a longer secret (32 bytes)
698
+ * secret = Iodine::Utils.totp_secret(len: 32)
699
+ *
700
+ * The secret is generated using cryptographically secure random bytes
701
+ * and encoded in Base32 (uppercase, no padding) for compatibility with
702
+ * authenticator apps.
703
+ *
704
+ * Parameters:
705
+ * - `len:` (optional) - Length of the secret in bytes (default: 20, range:
706
+ * 10-64)
707
+ *
708
+ * Returns: String - Base32 encoded secret suitable for QR codes and
709
+ * authenticator apps
710
+ */
711
+ FIO_SFUNC VALUE iodine_utils_totp_secret(int argc, VALUE *argv, VALUE self) {
712
+ int64_t len = 20;
713
+ iodine_rb2c_arg(argc, argv, IODINE_ARG_NUM(len, 0, "len", 0));
714
+
715
+ if (len < 10 || len > 64)
716
+ rb_raise(rb_eArgError, "len must be between 10 and 64");
717
+
718
+ /* Generate a good enough random key */
719
+ uint8_t key[64];
720
+ if (fio_rand_bytes_secure(key, (size_t)len))
721
+ fio_rand_bytes(key, (size_t)len);
722
+
723
+ /* Base32 encode (output is roughly 8/5 of input, plus null terminator) */
724
+ char encoded[128];
725
+ size_t encoded_len = fio_otp_print_key(encoded, key, (size_t)len);
726
+
727
+ return rb_str_new(encoded, (long)encoded_len);
728
+ (void)self;
729
+ }
730
+
731
+ /**
732
+ * Verifies a TOTP code against a secret with time window tolerance.
733
+ *
734
+ * # Verify a TOTP code with default settings
735
+ * valid = Iodine::Utils.totp_verify(code: user_code, secret: my_secret)
736
+ *
737
+ * # Verify with larger time window (allows more clock drift)
738
+ * valid = Iodine::Utils.totp_verify(code: user_code, secret: my_secret,
739
+ * window: 2)
740
+ *
741
+ * # Verify with custom interval (must match the interval used to generate)
742
+ * valid = Iodine::Utils.totp_verify(code: user_code, secret: my_secret,
743
+ * interval: 60)
744
+ *
745
+ * The window parameter specifies how many intervals to check on either side
746
+ * of the current time. For example, window: 1 checks current ± 1 interval.
747
+ *
748
+ * Parameters:
749
+ * - `code:` (required) - The TOTP code to verify (Integer)
750
+ * - `secret:` (required) - The shared secret key (raw bytes or Base32 decoded)
751
+ * - `window:` (optional) - Number of intervals to check on each side (default:
752
+ * 1, range: 0-10)
753
+ * - `interval:` (optional) - Time window in seconds (default: 30)
754
+ *
755
+ * Returns: true if the code is valid, false otherwise
756
+ */
757
+ FIO_SFUNC VALUE iodine_utils_totp_verify(int argc, VALUE *argv, VALUE self) {
758
+ fio_buf_info_s secret = FIO_BUF_INFO0;
759
+ int64_t code = 0;
760
+ int64_t window = 1;
761
+ size_t interval = 0;
762
+
763
+ iodine_rb2c_arg(argc,
764
+ argv,
765
+ IODINE_ARG_NUM(code, 0, "code", 1),
766
+ IODINE_ARG_BUF(secret, 0, "secret", 1),
767
+ IODINE_ARG_NUM(window, 0, "window", 0),
768
+ IODINE_ARG_SIZE_T(interval, 0, "interval", 0));
769
+
770
+ if (!interval)
771
+ interval = 30;
772
+ if (window < 0 || window > 10)
773
+ rb_raise(rb_eArgError, "window must be between 0 and 10");
774
+
775
+ /* Check code against each offset in the window */
776
+ for (int64_t offset = -window; offset <= window; ++offset) {
777
+ uint32_t expected = fio_otp(secret, .offset = offset, .interval = interval);
778
+ if (expected == (uint32_t)code)
779
+ return Qtrue;
780
+ }
781
+
782
+ return Qfalse;
783
+ (void)self;
784
+ }
785
+
786
+ /* *****************************************************************************
787
+ Create Methods in Module
788
+ ***************************************************************************** */
789
+
790
+ FIO_SFUNC void iodine_utils_define_methods(VALUE m) {
791
+ rb_define_singleton_method(m, "escape_path", iodine_utils_encode_path, 1);
792
+ rb_define_singleton_method(m, "escape_path!", iodine_utils_encode_path1, 1);
793
+ rb_define_singleton_method(m, "unescape_path", iodine_utils_decode_path, -1);
794
+ rb_define_singleton_method(m, "unescape_path!", iodine_utils_decode_path1, 1);
795
+ rb_define_singleton_method(m, "escape", iodine_utils_encode_url, 1);
796
+ rb_define_singleton_method(m, "escape!", iodine_utils_encode_url1, 1);
797
+ rb_define_singleton_method(m, "unescape", iodine_utils_decode_url, -1);
798
+ rb_define_singleton_method(m, "unescape!", iodine_utils_decode_url1, 1);
799
+ rb_define_singleton_method(m, "escape_html", iodine_utils_encode_html, 1);
800
+ rb_define_singleton_method(m, "escape_html!", iodine_utils_encode_html1, 1);
801
+ rb_define_singleton_method(m, "unescape_html", iodine_utils_decode_html, -1);
802
+ rb_define_singleton_method(m, "unescape_html!", iodine_utils_decode_html1, 1);
803
+ rb_define_singleton_method(m, "rfc2109", iodine_utils_rfc2109, 1);
804
+ rb_define_singleton_method(m, "rfc2822", iodine_utils_rfc2822, 1);
805
+ rb_define_singleton_method(m, "time2str", iodine_utils_rfc7231, 1);
806
+ rb_define_singleton_method(m, "secure_compare", iodine_utils_is_eq, 2);
807
+ }
808
+
809
+ /**
810
+ * Adds the `Iodine::Utils` methods to the modules passed as arguments.
811
+ *
812
+ * If no modules were passed to the `monkey_patch` method, `Rack::Utils` will be
813
+ * monkey patched.
814
+ *
815
+ */
816
+ FIO_SFUNC VALUE iodine_utils_monkey_patch(int argc, VALUE *argv, VALUE self) {
817
+ VALUE default_module;
818
+ if (!argc) {
819
+ argc = 1;
820
+ argv = &default_module;
821
+ rb_require("rack");
822
+ default_module = rb_define_module_under(rb_define_module("Rack"), "Utils");
823
+ }
824
+ for (int i = 0; i < argc; ++i) {
825
+ rb_check_type(argv[i], T_MODULE);
826
+ iodine_utils_define_methods(argv[i]);
827
+ }
828
+ return self;
829
+ }
830
+
831
+ /** Initialize Iodine::Utils */ // clang-format off
832
+ /**
833
+
834
+ # Utility Helpers - Iodine's Helpers
835
+
836
+ These are some unescaping / decoding helpers provided by Iodine.
837
+
838
+ These **should** be faster then their common Ruby / Rack alternative.
839
+
840
+ Performance may differ according to architecture and compiler used. Please measure:
841
+
842
+ require 'iodine'
843
+ require 'rack'
844
+ require 'cgi'
845
+ require 'benchmark/ips'
846
+ encoded = '%E3 + %83 + %AB + %E3 + %83 + %93 + %E3 + %82 + %A4 + %E3 + %82 + %B9 + %E3 + %81 + %A8' # a String in need of decoding
847
+ decoded = Rack::Utils.unescape(encoded, "binary")
848
+ html_xss = "<script>alert('avoid xss attacks')</script>"
849
+ html_xss_safe = Rack::Utils.escape_html html_xss
850
+ short_str1 = Array.new(64) { 'a' } .join
851
+ short_str2 = Array.new(64) { 'a' } .join
852
+ long_str1 = Array.new(4094) { 'a' } .join
853
+ long_str2 = Array.new(4094) { 'a' } .join
854
+ now_preclaculated = Time.now
855
+ Benchmark.ips do |bm|
856
+ bm.report(" Iodine rfc2822") { Iodine::Utils.rfc2822(now_preclaculated) }
857
+ bm.report(" Rack rfc2822") { Rack::Utils.rfc2822(now_preclaculated) }
858
+ bm.compare!
859
+ end; Benchmark.ips do |bm|
860
+ bm.report("Iodine unescape") { Iodine::Utils.unescape encoded }
861
+ bm.report(" Rack unescape") { Rack::Utils.unescape encoded }
862
+ bm.compare!
863
+ end; Benchmark.ips do |bm|
864
+ bm.report("Iodine escape") { Iodine::Utils.escape decoded }
865
+ bm.report(" Rack escape") { Rack::Utils.escape decoded }
866
+ bm.compare!
867
+ end; Benchmark.ips do |bm|
868
+ bm.report("Iodine escape HTML") { Iodine::Utils.escape_html html_xss }
869
+ bm.report(" Rack escape HTML") { Rack::Utils.escape_html html_xss }
870
+ bm.compare!
871
+ end; Benchmark.ips do |bm|
872
+ bm.report("Iodine unescape HTML") { Iodine::Utils.unescape_html html_xss_safe }
873
+ bm.report(" CGI unescape HTML") { CGI.unescapeHTML html_xss_safe }
874
+ bm.compare!
875
+ end; Benchmark.ips do |bm|
876
+ bm.report("Iodine secure compare (short)") { Iodine::Utils.secure_compare short_str1, short_str2 }
877
+ bm.report(" Rack secure compare (short)") { Rack::Utils.secure_compare short_str1, short_str2 }
878
+ bm.compare!
879
+ end; Benchmark.ips do |bm|
880
+ bm.report("Iodine secure compare (long)") { Iodine::Utils.secure_compare long_str1, long_str2 }
881
+ bm.report(" Rack secure compare (long)") { Rack::Utils.secure_compare long_str1, long_str2 }
882
+ bm.compare!
883
+ end && nil
884
+
885
+ */
886
+ static void Init_Iodine_Utils(void) {
887
+ VALUE m = rb_define_module_under(iodine_rb_IODINE, "Utils");
888
+ iodine_utils_define_methods(m);
889
+ /* non-standard helpers */
890
+ rb_define_singleton_method(m, "monkey_patch", iodine_utils_monkey_patch, -1);
891
+ rb_define_singleton_method(m, "random", iodine_utils_random, -1);
892
+ rb_define_singleton_method(m, "uuid", iodine_utils_uuid, -1);
893
+ rb_define_singleton_method(m, "totp", iodine_utils_totp, -1);
894
+ rb_define_singleton_method(m, "totp_secret", iodine_utils_totp_secret, -1);
895
+ rb_define_singleton_method(m, "totp_verify", iodine_utils_totp_verify, -1);
896
+ rb_define_singleton_method(m, "hmac512", iodine_utils_hmac512, 2);
897
+ rb_define_singleton_method(m, "hmac256", iodine_utils_hmac256, 2);
898
+ rb_define_singleton_method(m, "hmac160", iodine_utils_hmac_sha1, 2);
899
+ rb_define_singleton_method(m, "hmac128", iodine_utils_hmac_poly, 2);
900
+ rb_define_singleton_method(m, "sha256", iodine_utils_sha256, 1);
901
+ rb_define_singleton_method(m, "sha512", iodine_utils_sha512, 1);
902
+ rb_define_singleton_method(m, "sha3_224", iodine_utils_sha3_224, 1);
903
+ rb_define_singleton_method(m, "sha3_256", iodine_utils_sha3_256, 1);
904
+ rb_define_singleton_method(m, "sha3_384", iodine_utils_sha3_384, 1);
905
+ rb_define_singleton_method(m, "sha3_512", iodine_utils_sha3_512, 1);
906
+ rb_define_singleton_method(m, "shake128", iodine_utils_shake128, -1);
907
+ rb_define_singleton_method(m, "shake256", iodine_utils_shake256, -1);
908
+ rb_define_singleton_method(m, "sha1", iodine_utils_sha1, 1);
909
+ rb_define_singleton_method(m, "crc32", iodine_utils_crc32, -1);
910
+ rb_define_singleton_method(m, "risky_hash", iodine_utils_risky_hash, -1);
911
+ rb_define_singleton_method(m, "risky256", iodine_utils_risky256, 1);
912
+ rb_define_singleton_method(m, "risky512", iodine_utils_risky512, 1);
913
+ rb_define_singleton_method(m, "risky256_hmac", iodine_utils_risky256_hmac, 2);
914
+ rb_define_singleton_method(m, "risky512_hmac", iodine_utils_risky512_hmac, 2);
915
+ rb_define_singleton_method(m, "secure_random", iodine_utils_secure_random, -1);
916
+ rb_define_singleton_method(m, "blake2b", iodine_utils_blake2b, -1);
917
+ rb_define_singleton_method(m, "blake2s", iodine_utils_blake2s, -1);
918
+
919
+
920
+
921
+ fio_state_callback_add(FIO_CALL_IN_CHILD, iodine_random_on_fork, NULL);
922
+ } // clang-format on
923
+ #endif /* H___IODINE_UTILS___H */