rage-iodine 1.7.58

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
  3. data/.github/workflows/ruby.yml +42 -0
  4. data/.gitignore +20 -0
  5. data/.rspec +2 -0
  6. data/.yardopts +8 -0
  7. data/CHANGELOG.md +1098 -0
  8. data/Gemfile +11 -0
  9. data/LICENSE.txt +21 -0
  10. data/LIMITS.md +41 -0
  11. data/README.md +782 -0
  12. data/Rakefile +23 -0
  13. data/SPEC-PubSub-Draft.md +159 -0
  14. data/SPEC-WebSocket-Draft.md +239 -0
  15. data/bin/console +22 -0
  16. data/bin/info.md +353 -0
  17. data/bin/mustache_bench.rb +100 -0
  18. data/bin/poc/Gemfile.lock +23 -0
  19. data/bin/poc/README.md +37 -0
  20. data/bin/poc/config.ru +66 -0
  21. data/bin/poc/gemfile +1 -0
  22. data/bin/poc/www/index.html +57 -0
  23. data/examples/async_task.ru +92 -0
  24. data/examples/bates/README.md +3 -0
  25. data/examples/bates/config.ru +342 -0
  26. data/examples/bates/david+bold.pdf +0 -0
  27. data/examples/bates/public/drop-pdf.png +0 -0
  28. data/examples/bates/public/index.html +600 -0
  29. data/examples/config.ru +59 -0
  30. data/examples/echo.ru +59 -0
  31. data/examples/etag.ru +16 -0
  32. data/examples/hello.ru +29 -0
  33. data/examples/pubsub_engine.ru +81 -0
  34. data/examples/rack3.ru +12 -0
  35. data/examples/redis.ru +70 -0
  36. data/examples/shootout.ru +73 -0
  37. data/examples/sub-protocols.ru +90 -0
  38. data/examples/tcp_client.rb +66 -0
  39. data/examples/x-sendfile.ru +14 -0
  40. data/exe/iodine +280 -0
  41. data/ext/iodine/extconf.rb +110 -0
  42. data/ext/iodine/fio.c +12096 -0
  43. data/ext/iodine/fio.h +6390 -0
  44. data/ext/iodine/fio_cli.c +431 -0
  45. data/ext/iodine/fio_cli.h +189 -0
  46. data/ext/iodine/fio_json_parser.h +687 -0
  47. data/ext/iodine/fio_siphash.c +157 -0
  48. data/ext/iodine/fio_siphash.h +37 -0
  49. data/ext/iodine/fio_tls.h +129 -0
  50. data/ext/iodine/fio_tls_missing.c +649 -0
  51. data/ext/iodine/fio_tls_openssl.c +1056 -0
  52. data/ext/iodine/fio_tmpfile.h +50 -0
  53. data/ext/iodine/fiobj.h +44 -0
  54. data/ext/iodine/fiobj4fio.h +21 -0
  55. data/ext/iodine/fiobj_ary.c +333 -0
  56. data/ext/iodine/fiobj_ary.h +139 -0
  57. data/ext/iodine/fiobj_data.c +1185 -0
  58. data/ext/iodine/fiobj_data.h +167 -0
  59. data/ext/iodine/fiobj_hash.c +409 -0
  60. data/ext/iodine/fiobj_hash.h +176 -0
  61. data/ext/iodine/fiobj_json.c +622 -0
  62. data/ext/iodine/fiobj_json.h +68 -0
  63. data/ext/iodine/fiobj_mem.h +71 -0
  64. data/ext/iodine/fiobj_mustache.c +317 -0
  65. data/ext/iodine/fiobj_mustache.h +62 -0
  66. data/ext/iodine/fiobj_numbers.c +344 -0
  67. data/ext/iodine/fiobj_numbers.h +127 -0
  68. data/ext/iodine/fiobj_str.c +433 -0
  69. data/ext/iodine/fiobj_str.h +172 -0
  70. data/ext/iodine/fiobject.c +620 -0
  71. data/ext/iodine/fiobject.h +654 -0
  72. data/ext/iodine/hpack.h +1923 -0
  73. data/ext/iodine/http.c +2736 -0
  74. data/ext/iodine/http.h +1019 -0
  75. data/ext/iodine/http1.c +825 -0
  76. data/ext/iodine/http1.h +29 -0
  77. data/ext/iodine/http1_parser.h +1835 -0
  78. data/ext/iodine/http_internal.c +1279 -0
  79. data/ext/iodine/http_internal.h +248 -0
  80. data/ext/iodine/http_mime_parser.h +350 -0
  81. data/ext/iodine/iodine.c +1433 -0
  82. data/ext/iodine/iodine.h +64 -0
  83. data/ext/iodine/iodine_caller.c +218 -0
  84. data/ext/iodine/iodine_caller.h +27 -0
  85. data/ext/iodine/iodine_connection.c +941 -0
  86. data/ext/iodine/iodine_connection.h +55 -0
  87. data/ext/iodine/iodine_defer.c +420 -0
  88. data/ext/iodine/iodine_defer.h +6 -0
  89. data/ext/iodine/iodine_fiobj2rb.h +120 -0
  90. data/ext/iodine/iodine_helpers.c +282 -0
  91. data/ext/iodine/iodine_helpers.h +12 -0
  92. data/ext/iodine/iodine_http.c +1280 -0
  93. data/ext/iodine/iodine_http.h +23 -0
  94. data/ext/iodine/iodine_json.c +302 -0
  95. data/ext/iodine/iodine_json.h +6 -0
  96. data/ext/iodine/iodine_mustache.c +567 -0
  97. data/ext/iodine/iodine_mustache.h +6 -0
  98. data/ext/iodine/iodine_pubsub.c +580 -0
  99. data/ext/iodine/iodine_pubsub.h +26 -0
  100. data/ext/iodine/iodine_rack_io.c +273 -0
  101. data/ext/iodine/iodine_rack_io.h +20 -0
  102. data/ext/iodine/iodine_store.c +142 -0
  103. data/ext/iodine/iodine_store.h +20 -0
  104. data/ext/iodine/iodine_tcp.c +346 -0
  105. data/ext/iodine/iodine_tcp.h +13 -0
  106. data/ext/iodine/iodine_tls.c +261 -0
  107. data/ext/iodine/iodine_tls.h +13 -0
  108. data/ext/iodine/mustache_parser.h +1546 -0
  109. data/ext/iodine/redis_engine.c +957 -0
  110. data/ext/iodine/redis_engine.h +79 -0
  111. data/ext/iodine/resp_parser.h +317 -0
  112. data/ext/iodine/scheduler.c +173 -0
  113. data/ext/iodine/scheduler.h +6 -0
  114. data/ext/iodine/websocket_parser.h +506 -0
  115. data/ext/iodine/websockets.c +752 -0
  116. data/ext/iodine/websockets.h +185 -0
  117. data/iodine.gemspec +50 -0
  118. data/lib/iodine/connection.rb +61 -0
  119. data/lib/iodine/json.rb +42 -0
  120. data/lib/iodine/mustache.rb +113 -0
  121. data/lib/iodine/pubsub.rb +55 -0
  122. data/lib/iodine/rack_utils.rb +43 -0
  123. data/lib/iodine/tls.rb +16 -0
  124. data/lib/iodine/version.rb +3 -0
  125. data/lib/iodine.rb +274 -0
  126. data/lib/rack/handler/iodine.rb +33 -0
  127. data/logo.png +0 -0
  128. metadata +284 -0
