iodine 0.6.5 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of iodine might be problematic. Click here for more details.

Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/README.md +4 -4
  4. data/SPEC-Websocket-Draft.md +3 -6
  5. data/bin/mustache.rb +128 -0
  6. data/examples/test_template.mustache +16 -0
  7. data/ext/iodine/fio.c +9397 -0
  8. data/ext/iodine/fio.h +4723 -0
  9. data/ext/iodine/fio_ary.h +353 -54
  10. data/ext/iodine/fio_cli.c +351 -361
  11. data/ext/iodine/fio_cli.h +84 -105
  12. data/ext/iodine/fio_hashmap.h +70 -16
  13. data/ext/iodine/fio_json_parser.h +35 -24
  14. data/ext/iodine/fio_siphash.c +104 -4
  15. data/ext/iodine/fio_siphash.h +18 -2
  16. data/ext/iodine/fio_str.h +1218 -0
  17. data/ext/iodine/fio_tmpfile.h +1 -1
  18. data/ext/iodine/fiobj.h +13 -8
  19. data/ext/iodine/fiobj4sock.h +6 -8
  20. data/ext/iodine/fiobj_ary.c +107 -17
  21. data/ext/iodine/fiobj_ary.h +36 -4
  22. data/ext/iodine/fiobj_data.c +146 -127
  23. data/ext/iodine/fiobj_data.h +25 -23
  24. data/ext/iodine/fiobj_hash.c +7 -7
  25. data/ext/iodine/fiobj_hash.h +6 -5
  26. data/ext/iodine/fiobj_json.c +20 -17
  27. data/ext/iodine/fiobj_json.h +5 -5
  28. data/ext/iodine/fiobj_mem.h +71 -0
  29. data/ext/iodine/fiobj_mustache.c +310 -0
  30. data/ext/iodine/fiobj_mustache.h +40 -0
  31. data/ext/iodine/fiobj_numbers.c +199 -94
  32. data/ext/iodine/fiobj_numbers.h +7 -7
  33. data/ext/iodine/fiobj_str.c +142 -333
  34. data/ext/iodine/fiobj_str.h +65 -55
  35. data/ext/iodine/fiobject.c +49 -11
  36. data/ext/iodine/fiobject.h +40 -39
  37. data/ext/iodine/http.c +382 -190
  38. data/ext/iodine/http.h +124 -80
  39. data/ext/iodine/http1.c +99 -127
  40. data/ext/iodine/http1.h +5 -5
  41. data/ext/iodine/http1_parser.c +3 -2
  42. data/ext/iodine/http1_parser.h +2 -2
  43. data/ext/iodine/http_internal.c +14 -12
  44. data/ext/iodine/http_internal.h +25 -19
  45. data/ext/iodine/iodine.c +37 -18
  46. data/ext/iodine/iodine.h +4 -0
  47. data/ext/iodine/iodine_caller.c +9 -2
  48. data/ext/iodine/iodine_caller.h +2 -0
  49. data/ext/iodine/iodine_connection.c +82 -117
  50. data/ext/iodine/iodine_defer.c +57 -50
  51. data/ext/iodine/iodine_defer.h +0 -1
  52. data/ext/iodine/iodine_fiobj2rb.h +4 -2
  53. data/ext/iodine/iodine_helpers.c +4 -4
  54. data/ext/iodine/iodine_http.c +25 -32
  55. data/ext/iodine/iodine_json.c +2 -1
  56. data/ext/iodine/iodine_mustache.c +423 -0
  57. data/ext/iodine/iodine_mustache.h +6 -0
  58. data/ext/iodine/iodine_pubsub.c +48 -153
  59. data/ext/iodine/iodine_pubsub.h +5 -4
  60. data/ext/iodine/iodine_rack_io.c +7 -5
  61. data/ext/iodine/iodine_store.c +16 -13
  62. data/ext/iodine/iodine_tcp.c +26 -34
  63. data/ext/iodine/mustache_parser.h +1085 -0
  64. data/ext/iodine/redis_engine.c +740 -646
  65. data/ext/iodine/redis_engine.h +13 -15
  66. data/ext/iodine/resp_parser.h +11 -5
  67. data/ext/iodine/websocket_parser.h +13 -13
  68. data/ext/iodine/websockets.c +240 -393
  69. data/ext/iodine/websockets.h +52 -113
  70. data/lib/iodine.rb +1 -1
  71. data/lib/iodine/mustache.rb +140 -0
  72. data/lib/iodine/version.rb +1 -1
  73. metadata +15 -28
  74. data/ext/iodine/defer.c +0 -566
  75. data/ext/iodine/defer.h +0 -148
  76. data/ext/iodine/evio.c +0 -26
  77. data/ext/iodine/evio.h +0 -161
  78. data/ext/iodine/evio_callbacks.c +0 -26
  79. data/ext/iodine/evio_epoll.c +0 -251
  80. data/ext/iodine/evio_kqueue.c +0 -194
  81. data/ext/iodine/facil.c +0 -2325
  82. data/ext/iodine/facil.h +0 -616
  83. data/ext/iodine/fio_base64.c +0 -277
  84. data/ext/iodine/fio_base64.h +0 -71
  85. data/ext/iodine/fio_llist.h +0 -257
  86. data/ext/iodine/fio_mem.c +0 -675
  87. data/ext/iodine/fio_mem.h +0 -143
  88. data/ext/iodine/fio_random.c +0 -248
  89. data/ext/iodine/fio_random.h +0 -45
  90. data/ext/iodine/fio_sha1.c +0 -362
  91. data/ext/iodine/fio_sha1.h +0 -107
  92. data/ext/iodine/fio_sha2.c +0 -842
  93. data/ext/iodine/fio_sha2.h +0 -169
  94. data/ext/iodine/pubsub.c +0 -867
  95. data/ext/iodine/pubsub.h +0 -221
  96. data/ext/iodine/sock.c +0 -1366
  97. data/ext/iodine/sock.h +0 -566
  98. data/ext/iodine/spnlock.inc +0 -111
@@ -2,6 +2,5 @@
2
2
  #define H_IODINE_DEFER_H
3
3
 
4
4
  void iodine_defer_initialize(void);
5
- void iodine_defer_on_finish(void);
6
5
 
7
6
  #endif
@@ -18,7 +18,9 @@ typedef struct {
18
18
  uint8_t str2sym;
19
19
  } fiobj2rb_s;
20
20
 
21
- typedef struct { uint8_t str2sym; } fiobj2rb_settings_s;
21
+ typedef struct {
22
+ uint8_t str2sym;
23
+ } fiobj2rb_settings_s;
22
24
 
23
25
  static inline VALUE fiobj2rb(FIOBJ o, uint8_t str2sym) {
24
26
  VALUE rb;
@@ -40,7 +42,7 @@ static inline VALUE fiobj2rb(FIOBJ o, uint8_t str2sym) {
40
42
  case FIOBJ_T_DATA: /* fallthrough */
41
43
  case FIOBJ_T_UNKNOWN: /* fallthrough */
42
44
  case FIOBJ_T_STRING: {
43
- fio_cstr_s tmp = fiobj_obj2cstr(o);
45
+ fio_str_info_s tmp = fiobj_obj2cstr(o);
44
46
  if (str2sym) {
45
47
  rb = rb_intern2(tmp.data, tmp.len);
46
48
  rb = ID2SYM(rb);
@@ -144,9 +144,9 @@ static VALUE date_str(int argc, VALUE *argv, VALUE self) {
144
144
  argv[0] = rb_funcallv(argv[0], iodine_to_i_func_id, 0, NULL);
145
145
  Check_Type(argv[0], T_FIXNUM);
146
146
  last_tick =
147
- FIX2ULONG(argv[0]) ? FIX2ULONG(argv[0]) : facil_last_tick().tv_sec;
147
+ FIX2ULONG(argv[0]) ? FIX2ULONG(argv[0]) : fio_last_tick().tv_sec;
148
148
  } else
149
- last_tick = facil_last_tick().tv_sec;
149
+ last_tick = fio_last_tick().tv_sec;
150
150
  VALUE str = rb_str_buf_new(32);
151
151
  struct tm tm;
152
152
 
@@ -172,7 +172,7 @@ Since Iodine uses time caching within it's reactor, using the default value
172
172
  static VALUE iodine_rfc2822(VALUE self, VALUE rtm) {
173
173
  time_t last_tick;
174
174
  rtm = rb_funcallv(rtm, iodine_to_i_func_id, 0, NULL);
175
- last_tick = FIX2ULONG(rtm) ? FIX2ULONG(rtm) : facil_last_tick().tv_sec;
175
+ last_tick = FIX2ULONG(rtm) ? FIX2ULONG(rtm) : fio_last_tick().tv_sec;
176
176
  VALUE str = rb_str_buf_new(34);
177
177
  struct tm tm;
178
178
 
@@ -198,7 +198,7 @@ Since Iodine uses time caching within it's reactor, using the default value
198
198
  static VALUE iodine_rfc2109(VALUE self, VALUE rtm) {
199
199
  time_t last_tick;
200
200
  rtm = rb_funcallv(rtm, iodine_to_i_func_id, 0, NULL);
201
- last_tick = FIX2ULONG(rtm) ? FIX2ULONG(rtm) : facil_last_tick().tv_sec;
201
+ last_tick = FIX2ULONG(rtm) ? FIX2ULONG(rtm) : fio_last_tick().tv_sec;
202
202
  VALUE str = rb_str_buf_new(32);
203
203
  struct tm tm;
204
204
 
@@ -6,9 +6,8 @@ Feel free to copy, use and enjoy according to the license provided.
6
6
  */
7
7
  #include "iodine.h"
8
8
 
9
- #include "evio.h"
10
- #include "fio_mem.h"
11
9
  #include "http.h"
10
+
12
11
  #include <ruby/encoding.h>
13
12
  #include <ruby/io.h>
14
13
  // #include "iodine_websockets.h"
@@ -131,13 +130,13 @@ static void *iodine_ws_fire_message(void *msg_) {
131
130
  return NULL;
132
131
  }
133
132
 
134
- static void iodine_ws_on_message(ws_s *ws, char *data, size_t size,
133
+ static void iodine_ws_on_message(ws_s *ws, fio_str_info_s data,
135
134
  uint8_t is_text) {
136
135
  iodine_msg2ruby_s msg = {
137
- .data = data,
138
- .size = size,
136
+ .data = data.data,
137
+ .size = data.len,
139
138
  .is_text = is_text,
140
- .io = (VALUE)websocket_udata(ws),
139
+ .io = (VALUE)websocket_udata_get(ws),
141
140
  };
142
141
  IodineCaller.enterGVL(iodine_ws_fire_message, &msg);
143
142
  }
@@ -147,7 +146,7 @@ static void iodine_ws_on_message(ws_s *ws, char *data, size_t size,
147
146
  * `on_message` events are raised before `on_open` returns.
148
147
  */
149
148
  static void iodine_ws_on_open(ws_s *ws) {
150
- VALUE h = (VALUE)websocket_udata(ws);
149
+ VALUE h = (VALUE)websocket_udata_get(ws);
151
150
  iodine_connection_s *c = iodine_connection_CData(h);
152
151
  c->arg = ws;
153
152
  c->uuid = websocket_uuid(ws);
@@ -160,7 +159,7 @@ static void iodine_ws_on_open(ws_s *ws) {
160
159
  * If the socket's buffer is never used, the callback is never called.
161
160
  */
162
161
  static void iodine_ws_on_ready(ws_s *ws) {
163
- iodine_connection_fire_event((VALUE)websocket_udata(ws),
162
+ iodine_connection_fire_event((VALUE)websocket_udata_get(ws),
164
163
  IODINE_CONNECTION_ON_DRAINED, Qnil);
165
164
  }
166
165
  /**
@@ -169,7 +168,7 @@ static void iodine_ws_on_ready(ws_s *ws) {
169
168
  * `on_close`).
170
169
  */
171
170
  static void iodine_ws_on_shutdown(ws_s *ws) {
172
- iodine_connection_fire_event((VALUE)websocket_udata(ws),
171
+ iodine_connection_fire_event((VALUE)websocket_udata_get(ws),
173
172
  IODINE_CONNECTION_ON_SHUTDOWN, Qnil);
174
173
  }
175
174
  /**
@@ -195,7 +194,7 @@ static void iodine_ws_attach(http_s *h, VALUE handler, VALUE env) {
195
194
  if (io == Qnil)
196
195
  return;
197
196
 
198
- http_upgrade2ws(.http = h, .on_message = iodine_ws_on_message,
197
+ http_upgrade2ws(h, .on_message = iodine_ws_on_message,
199
198
  .on_open = iodine_ws_on_open, .on_ready = iodine_ws_on_ready,
200
199
  .on_shutdown = iodine_ws_on_shutdown,
201
200
  .on_close = iodine_ws_on_close, .udata = (void *)io);
@@ -226,7 +225,7 @@ static void iodine_sse_on_open(http_sse_s *sse) {
226
225
  c->uuid = http_sse2uuid(sse);
227
226
  iodine_connection_fire_event(h, IODINE_CONNECTION_ON_OPEN, Qnil);
228
227
  sse->on_ready = iodine_sse_on_ready;
229
- evio_add_write(sock_uuid2fd(c->uuid), (void *)c->uuid);
228
+ fio_force_event(c->uuid, FIO_EVENT_ON_READY);
230
229
  }
231
230
 
232
231
  static void iodine_sse_attach(http_s *h, VALUE handler, VALUE env) {
@@ -250,7 +249,7 @@ Copying data from the C request to the Rack's ENV
250
249
  int iodine_copy2env_task(FIOBJ o, void *env_) {
251
250
  VALUE env = (VALUE)env_;
252
251
  FIOBJ name = fiobj_hash_key_in_loop();
253
- fio_cstr_s tmp = fiobj_obj2cstr(name);
252
+ fio_str_info_s tmp = fiobj_obj2cstr(name);
254
253
  VALUE hname = (VALUE)0;
255
254
  if (tmp.len > 59) {
256
255
  char *buf = fio_malloc(tmp.len + 5);
@@ -303,7 +302,7 @@ static inline VALUE copy2env(iodine_http_request_handle_s *handle) {
303
302
  }
304
303
  IodineStore.add(env);
305
304
 
306
- fio_cstr_s tmp;
305
+ fio_str_info_s tmp;
307
306
  char *pos = NULL;
308
307
  /* Copy basic data */
309
308
  tmp = fiobj_obj2cstr(h->method);
@@ -330,17 +329,9 @@ static inline VALUE copy2env(iodine_http_request_handle_s *handle) {
330
329
  }
331
330
 
332
331
  { // Support for Ruby web-console.
333
- char buf[64];
334
- buf[63] = 0;
335
- sock_peer_addr_s addrinfo = http_peer_addr(h);
336
- if (addrinfo.addrlen &&
337
- inet_ntop(
338
- addrinfo.addr->sa_family,
339
- addrinfo.addr->sa_family == AF_INET
340
- ? (void *)&((struct sockaddr_in *)addrinfo.addr)->sin_addr
341
- : (void *)&((struct sockaddr_in6 *)addrinfo.addr)->sin6_addr,
342
- buf, 64)) {
343
- rb_hash_aset(env, REMOTE_ADDR, rb_str_new(buf, strlen(buf)));
332
+ fio_str_info_s peer = http_peer_addr(h);
333
+ if (peer.len) {
334
+ rb_hash_aset(env, REMOTE_ADDR, rb_str_new(peer.data, peer.len));
344
335
  }
345
336
  }
346
337
 
@@ -477,7 +468,7 @@ static int for_each_header_data(VALUE key, VALUE val, VALUE h_) {
477
468
  // make the headers lowercase
478
469
  FIOBJ name = fiobj_str_new(key_s, key_len);
479
470
  {
480
- fio_cstr_s tmp = fiobj_obj2cstr(name);
471
+ fio_str_info_s tmp = fiobj_obj2cstr(name);
481
472
  for (int i = 0; i < key_len; ++i) {
482
473
  tmp.data[i] = tolower(tmp.data[i]);
483
474
  }
@@ -719,7 +710,7 @@ static inline void
719
710
  iodine_perform_handle_action(iodine_http_request_handle_s handle) {
720
711
  switch (handle.type) {
721
712
  case IODINE_HTTP_SENDBODY: {
722
- fio_cstr_s data = fiobj_obj2cstr(handle.body);
713
+ fio_str_info_s data = fiobj_obj2cstr(handle.body);
723
714
  http_send_body(handle.h, data.data, data.len);
724
715
  fiobj_free(handle.body);
725
716
  break;
@@ -732,7 +723,7 @@ iodine_perform_handle_action(iodine_http_request_handle_s handle) {
732
723
  .len == 7)
733
724
  fiobj_hash_delete2(handle.h->private_data.out_headers,
734
725
  fiobj_obj2hash(HTTP_HEADER_CONTENT_ENCODING));
735
- fio_cstr_s data = fiobj_obj2cstr(handle.body);
726
+ fio_str_info_s data = fiobj_obj2cstr(handle.body);
736
727
  if (http_sendfile2(handle.h, data.data, data.len, NULL, 0)) {
737
728
  http_send_error(handle.h, 404);
738
729
  }
@@ -754,7 +745,8 @@ iodine_perform_handle_action(iodine_http_request_handle_s handle) {
754
745
  }
755
746
  static void on_rack_request(http_s *h) {
756
747
  iodine_http_request_handle_s handle = (iodine_http_request_handle_s){
757
- .h = h, .upgrade = IODINE_UPGRADE_NONE,
748
+ .h = h,
749
+ .upgrade = IODINE_UPGRADE_NONE,
758
750
  };
759
751
  IodineCaller.enterGVL((void *(*)(void *))iodine_handle_request_in_GVL,
760
752
  &handle);
@@ -794,13 +786,14 @@ void *iodine_print_http_msg_in_gvl(void *d_) {
794
786
  fprintf(stderr,
795
787
  "Iodine HTTP Server on port %s:\n"
796
788
  " * Serving static files from %s\n\n",
797
- StringValueCStr(arg->port), StringValueCStr(arg->www));
789
+ (arg->port ? StringValueCStr(arg->port) : "----"),
790
+ StringValueCStr(arg->www));
798
791
  }
799
792
  return NULL;
800
793
  }
801
794
 
802
795
  static void iodine_print_http_msg(void *www, void *port) {
803
- if (getpid() != facil_parent_pid())
796
+ if (!fio_is_master())
804
797
  goto finish;
805
798
  struct {
806
799
  void *www;
@@ -989,7 +982,7 @@ VALUE iodine_http_listen(VALUE self, VALUE opt) {
989
982
  .ws_timeout = ping, .ws_max_msg_size = max_msg,
990
983
  .max_header_size = max_headers, .on_finish = free_iodine_http,
991
984
  .log = log_http, .max_body_size = max_body,
992
- .public_folder = (www ? StringValueCStr(www) : NULL))) {
985
+ .public_folder = (www ? StringValueCStr(www) : NULL)) == -1) {
993
986
  fprintf(stderr,
994
987
  "ERROR: Failed to initialize a listening HTTP socket for port %s\n",
995
988
  port ? StringValueCStr(port) : "3000");
@@ -1002,7 +995,7 @@ VALUE iodine_http_listen(VALUE self, VALUE opt) {
1002
995
  "static files.\n",
1003
996
  (port ? StringValueCStr(port) : "3000"));
1004
997
  }
1005
- defer(iodine_print_http_msg, (www ? (void *)www : NULL), (void *)port);
998
+ fio_defer(iodine_print_http_msg, (www ? (void *)www : NULL), (void *)port);
1006
999
 
1007
1000
  return Qtrue;
1008
1001
  (void)self;
@@ -1,8 +1,9 @@
1
1
  #include "iodine.h"
2
2
 
3
+ #include "fio.h"
4
+
3
5
  #include "fio_ary.h"
4
6
  #include "fio_json_parser.h"
5
- #include "fio_mem.h"
6
7
  #include "fiobj.h"
7
8
  #include "iodine_fiobj2rb.h"
8
9
  #include "iodine_store.h"
@@ -0,0 +1,423 @@
1
+
2
+ #define INCLUDE_MUSTACHE_IMPLEMENTATION 1
3
+ #include "mustache_parser.h"
4
+
5
+ #include "iodine.h"
6
+
7
+ #define FIO_INCLUDE_STR
8
+ #include <fio.h>
9
+
10
+ ID call_func_id;
11
+ ID to_s_func_id;
12
+ /* *****************************************************************************
13
+ C <=> Ruby Data allocation
14
+ ***************************************************************************** */
15
+
16
+ static size_t iodine_mustache_data_size(const void *c_) {
17
+ return sizeof(mustache_s *);
18
+ (void)c_;
19
+ }
20
+
21
+ static size_t iodine_mustache_data_free(const void *c_) {
22
+ mustache_free(((mustache_s **)c_)[0]);
23
+ free((void *)c_);
24
+ return sizeof(mustache_s *);
25
+ (void)c_;
26
+ }
27
+
28
+ const rb_data_type_t iodine_mustache_data_type = {
29
+ .wrap_struct_name = "IodineMustacheData",
30
+ .function =
31
+ {
32
+ .dmark = NULL,
33
+ .dfree = free,
34
+ .dsize = iodine_mustache_data_size,
35
+ },
36
+ .data = NULL,
37
+ // .flags = RUBY_TYPED_FREE_IMMEDIATELY,
38
+ };
39
+
40
+ /* Iodine::PubSub::Engine.allocate */
41
+ static VALUE iodine_mustache_data_alloc_c(VALUE self) {
42
+ void *m = malloc(sizeof(mustache_s *));
43
+ ((mustache_s **)m)[0] = NULL;
44
+ return TypedData_Wrap_Struct(self, &iodine_mustache_data_type, m);
45
+ }
46
+
47
+ static inline mustache_s *iodine_mustache_ruby2C(VALUE self) {
48
+ mustache_s **m = NULL;
49
+ TypedData_Get_Struct(self, mustache_s *, &iodine_mustache_data_type, m);
50
+ if (!m)
51
+ return NULL;
52
+ return m[0];
53
+ }
54
+
55
+ /* *****************************************************************************
56
+ Parser Callbacks
57
+ ***************************************************************************** */
58
+
59
+ /** HTML ecape table, created using the following Ruby Script:
60
+ a = []
61
+ 256.times {|i| a[i] = "&\#x#{ i < 16 ? "0#{i.to_s(16)}" : i.to_s(16)};"}
62
+ ('a'.ord..'z'.ord).each {|i| a[i] = i.chr }
63
+ ('A'.ord..'Z'.ord).each {|i| a[i] = i.chr }
64
+ ('0'.ord..'9'.ord).each {|i| a[i] = i.chr }
65
+ a['<'.ord] = "&lt;"
66
+ a['>'.ord] = "&gt;"
67
+ a['&'.ord] = "&amp;"
68
+ a['"'.ord] = "&quot;"
69
+
70
+ b = a.map {|s| s.length }
71
+ puts "static char *html_escape_strs[] = {", a.to_s.slice(1..-2) ,"};",
72
+ "static uint8_t html_escape_len[] = {", b.to_s.slice(1..-2),"};"
73
+ */
74
+ static char *html_escape_strs[] = {
75
+ "&#x00;", "&#x01;", "&#x02;", "&#x03;", "&#x04;", "&#x05;", "&#x06;",
76
+ "&#x07;", "&#x08;", "&#x09;", "&#x0a;", "&#x0b;", "&#x0c;", "&#x0d;",
77
+ "&#x0e;", "&#x0f;", "&#x10;", "&#x11;", "&#x12;", "&#x13;", "&#x14;",
78
+ "&#x15;", "&#x16;", "&#x17;", "&#x18;", "&#x19;", "&#x1a;", "&#x1b;",
79
+ "&#x1c;", "&#x1d;", "&#x1e;", "&#x1f;", "&#x20;", "&#x21;", "&quot;",
80
+ "&#x23;", "&#x24;", "&#x25;", "&amp;", "&#x27;", "&#x28;", "&#x29;",
81
+ "&#x2a;", "&#x2b;", "&#x2c;", "&#x2d;", "&#x2e;", "&#x2f;", "0",
82
+ "1", "2", "3", "4", "5", "6", "7",
83
+ "8", "9", "&#x3a;", "&#x3b;", "&lt;", "&#x3d;", "&gt;",
84
+ "&#x3f;", "&#x40;", "A", "B", "C", "D", "E",
85
+ "F", "G", "H", "I", "J", "K", "L",
86
+ "M", "N", "O", "P", "Q", "R", "S",
87
+ "T", "U", "V", "W", "X", "Y", "Z",
88
+ "&#x5b;", "&#x5c;", "&#x5d;", "&#x5e;", "&#x5f;", "&#x60;", "a",
89
+ "b", "c", "d", "e", "f", "g", "h",
90
+ "i", "j", "k", "l", "m", "n", "o",
91
+ "p", "q", "r", "s", "t", "u", "v",
92
+ "w", "x", "y", "z", "&#x7b;", "&#x7c;", "&#x7d;",
93
+ "&#x7e;", "&#x7f;", "&#x80;", "&#x81;", "&#x82;", "&#x83;", "&#x84;",
94
+ "&#x85;", "&#x86;", "&#x87;", "&#x88;", "&#x89;", "&#x8a;", "&#x8b;",
95
+ "&#x8c;", "&#x8d;", "&#x8e;", "&#x8f;", "&#x90;", "&#x91;", "&#x92;",
96
+ "&#x93;", "&#x94;", "&#x95;", "&#x96;", "&#x97;", "&#x98;", "&#x99;",
97
+ "&#x9a;", "&#x9b;", "&#x9c;", "&#x9d;", "&#x9e;", "&#x9f;", "&#xa0;",
98
+ "&#xa1;", "&#xa2;", "&#xa3;", "&#xa4;", "&#xa5;", "&#xa6;", "&#xa7;",
99
+ "&#xa8;", "&#xa9;", "&#xaa;", "&#xab;", "&#xac;", "&#xad;", "&#xae;",
100
+ "&#xaf;", "&#xb0;", "&#xb1;", "&#xb2;", "&#xb3;", "&#xb4;", "&#xb5;",
101
+ "&#xb6;", "&#xb7;", "&#xb8;", "&#xb9;", "&#xba;", "&#xbb;", "&#xbc;",
102
+ "&#xbd;", "&#xbe;", "&#xbf;", "&#xc0;", "&#xc1;", "&#xc2;", "&#xc3;",
103
+ "&#xc4;", "&#xc5;", "&#xc6;", "&#xc7;", "&#xc8;", "&#xc9;", "&#xca;",
104
+ "&#xcb;", "&#xcc;", "&#xcd;", "&#xce;", "&#xcf;", "&#xd0;", "&#xd1;",
105
+ "&#xd2;", "&#xd3;", "&#xd4;", "&#xd5;", "&#xd6;", "&#xd7;", "&#xd8;",
106
+ "&#xd9;", "&#xda;", "&#xdb;", "&#xdc;", "&#xdd;", "&#xde;", "&#xdf;",
107
+ "&#xe0;", "&#xe1;", "&#xe2;", "&#xe3;", "&#xe4;", "&#xe5;", "&#xe6;",
108
+ "&#xe7;", "&#xe8;", "&#xe9;", "&#xea;", "&#xeb;", "&#xec;", "&#xed;",
109
+ "&#xee;", "&#xef;", "&#xf0;", "&#xf1;", "&#xf2;", "&#xf3;", "&#xf4;",
110
+ "&#xf5;", "&#xf6;", "&#xf7;", "&#xf8;", "&#xf9;", "&#xfa;", "&#xfb;",
111
+ "&#xfc;", "&#xfd;", "&#xfe;", "&#xff;"};
112
+ static uint8_t html_escape_len[] = {
113
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
114
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6,
115
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 6, 6, 4, 6, 4, 6, 6, 1, 1, 1, 1, 1, 1, 1,
116
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 6, 6, 6, 6, 6,
117
+ 6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
118
+ 1, 1, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
119
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
120
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
121
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
122
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
123
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6};
124
+
125
+ static inline VALUE fiobj_mustache_find_obj(mustache_section_s *section,
126
+ const char *name,
127
+ uint32_t name_len) {
128
+ do {
129
+ VALUE tmp;
130
+ #if 0
131
+ /* test for Array indexing */
132
+ if (name[0] >= '0' && name[0] <= '9' &&
133
+ RB_TYPE_P((VALUE)section->udata2, T_ARRAY)) {
134
+ char **pos = (char **)&name;
135
+ tmp = rb_ary_entry((VALUE)section->udata2, fio_atol(pos));
136
+ if (tmp)
137
+ return tmp;
138
+ }
139
+ #endif
140
+ if (!RB_TYPE_P((VALUE)section->udata2, T_HASH)) {
141
+ continue;
142
+ }
143
+ /* search by String */
144
+ VALUE key = rb_str_new(name, name_len);
145
+ tmp = rb_hash_aref((VALUE)section->udata2, key);
146
+ if (tmp != Qnil)
147
+ return tmp;
148
+ /* search by Symbol */
149
+ key = rb_id2sym(rb_intern2(name, name_len));
150
+ tmp = rb_hash_aref((VALUE)section->udata2, key);
151
+ if (tmp != Qnil)
152
+ return tmp;
153
+ section = section->parent;
154
+ } while (section);
155
+ return Qnil;
156
+ }
157
+ /**
158
+ * Called when an argument name was detected in the current section.
159
+ *
160
+ * A conforming implementation will search for the named argument both in the
161
+ * existing section and all of it's parents (walking backwards towards the root)
162
+ * until a value is detected.
163
+ *
164
+ * A missing value should be treated the same as an empty string.
165
+ *
166
+ * A conforming implementation will output the named argument's value (either
167
+ * HTML escaped or not, depending on the `escape` flag) as a string.
168
+ */
169
+ static int mustache_on_arg(mustache_section_s *section, const char *name,
170
+ uint32_t name_len, unsigned char escape) {
171
+ VALUE o = fiobj_mustache_find_obj(section, name, name_len);
172
+ if (!o)
173
+ return 0;
174
+ if (rb_respond_to(o, call_func_id))
175
+ goto callable;
176
+ if (!RB_TYPE_P(o, T_STRING))
177
+ o = IodineCaller.call(o, to_s_func_id);
178
+ if (!RB_TYPE_P(o, T_STRING) || !RSTRUCT_LEN(o))
179
+ return 0;
180
+ if (!escape) {
181
+ fio_str_write(section->udata1, RSTRING_PTR(o), RSTRING_LEN(o));
182
+ return 0;
183
+ }
184
+ /* HTML escape */
185
+ fio_str_info_s str = {.data = RSTRING_PTR(o), .len = RSTRING_LEN(o)};
186
+ fio_str_info_s i = fio_str_capa_assert(
187
+ section->udata1, fio_str_len(section->udata1) + str.len + 64);
188
+ do {
189
+ if (i.len + 6 >= i.capa)
190
+ i = fio_str_capa_assert(section->udata1, i.capa + 64);
191
+ i = fio_str_write(section->udata1, html_escape_strs[(uint8_t)str.data[0]],
192
+ html_escape_len[(uint8_t)str.data[0]]);
193
+ --str.len;
194
+ ++str.data;
195
+ } while (str.len);
196
+ (void)section;
197
+ (void)name;
198
+ (void)name_len;
199
+ (void)escape;
200
+ return 0;
201
+ callable:
202
+ o = rb_funcall2(o, call_func_id, 0, NULL);
203
+ o = rb_any_to_s(o);
204
+ fio_str_write(section->udata1, RSTRING_PTR(o), RSTRUCT_LEN(o));
205
+ return 0;
206
+ }
207
+
208
+ /**
209
+ * Called when simple template text (string) is detected.
210
+ *
211
+ * A conforming implementation will output data as a string (no escaping).
212
+ */
213
+ static int mustache_on_text(mustache_section_s *section, const char *data,
214
+ uint32_t data_len) {
215
+ fio_str_write(section->udata1, data, data_len);
216
+ return 0;
217
+ }
218
+
219
+ /**
220
+ * Called for nested sections, must return the number of objects in the new
221
+ * subsection (depending on the argument's name).
222
+ *
223
+ * Arrays should return the number of objects in the array.
224
+ *
225
+ * `true` values should return 1.
226
+ *
227
+ * `false` values should return 0.
228
+ *
229
+ * A return value of -1 will stop processing with an error.
230
+ *
231
+ * Please note, this will handle both normal and inverted sections.
232
+ */
233
+ static int32_t mustache_on_section_test(mustache_section_s *section,
234
+ const char *name, uint32_t name_len) {
235
+ VALUE o = fiobj_mustache_find_obj(section, name, name_len);
236
+ if (o == Qnil) {
237
+ return 0;
238
+ }
239
+ if (RB_TYPE_P(o, T_ARRAY)) {
240
+ return rb_array_len(o);
241
+ }
242
+ return 1;
243
+ }
244
+
245
+ /**
246
+ * Called when entering a nested section.
247
+ *
248
+ * `index` is a zero based index indicating the number of repetitions that
249
+ * occurred so far (same as the array index for arrays).
250
+ *
251
+ * A return value of -1 will stop processing with an error.
252
+ *
253
+ * Note: this is a good time to update the subsection's `udata` with the value
254
+ * of the array index. The `udata` will always contain the value or the parent's
255
+ * `udata`.
256
+ */
257
+ static int mustache_on_section_start(mustache_section_s *section,
258
+ char const *name, uint32_t name_len,
259
+ uint32_t index) {
260
+ VALUE o = fiobj_mustache_find_obj(section, name, name_len);
261
+ if (o == Qnil)
262
+ return 0;
263
+ if (RB_TYPE_P(o, T_ARRAY))
264
+ section->udata2 = (void *)rb_ary_entry(o, index);
265
+ else
266
+ section->udata2 = (void *)o;
267
+ return 0;
268
+ }
269
+
270
+ /**
271
+ * Called for cleanup in case of error.
272
+ */
273
+ static void mustache_on_formatting_error(void *udata1, void *udata2) {
274
+ (void)udata1;
275
+ (void)udata2;
276
+ }
277
+
278
+ /* *****************************************************************************
279
+ Loading the template
280
+ ***************************************************************************** */
281
+
282
+ /**
283
+ Loads a mustache template (and any partials).
284
+
285
+ Once a template was loaded, it could be rendered using {render}.
286
+ */
287
+ static VALUE iodine_mustache_new(VALUE self, VALUE filename) {
288
+ mustache_s **m = NULL;
289
+ TypedData_Get_Struct(self, mustache_s *, &iodine_mustache_data_type, m);
290
+ if (!m) {
291
+ rb_raise(rb_eRuntimeError, "Iodine::Mustache allocation error.");
292
+ }
293
+ Check_Type(filename, T_STRING);
294
+ mustache_error_en err;
295
+ *m = mustache_load(.filename = RSTRING_PTR(filename),
296
+ .filename_len = RSTRING_LEN(filename), .err = &err);
297
+ if (!*m)
298
+ goto error;
299
+ return self;
300
+ error:
301
+ switch (err) {
302
+ case MUSTACHE_OK:
303
+ rb_raise(rb_eRuntimeError, "Iodine::Mustache template ok, unknown error.");
304
+ break;
305
+ case MUSTACHE_ERR_TOO_DEEP:
306
+ rb_raise(rb_eRuntimeError, "Iodine::Mustache element nesting too deep.");
307
+ break;
308
+ case MUSTACHE_ERR_CLOSURE_MISMATCH:
309
+ rb_raise(rb_eRuntimeError,
310
+ "Iodine::Mustache template error, closure mismatch.");
311
+ break;
312
+ case MUSTACHE_ERR_FILE_NOT_FOUND:
313
+ rb_raise(rb_eRuntimeError, "Iodine::Mustache template not found.");
314
+ break;
315
+ case MUSTACHE_ERR_FILE_TOO_BIG:
316
+ rb_raise(rb_eRuntimeError, "Iodine::Mustache template too big.");
317
+ break;
318
+ case MUSTACHE_ERR_FILE_NAME_TOO_LONG:
319
+ rb_raise(rb_eRuntimeError, "Iodine::Mustache template name too long.");
320
+ break;
321
+ case MUSTACHE_ERR_EMPTY_TEMPLATE:
322
+ rb_raise(rb_eRuntimeError, "Iodine::Mustache template is empty.");
323
+ break;
324
+ case MUSTACHE_ERR_UNKNOWN:
325
+ rb_raise(rb_eRuntimeError, "Iodine::Mustache unknown error.");
326
+ break;
327
+ case MUSTACHE_ERR_USER_ERROR:
328
+ rb_raise(rb_eRuntimeError, "Iodine::Mustache internal error.");
329
+ break;
330
+ }
331
+ return self;
332
+ }
333
+
334
+ /* *****************************************************************************
335
+ Rendering
336
+ ***************************************************************************** */
337
+
338
+ /**
339
+ Renders the mustache template using the data provided in the `data` argument.
340
+
341
+ Returns a String.
342
+
343
+ Raises an exception on error.
344
+
345
+ NOTE:
346
+
347
+ As one might notice, no binding is provided. Instead, a `data` Hash is assumed.
348
+ Iodine will search the Hash for any data while protecting against code
349
+ execution.
350
+ */
351
+ static VALUE iodine_mustache_render(VALUE self, VALUE data) {
352
+ fio_str_s str = FIO_STR_INIT;
353
+ mustache_s **m = NULL;
354
+ TypedData_Get_Struct(self, mustache_s *, &iodine_mustache_data_type, m);
355
+ if (!m) {
356
+ rb_raise(rb_eRuntimeError, "Iodine::Mustache allocation error.");
357
+ }
358
+ if (mustache_build(*m, .udata1 = &str, .udata2 = (void *)data))
359
+ goto error;
360
+ fio_str_info_s i = fio_str_info(&str);
361
+ VALUE ret = rb_str_new(i.data, i.len);
362
+ fio_str_free(&str);
363
+ return ret;
364
+
365
+ error:
366
+ fio_str_free(&str);
367
+ rb_raise(rb_eRuntimeError, "Couldn't build template frome data.");
368
+ }
369
+
370
+ /* *****************************************************************************
371
+ Initialize Iodine::Mustache
372
+ ***************************************************************************** */
373
+
374
+ void iodine_init_mustache(void) {
375
+ call_func_id = rb_intern2("call", 4);
376
+ to_s_func_id = rb_intern2("to_s", 4);
377
+ /**
378
+ Iodine::Mustache offers a logicless mustache template engine with strict HTML
379
+ escaping (more than the basic `"<>'$`).
380
+
381
+ This offers more security against XSS and protects against the chance of
382
+ executing Ruby code within the template.
383
+
384
+ You can test the parser using:
385
+
386
+ TEMPLATE="my_template.mustache"
387
+
388
+ require 'json'
389
+ require 'iodine'
390
+ TIMES = 100
391
+ STR = IO.binread(JSON_FILENAME); nil
392
+
393
+ JSON.parse(STR) == Iodine::JSON.parse(STR) # => true
394
+ JSON.parse(STR,
395
+ symbolize_names: true) == Iodine::JSON.parse(STR,
396
+ symbolize_names: true) # => true
397
+ JSON.parse!(STR) == Iodine::JSON.parse!(STR) # => true/false (unknown)
398
+
399
+ # warm-up
400
+ TIMES.times { JSON.parse STR }
401
+ TIMES.times { Iodine::JSON.parse STR }
402
+
403
+ Benchmark.bm do |b|
404
+ sys = b.report("system") { TIMES.times { JSON.parse STR } }
405
+ sys_sym = b.report("system sym") { TIMES.times { JSON.parse STR,
406
+ symbolize_names: true } }
407
+ iodine = b.report("iodine") { TIMES.times { Iodine::JSON.parse STR } }
408
+ iodine_sym = b.report("iodine sym") do
409
+ TIMES.times { Iodine::JSON.parse STR,
410
+ symbolize_names: true }
411
+ end
412
+ puts "System / Iodine: #{sys/iodine}"
413
+ puts "System-sym/Iodine-sym: #{sys_sym/iodine_sym}"
414
+ end; nil
415
+
416
+
417
+ */
418
+ VALUE tmp = rb_define_class_under(IodineModule, "Mustache", rb_cData);
419
+ rb_define_alloc_func(tmp, iodine_mustache_data_alloc_c);
420
+ rb_define_method(tmp, "initialize", iodine_mustache_new, 1);
421
+ rb_define_method(tmp, "render", iodine_mustache_render, 1);
422
+ // rb_define_module_function(tmp, "parse", iodine_json_parse, -1);
423
+ }