@@ -0,0 +1,433 @@
1
+ /*
2
+ Copyright: Boaz Segev, 2017-2019
3
+ License: MIT
4
+ */
5
+
6
+ #if defined(__unix__) || defined(__APPLE__) || defined(__linux__)
7
+ #ifndef _GNU_SOURCE
8
+ #define _GNU_SOURCE
9
+ #endif
10
+ #include <unistd.h>
11
+ #endif
12
+
13
+ #ifdef _SC_PAGESIZE
14
+ #define PAGE_SIZE sysconf(_SC_PAGESIZE)
15
+ #else
16
+ #define PAGE_SIZE 4096
17
+ #endif
18
+
19
+ #include <fiobject.h>
20
+
21
+ #include <fio_siphash.h>
22
+ #include <fiobj_numbers.h>
23
+ #include <fiobj_str.h>
24
+
25
+ #include <assert.h>
26
+ #include <errno.h>
27
+ #include <fcntl.h>
28
+ #include <limits.h>
29
+ #include <string.h>
30
+ #include <sys/stat.h>
31
+
32
+ #define FIO_INCLUDE_STR
33
+ #define FIO_STR_NO_REF
34
+ #include <fio.h>
35
+
36
+ #ifndef PATH_MAX
37
+ #define PATH_MAX PAGE_SIZE
38
+ #endif
39
+
40
+ #include <pthread.h>
41
+
42
+ /* *****************************************************************************
43
+ String Type
44
+ ***************************************************************************** */
45
+
46
+ typedef struct {
47
+ fiobj_object_header_s head;
48
+ uint64_t hash;
49
+ fio_str_s str;
50
+ } fiobj_str_s;
51
+
52
+ #define obj2str(o) ((fiobj_str_s *)(FIOBJ2PTR(o)))
53
+
54
+ static inline fio_str_info_s fiobj_str_get_cstr(const FIOBJ o) {
55
+ return fio_str_info(&obj2str(o)->str);
56
+ }
57
+
58
+ /* *****************************************************************************
59
+ String VTables
60
+ ***************************************************************************** */
61
+
62
+ static fio_str_info_s fio_str2str(const FIOBJ o) {
63
+ return fiobj_str_get_cstr(o);
64
+ }
65
+
66
+ static void fiobj_str_dealloc(FIOBJ o, void (*task)(FIOBJ, void *), void *arg) {
67
+ fio_str_free(&obj2str(o)->str);
68
+ fio_free(FIOBJ2PTR(o));
69
+ (void)task;
70
+ (void)arg;
71
+ }
72
+
73
+ static size_t fiobj_str_is_eq(const FIOBJ self, const FIOBJ other) {
74
+ return fio_str_iseq(&obj2str(self)->str, &obj2str(other)->str);
75
+ }
76
+
77
+ static intptr_t fio_str2i(const FIOBJ o) {
78
+ char *pos = fio_str_data(&obj2str(o)->str);
79
+ return fio_atol(&pos);
80
+ }
81
+ static double fio_str2f(const FIOBJ o) {
82
+ char *pos = fio_str_data(&obj2str(o)->str);
83
+ return fio_atof(&pos);
84
+ }
85
+
86
+ static size_t fio_str2bool(const FIOBJ o) {
87
+ return fio_str_len(&obj2str(o)->str) != 0;
88
+ }
89
+
90
+ uintptr_t fiobject___noop_count(const FIOBJ o);
91
+
92
+ const fiobj_object_vtable_s FIOBJECT_VTABLE_STRING = {
93
+ .class_name = "String",
94
+ .dealloc = fiobj_str_dealloc,
95
+ .to_i = fio_str2i,
96
+ .to_f = fio_str2f,
97
+ .to_str = fio_str2str,
98
+ .is_eq = fiobj_str_is_eq,
99
+ .is_true = fio_str2bool,
100
+ .count = fiobject___noop_count,
101
+ };
102
+
103
+ /* *****************************************************************************
104
+ String API
105
+ ***************************************************************************** */
106
+
107
+ /** Creates a buffer String object. Remember to use `fiobj_free`. */
108
+ FIOBJ fiobj_str_buf(size_t capa) {
109
+ if (capa)
110
+ capa = capa + 1;
111
+ else
112
+ capa = PAGE_SIZE;
113
+
114
+ fiobj_str_s *s = fio_malloc(sizeof(*s));
115
+ if (!s) {
116
+ perror("ERROR: fiobj string couldn't allocate memory");
117
+ exit(errno);
118
+ }
119
+ *s = (fiobj_str_s){
120
+ .head =
121
+ {
122
+ .ref = 1,
123
+ .type = FIOBJ_T_STRING,
124
+ },
125
+ .str = FIO_STR_INIT,
126
+ };
127
+ if (capa) {
128
+ fio_str_capa_assert(&s->str, capa);
129
+ }
130
+ return ((uintptr_t)s | FIOBJECT_STRING_FLAG);
131
+ }
132
+
133
+ /** Creates a String object. Remember to use `fiobj_free`. */
134
+ FIOBJ fiobj_str_new(const char *str, size_t len) {
135
+ fiobj_str_s *s = fio_malloc(sizeof(*s));
136
+ if (!s) {
137
+ perror("ERROR: fiobj string couldn't allocate memory");
138
+ exit(errno);
139
+ }
140
+ *s = (fiobj_str_s){
141
+ .head =
142
+ {
143
+ .ref = 1,
144
+ .type = FIOBJ_T_STRING,
145
+ },
146
+ .str = FIO_STR_INIT,
147
+ };
148
+ if (str && len) {
149
+ fio_str_write(&s->str, str, len);
150
+ }
151
+ return ((uintptr_t)s | FIOBJECT_STRING_FLAG);
152
+ }
153
+
154
+ /**
155
+ * Creates a String object. Remember to use `fiobj_free`.
156
+ *
157
+ * It's possible to wrap a previosly allocated memory block in a FIOBJ String
158
+ * object, as long as it was allocated using `fio_malloc`.
159
+ *
160
+ * The ownership of the memory indicated by `str` will "move" to the object and
161
+ * will be freed (using `fio_free`) once the object's reference count drops to
162
+ * zero.
163
+ */
164
+ FIOBJ fiobj_str_move(char *str, size_t len, size_t capacity) {
165
+ fiobj_str_s *s = fio_malloc(sizeof(*s));
166
+ if (!s) {
167
+ perror("ERROR: fiobj string couldn't allocate memory");
168
+ exit(errno);
169
+ }
170
+ *s = (fiobj_str_s){
171
+ .head =
172
+ {
173
+ .ref = 1,
174
+ .type = FIOBJ_T_STRING,
175
+ },
176
+ .str = FIO_STR_INIT_EXISTING(str, len, capacity),
177
+ };
178
+ return ((uintptr_t)s | FIOBJECT_STRING_FLAG);
179
+ }
180
+
181
+ static pthread_key_t str_tmp_key;
182
+ static pthread_once_t str_tmp_once = PTHREAD_ONCE_INIT;
183
+ static void init_str_tmp_key(void) {
184
+ pthread_key_create(&str_tmp_key, free);
185
+ }
186
+ static void init_str_tmp_key_ptr(void) {
187
+ fiobj_str_s *tmp = malloc(sizeof(fiobj_str_s));
188
+ FIO_ASSERT_ALLOC(tmp);
189
+ tmp->head.ref = ((~(uint32_t)0) >> 4);
190
+ tmp->head.type = FIOBJ_T_STRING;
191
+ tmp->str.small = 1;
192
+ pthread_setspecific(str_tmp_key, tmp);
193
+ }
194
+ /**
195
+ * Returns a thread-static temporary string. Avoid calling `fiobj_dup` or
196
+ * `fiobj_free`.
197
+ */
198
+ FIOBJ fiobj_str_tmp(void) {
199
+ pthread_once(&str_tmp_once, init_str_tmp_key);
200
+ fiobj_str_s *tmp = (fiobj_str_s *)pthread_getspecific(str_tmp_key);
201
+ if (!tmp) {
202
+ init_str_tmp_key_ptr();
203
+ tmp = (fiobj_str_s *)pthread_getspecific(str_tmp_key);
204
+ }
205
+ tmp->str.frozen = 0;
206
+ fio_str_resize(&tmp->str, 0);
207
+ return ((uintptr_t)tmp | FIOBJECT_STRING_FLAG);
208
+ }
209
+
210
+ /** Prevents the String object from being changed. */
211
+ void fiobj_str_freeze(FIOBJ str) {
212
+ if (FIOBJ_TYPE_IS(str, FIOBJ_T_STRING))
213
+ fio_str_freeze(&obj2str(str)->str);
214
+ }
215
+
216
+ /** Confirms the requested capacity is available and allocates as required. */
217
+ size_t fiobj_str_capa_assert(FIOBJ str, size_t size) {
218
+
219
+ assert(FIOBJ_TYPE_IS(str, FIOBJ_T_STRING));
220
+ if (obj2str(str)->str.frozen)
221
+ return 0;
222
+ fio_str_info_s state = fio_str_capa_assert(&obj2str(str)->str, size);
223
+ return state.capa;
224
+ }
225
+
226
+ /** Return's a String's capacity, if any. */
227
+ size_t fiobj_str_capa(FIOBJ str) {
228
+ assert(FIOBJ_TYPE_IS(str, FIOBJ_T_STRING));
229
+ return fio_str_capa(&obj2str(str)->str);
230
+ }
231
+
232
+ /** Resizes a String object, allocating more memory if required. */
233
+ void fiobj_str_resize(FIOBJ str, size_t size) {
234
+ assert(FIOBJ_TYPE_IS(str, FIOBJ_T_STRING));
235
+ fio_str_resize(&obj2str(str)->str, size);
236
+ obj2str(str)->hash = 0;
237
+ return;
238
+ }
239
+
240
+ /** Deallocates any unnecessary memory (if supported by OS). */
241
+ void fiobj_str_compact(FIOBJ str) {
242
+ assert(FIOBJ_TYPE_IS(str, FIOBJ_T_STRING));
243
+ fio_str_compact(&obj2str(str)->str);
244
+ return;
245
+ }
246
+
247
+ /** Empties a String's data. */
248
+ void fiobj_str_clear(FIOBJ str) {
249
+ assert(FIOBJ_TYPE_IS(str, FIOBJ_T_STRING));
250
+ fio_str_resize(&obj2str(str)->str, 0);
251
+ obj2str(str)->hash = 0;
252
+ }
253
+
254
+ /**
255
+ * Writes data at the end of the string, resizing the string as required.
256
+ * Returns the new length of the String
257
+ */
258
+ size_t fiobj_str_write(FIOBJ dest, const char *data, size_t len) {
259
+ assert(FIOBJ_TYPE_IS(dest, FIOBJ_T_STRING));
260
+ if (obj2str(dest)->str.frozen)
261
+ return 0;
262
+ obj2str(dest)->hash = 0;
263
+ return fio_str_write(&obj2str(dest)->str, data, len).len;
264
+ }
265
+
266
+ /**
267
+ * Writes a number at the end of the String using normal base 10 notation.
268
+ *
269
+ * Returns the new length of the String
270
+ */
271
+ size_t fiobj_str_write_i(FIOBJ dest, int64_t num) {
272
+ assert(FIOBJ_TYPE_IS(dest, FIOBJ_T_STRING));
273
+ if (obj2str(dest)->str.frozen)
274
+ return 0;
275
+ obj2str(dest)->hash = 0;
276
+ return fio_str_write_i(&obj2str(dest)->str, num).len;
277
+ }
278
+
279
+ /**
280
+ * Writes data at the end of the string, resizing the string as required.
281
+ * Returns the new length of the String
282
+ */
283
+ size_t fiobj_str_printf(FIOBJ dest, const char *format, ...) {
284
+ assert(FIOBJ_TYPE_IS(dest, FIOBJ_T_STRING));
285
+ if (obj2str(dest)->str.frozen)
286
+ return 0;
287
+ obj2str(dest)->hash = 0;
288
+ va_list argv;
289
+ va_start(argv, format);
290
+ fio_str_info_s state = fio_str_vprintf(&obj2str(dest)->str, format, argv);
291
+ va_end(argv);
292
+ return state.len;
293
+ }
294
+
295
+ size_t fiobj_str_vprintf(FIOBJ dest, const char *format, va_list argv) {
296
+ assert(FIOBJ_TYPE_IS(dest, FIOBJ_T_STRING));
297
+ if (obj2str(dest)->str.frozen)
298
+ return 0;
299
+ obj2str(dest)->hash = 0;
300
+ fio_str_info_s state = fio_str_vprintf(&obj2str(dest)->str, format, argv);
301
+ return state.len;
302
+ }
303
+
304
+ /** Dumps the `filename` file's contents at the end of a String. If `limit ==
305
+ * 0`, than the data will be read until EOF.
306
+ *
307
+ * If the file can't be located, opened or read, or if `start_at` is beyond
308
+ * the EOF position, NULL is returned.
309
+ *
310
+ * Remember to use `fiobj_free`.
311
+ */
312
+ size_t fiobj_str_readfile(FIOBJ dest, const char *filename, intptr_t start_at,
313
+ intptr_t limit) {
314
+ fio_str_info_s state =
315
+ fio_str_readfile(&obj2str(dest)->str, filename, start_at, limit);
316
+ return state.len;
317
+ }
318
+
319
+ /**
320
+ * Writes data at the end of the string, resizing the string as required.
321
+ * Returns the new length of the String
322
+ */
323
+ size_t fiobj_str_concat(FIOBJ dest, FIOBJ obj) {
324
+ assert(FIOBJ_TYPE_IS(dest, FIOBJ_T_STRING));
325
+ if (obj2str(dest)->str.frozen)
326
+ return 0;
327
+ obj2str(dest)->hash = 0;
328
+ fio_str_info_s o = fiobj_obj2cstr(obj);
329
+ if (o.len == 0)
330
+ return fio_str_len(&obj2str(dest)->str);
331
+ return fio_str_write(&obj2str(dest)->str, o.data, o.len).len;
332
+ }
333
+
334
+ /**
335
+ * Calculates a String's SipHash value for use as a HashMap key.
336
+ */
337
+ uint64_t fiobj_str_hash(FIOBJ o) {
338
+ assert(FIOBJ_TYPE_IS(o, FIOBJ_T_STRING));
339
+ // if (obj2str(o)->is_small) {
340
+ // return fiobj_hash_string(STR_INTENAL_STR(o), STR_INTENAL_LEN(o));
341
+ // } else
342
+ if (obj2str(o)->hash) {
343
+ return obj2str(o)->hash;
344
+ }
345
+ fio_str_info_s state = fio_str_info(&obj2str(o)->str);
346
+ obj2str(o)->hash = fiobj_hash_string(state.data, state.len);
347
+ return obj2str(o)->hash;
348
+ }
349
+
350
+ /* *****************************************************************************
351
+ Tests
352
+ ***************************************************************************** */
353
+
354
+ #if DEBUG
355
+ void fiobj_test_string(void) {
356
+ fprintf(stderr, "=== Testing Strings\n");
357
+ fprintf(stderr, "* Internal String Capacity %u \n",
358
+ (unsigned int)FIO_STR_SMALL_CAPA);
359
+ #define TEST_ASSERT(cond, ...) \
360
+ if (!(cond)) { \
361
+ fprintf(stderr, "* " __VA_ARGS__); \
362
+ fprintf(stderr, "Testing failed.\n"); \
363
+ exit(-1); \
364
+ }
365
+ #define STR_EQ(o, str) \
366
+ TEST_ASSERT((fiobj_str_getlen(o) == strlen(str) && \
367
+ !memcmp(fiobj_str_mem_addr(o), str, strlen(str))), \
368
+ "String not equal to " str)
369
+ FIOBJ o = fiobj_str_new("Hello", 5);
370
+ TEST_ASSERT(FIOBJ_TYPE_IS(o, FIOBJ_T_STRING), "Small String isn't string!\n");
371
+ TEST_ASSERT(obj2str(o)->str.small, "Hello isn't small\n");
372
+ fiobj_str_write(o, " World", 6);
373
+ TEST_ASSERT(FIOBJ_TYPE_IS(o, FIOBJ_T_STRING),
374
+ "Hello World String isn't string!\n");
375
+ TEST_ASSERT(obj2str(o)->str.small, "Hello World isn't small\n");
376
+ TEST_ASSERT(fiobj_obj2cstr(o).len == 11,
377
+ "Invalid small string length (%u != 11)!\n",
378
+ (unsigned int)fiobj_obj2cstr(o).len)
379
+ fiobj_str_write(o, " World, you crazy longer sleep loving person :-)", 48);
380
+ TEST_ASSERT(!obj2str(o)->str.small, "Crazier shouldn't be small\n");
381
+ fiobj_free(o);
382
+
383
+ o = fiobj_str_new(
384
+ "hello my dear friend, I hope that your are well and happy.", 58);
385
+ TEST_ASSERT(FIOBJ_TYPE_IS(o, FIOBJ_T_STRING), "Long String isn't string!\n");
386
+ TEST_ASSERT(!obj2str(o)->str.small,
387
+ "Long String is small! (capa: %lu, len: %lu)\n",
388
+ fio_str_capa(&obj2str(o)->str), fio_str_len(&obj2str(o)->str));
389
+ TEST_ASSERT(fiobj_obj2cstr(o).len == 58,
390
+ "Invalid long string length (%lu != 58)!\n",
391
+ fiobj_obj2cstr(o).len)
392
+ uint64_t hash = fiobj_str_hash(o);
393
+ TEST_ASSERT(!obj2str(o)->str.frozen, "String forzen when only hashing!\n");
394
+ fiobj_str_freeze(o);
395
+ TEST_ASSERT(obj2str(o)->str.frozen, "String not forzen!\n");
396
+ fiobj_str_write(o, " World", 6);
397
+ TEST_ASSERT(hash == fiobj_str_hash(o),
398
+ "String hash changed after hashing - not frozen?\n");
399
+ TEST_ASSERT(fiobj_obj2cstr(o).len == 58,
400
+ "String was edited after hashing - not frozen!\n (%lu): %s",
401
+ (unsigned long)fiobj_obj2cstr(o).len, fiobj_obj2cstr(o).data);
402
+ fiobj_free(o);
403
+
404
+ o = fiobj_str_buf(1);
405
+ fiobj_str_printf(o, "%u", 42);
406
+ TEST_ASSERT(fio_str_len(&obj2str(o)->str) == 2,
407
+ "fiobj_strprintf length error.\n");
408
+ TEST_ASSERT(fiobj_obj2num(o), "fiobj_strprintf integer error.\n");
409
+ TEST_ASSERT(!memcmp(fiobj_obj2cstr(o).data, "42", 2),
410
+ "fiobj_strprintf string error.\n");
411
+ fiobj_free(o);
412
+
413
+ o = fiobj_str_buf(4);
414
+ for (int i = 0; i < 16000; ++i) {
415
+ fiobj_str_write(o, "a", 1);
416
+ }
417
+ TEST_ASSERT(fio_str_len(&obj2str(o)->str) == 16000,
418
+ "16K fiobj_str_write not 16K.\n");
419
+ TEST_ASSERT(fio_str_capa(&obj2str(o)->str) >= 16000,
420
+ "16K fiobj_str_write capa not enough.\n");
421
+ fiobj_free(o);
422
+
423
+ o = fiobj_str_buf(0);
424
+ TEST_ASSERT(fiobj_str_readfile(o, __FILE__, 0, 0),
425
+ "`fiobj_str_readfile` - file wasn't read!");
426
+ TEST_ASSERT(!memcmp(fiobj_obj2cstr(o).data, "/*", 2),
427
+ "`fiobj_str_readfile` error, start of file doesn't match:\n%s",
428
+ fiobj_obj2cstr(o).data);
429
+ fiobj_free(o);
430
+
431
+ fprintf(stderr, "* passed.\n");
432
+ }
433
+ #endif
@@ -0,0 +1,172 @@
1
+ #ifndef H_FIOBJ_STR_H
2
+ /*
3
+ Copyright: Boaz Segev, 2017-2019
4
+ License: MIT
5
+ */
6
+ #define H_FIOBJ_STR_H
7
+
8
+ #include <fiobject.h>
9
+
10
+ #ifdef __cplusplus
11
+ extern "C" {
12
+ #endif
13
+
14
+ #define FIOBJ_IS_STRING(obj) FIOBJ_TYPE_IS((obj), FIOBJ_T_STRING)
15
+
16
+ /* *****************************************************************************
17
+ API: Creating a String Object
18
+ ***************************************************************************** */
19
+
20
+ /** Creates a String object. Remember to use `fiobj_free`. */
21
+ FIOBJ fiobj_str_new(const char *str, size_t len);
22
+
23
+ /**
24
+ * Creates a String object with pre-allocation for Strings up to `capa` long.
25
+ *
26
+ * If `capa` is zero, a whole memory page will be allocated.
27
+ *
28
+ * Remember to use `fiobj_free`.
29
+ */
30
+ FIOBJ fiobj_str_buf(size_t capa);
31
+
32
+ /** Creates a copy from an existing String. Remember to use `fiobj_free`. */
33
+ static inline __attribute__((unused)) FIOBJ fiobj_str_copy(FIOBJ src) {
34
+ fio_str_info_s s = fiobj_obj2cstr(src);
35
+ return fiobj_str_new(s.data, s.len);
36
+ }
37
+
38
+ /**
39
+ * Creates a String object. Remember to use `fiobj_free`.
40
+ *
41
+ * It's possible to wrap a previosly allocated memory block in a FIOBJ String
42
+ * object, as long as it was allocated using `fio_malloc`.
43
+ *
44
+ * The ownership of the memory indicated by `str` will "move" to the object and
45
+ * will be freed (using `fio_free`) once the object's reference count drops to
46
+ * zero.
47
+ *
48
+ * Note: The original memory MUST be allocated using `fio_malloc` (NOT the
49
+ * system's `malloc`) and it will be freed using `fio_free`.
50
+ */
51
+ FIOBJ fiobj_str_move(char *str, size_t len, size_t capacity);
52
+
53
+ /**
54
+ * Returns a thread-static temporary string. Avoid calling `fiobj_dup` or
55
+ * `fiobj_free`.
56
+ */
57
+ FIOBJ fiobj_str_tmp(void);
58
+
59
+ /* *****************************************************************************
60
+ API: Editing a String
61
+ ***************************************************************************** */
62
+
63
+ /**
64
+ * Prevents the String object from being changed.
65
+ *
66
+ * When a String is used as a key for a Hash, it is automatically frozen to
67
+ * prevent the Hash from becoming broken.
68
+ */
69
+ void fiobj_str_freeze(FIOBJ str);
70
+
71
+ /**
72
+ * Confirms the String allows for the requested capacity (counting used space as
73
+ * well as free space).
74
+ *
75
+ * Returns updated capacity.
76
+ */
77
+ size_t fiobj_str_capa_assert(FIOBJ str, size_t size);
78
+
79
+ /** Returns a String's capacity, if any. This should include the NUL byte. */
80
+ size_t fiobj_str_capa(FIOBJ str);
81
+
82
+ /** Resizes a String object, allocating more memory if required. */
83
+ void fiobj_str_resize(FIOBJ str, size_t size);
84
+
85
+ /**
86
+ * Performs a best attempt at minimizing memory consumption.
87
+ *
88
+ * Actual effects depend on the underlying memory allocator and it's
89
+ * implementation. Not all allocators will free any memory.
90
+ */
91
+ void fiobj_str_compact(FIOBJ str);
92
+
93
+ /** Alias for `fiobj_str_compact`. */
94
+ #define fiobj_str_minimize(str) fiobj_str_compact((str))
95
+
96
+ /** Empties a String's data. */
97
+ void fiobj_str_clear(FIOBJ str);
98
+
99
+ /**
100
+ * Writes data at the end of the string, resizing the string as required.
101
+ * Returns the new length of the String
102
+ */
103
+ size_t fiobj_str_write(FIOBJ dest, const char *data, size_t len);
104
+
105
+ /**
106
+ * Writes a number at the end of the String using normal base 10 notation.
107
+ *
108
+ * Returns the new length of the String
109
+ */
110
+ size_t fiobj_str_write_i(FIOBJ dest, int64_t num);
111
+
112
+ /**
113
+ * Writes data at the end of the string using a printf like interface, resizing
114
+ * the string as required. Returns the new length of the String
115
+ */
116
+ __attribute__((format(printf, 2, 3))) size_t
117
+ fiobj_str_printf(FIOBJ dest, const char *format, ...);
118
+
119
+ /**
120
+ * Writes data at the end of the string using a vprintf like interface, resizing
121
+ * the string as required.
122
+ *
123
+ * Returns the new length of the String
124
+ */
125
+ __attribute__((format(printf, 2, 0))) size_t
126
+ fiobj_str_vprintf(FIOBJ dest, const char *format, va_list argv);
127
+
128
+ /**
129
+ * Writes data at the end of the string, resizing the string as required.
130
+ *
131
+ * Remember to call `fiobj_free` to free the source (when done with it).
132
+ *
133
+ * Returns the new length of the String.
134
+ */
135
+ size_t fiobj_str_concat(FIOBJ dest, FIOBJ source);
136
+ #define fiobj_str_join(dest, src) fiobj_str_concat((dest), (src))
137
+
138
+ /**
139
+ * Dumps the `filename` file's contents at the end of the String.
140
+ *
141
+ * If `limit == 0`, than the data will be read until EOF.
142
+ *
143
+ * If the file can't be located, opened or read, or if `start_at` is out of
144
+ * bounds (i.e., beyond the EOF position), FIOBJ_INVALID is returned.
145
+ *
146
+ * If `start_at` is negative, it will be computed from the end of the file.
147
+ *
148
+ * Remember to use `fiobj_free`.
149
+ *
150
+ * NOTE: Requires a UNIX system, otherwise always returns FIOBJ_INVALID.
151
+ */
152
+ size_t fiobj_str_readfile(FIOBJ dest, const char *filename, intptr_t start_at,
153
+ intptr_t limit);
154
+
155
+ /* *****************************************************************************
156
+ API: String Values
157
+ ***************************************************************************** */
158
+
159
+ /**
160
+ * Calculates a String's SipHash value for possible use as a HashMap key.
161
+ */
162
+ uint64_t fiobj_str_hash(FIOBJ o);
163
+
164
+ #if DEBUG
165
+ void fiobj_test_string(void);
166
+ #endif
167
+
168
+ #ifdef __cplusplus
169
+ } /* extern "C" */
170
+ #endif
171
+
172
+ #endif