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,1185 @@
1
+ #if defined(__unix__) || defined(__APPLE__) || defined(__linux__) || \
2
+ defined(__CYGWIN__) || defined(__MINGW32__) /* require POSIX */
3
+ /*
4
+ Copyright: Boaz Segev, 2017-2019
5
+ License: MIT
6
+ */
7
+ #ifndef _GNU_SOURCE
8
+ #define _GNU_SOURCE
9
+ #endif
10
+
11
+ /**
12
+ * A dynamic type for reading / writing to a local file, a temporary file or an
13
+ * in-memory string.
14
+ *
15
+ * Supports basic reak, write, seek, puts and gets operations.
16
+ *
17
+ * Writing is always performed at the end of the stream / memory buffer,
18
+ * ignoring the current seek position.
19
+ */
20
+ #include <fio_tmpfile.h>
21
+ #include <fiobj_data.h>
22
+ #include <fiobj_str.h>
23
+
24
+ #include <assert.h>
25
+ #include <errno.h>
26
+ #include <fcntl.h>
27
+ #include <string.h>
28
+ #include <sys/stat.h>
29
+ #include <sys/types.h>
30
+ #include <unistd.h>
31
+
32
+ #include <fio.h>
33
+
34
+ /* *****************************************************************************
35
+ Numbers Type
36
+ ***************************************************************************** */
37
+
38
+ typedef struct {
39
+ fiobj_object_header_s head;
40
+ uint8_t *buffer; /* reader buffer */
41
+ union {
42
+ FIOBJ parent;
43
+ void (*dealloc)(void *); /* buffer deallocation function */
44
+ size_t fpos; /* the file reader's position */
45
+ } source;
46
+ size_t capa; /* total buffer capacity / slice offset */
47
+ size_t len; /* length of valid data in buffer */
48
+ size_t pos; /* position of reader */
49
+ int fd; /* file descriptor (-1 if invalid). */
50
+ } fiobj_data_s;
51
+
52
+ #define obj2io(o) ((fiobj_data_s *)(o))
53
+
54
+ /* *****************************************************************************
55
+ Object required VTable and functions
56
+ ***************************************************************************** */
57
+
58
+ #define REQUIRE_MEM(mem) \
59
+ do { \
60
+ if ((mem) == NULL) { \
61
+ perror("FATAL ERROR: fiobj IO couldn't allocate memory"); \
62
+ exit(errno); \
63
+ } \
64
+ } while (0)
65
+
66
+ static void fiobj_data_copy_buffer(FIOBJ o) {
67
+ obj2io(o)->capa = (((obj2io(o)->len) >> 12) + 1) << 12;
68
+ void *tmp = fio_malloc(obj2io(o)->capa);
69
+ REQUIRE_MEM(tmp);
70
+ memcpy(tmp, obj2io(o)->buffer, obj2io(o)->len);
71
+ if (obj2io(o)->source.dealloc)
72
+ obj2io(o)->source.dealloc(obj2io(o)->buffer);
73
+ obj2io(o)->source.dealloc = fio_free;
74
+ obj2io(o)->buffer = tmp;
75
+ }
76
+
77
+ static void fiobj_data_copy_parent(FIOBJ o) {
78
+ switch (obj2io(obj2io(o)->source.parent)->fd) {
79
+ case -1:
80
+ obj2io(o)->buffer = fio_malloc(obj2io(o)->len + 1);
81
+ FIO_ASSERT_ALLOC(obj2io(o)->buffer);
82
+ memcpy(obj2io(o)->buffer,
83
+ obj2io(obj2io(o)->source.parent)->buffer + obj2io(o)->capa,
84
+ obj2io(o)->len);
85
+ obj2io(o)->buffer[obj2io(o)->len] = 0;
86
+ obj2io(o)->capa = obj2io(o)->len;
87
+ obj2io(o)->fd = -1;
88
+ fiobj_free(obj2io(o)->source.parent);
89
+ obj2io(o)->source.dealloc = fio_free;
90
+ return;
91
+ default:
92
+ obj2io(o)->fd = fio_tmpfile();
93
+ if (obj2io(o)->fd < 0) {
94
+ perror("FATAL ERROR: (fiobj_data) can't create temporary file");
95
+ exit(errno);
96
+ }
97
+ fio_str_info_s data;
98
+ size_t pos = 0;
99
+ do {
100
+ ssize_t written;
101
+ data = fiobj_data_pread(obj2io(o)->source.parent, pos + obj2io(o)->capa,
102
+ 4096);
103
+ if (data.len + pos > obj2io(o)->len)
104
+ data.len = obj2io(o)->len - pos;
105
+ retry_int:
106
+ #ifdef __MINGW32__
107
+ written = pwrite(obj2io(o)->fd, data.data, data.len, 0);
108
+ #else
109
+ written = write(obj2io(o)->fd, data.data, data.len);
110
+ #endif
111
+ if (written < 0) {
112
+ if (errno == EINTR)
113
+ goto retry_int;
114
+ perror("FATAL ERROR: (fiobj_data) can't write to temporary file");
115
+ exit(errno);
116
+ }
117
+ pos += written;
118
+ } while (data.len == 4096);
119
+ fiobj_free(obj2io(o)->source.parent);
120
+ obj2io(o)->capa = 0;
121
+ obj2io(o)->len = pos;
122
+ obj2io(o)->source.fpos = obj2io(o)->pos;
123
+ obj2io(o)->pos = 0;
124
+ obj2io(o)->buffer = NULL;
125
+ break;
126
+ }
127
+ }
128
+
129
+ static inline void fiobj_data_pre_write(FIOBJ o, uintptr_t length) {
130
+ switch (obj2io(o)->fd) {
131
+ case -1:
132
+ if (obj2io(o)->source.dealloc != fio_free) {
133
+ fiobj_data_copy_buffer(o);
134
+ }
135
+ break;
136
+ case -2:
137
+ fiobj_data_copy_parent(o);
138
+ break;
139
+ }
140
+ if (obj2io(o)->capa >= obj2io(o)->len + length)
141
+ return;
142
+ /* add rounded pages (4096) to capacity */
143
+ obj2io(o)->capa = (((obj2io(o)->len + length) >> 12) + 1) << 12;
144
+ obj2io(o)->buffer = fio_realloc(obj2io(o)->buffer, obj2io(o)->capa);
145
+ REQUIRE_MEM(obj2io(o)->buffer);
146
+ }
147
+
148
+ static inline int64_t fiobj_data_get_fd_size(const FIOBJ o) {
149
+ struct stat stat;
150
+ retry:
151
+ if (fstat(obj2io(o)->fd, &stat)) {
152
+ if (errno == EINTR)
153
+ goto retry;
154
+ return -1;
155
+ }
156
+ return stat.st_size;
157
+ }
158
+
159
+ static FIOBJ fiobj_data_alloc(void *buffer, int fd) {
160
+ fiobj_data_s *io = fio_malloc(sizeof(*io));
161
+ REQUIRE_MEM(io);
162
+ *io = (fiobj_data_s){
163
+ .head = {.ref = 1, .type = FIOBJ_T_DATA},
164
+ .buffer = buffer,
165
+ .fd = fd,
166
+ };
167
+ return (FIOBJ)io;
168
+ }
169
+
170
+ static void fiobj_data_dealloc(FIOBJ o, void (*task)(FIOBJ, void *),
171
+ void *arg) {
172
+ switch (obj2io(o)->fd) {
173
+ case -1:
174
+ if (obj2io(o)->source.dealloc && obj2io(o)->buffer)
175
+ obj2io(o)->source.dealloc(obj2io(o)->buffer);
176
+ break;
177
+ case -2:
178
+ fiobj_free(obj2io(o)->source.parent);
179
+ break;
180
+ default:
181
+ close(obj2io(o)->fd);
182
+ fio_free(obj2io(o)->buffer);
183
+ break;
184
+ }
185
+ fio_free((void *)o);
186
+ (void)task;
187
+ (void)arg;
188
+ }
189
+
190
+ static intptr_t fiobj_data_i(const FIOBJ o) {
191
+ switch (obj2io(o)->fd) {
192
+ case -1:
193
+ case -2:
194
+ return obj2io(o)->len;
195
+ break;
196
+ default:
197
+ return fiobj_data_get_fd_size(o);
198
+ }
199
+ }
200
+
201
+ static size_t fiobj_data_is_true(const FIOBJ o) { return fiobj_data_i(o) > 0; }
202
+
203
+ static fio_str_info_s fio_io2str(const FIOBJ o) {
204
+ switch (obj2io(o)->fd) {
205
+ case -1:
206
+ return (fio_str_info_s){.data = (char *)obj2io(o)->buffer,
207
+ .len = obj2io(o)->len};
208
+ break;
209
+ case -2:
210
+ return fiobj_data_pread(obj2io(o)->source.parent, obj2io(o)->capa,
211
+ obj2io(o)->len);
212
+ break;
213
+ }
214
+ int64_t i = fiobj_data_get_fd_size(o);
215
+ if (i <= 0)
216
+ return (fio_str_info_s){.data = (char *)obj2io(o)->buffer,
217
+ .len = obj2io(o)->len};
218
+ obj2io(o)->len = 0;
219
+ obj2io(o)->pos = 0;
220
+ fiobj_data_pre_write((FIOBJ)o, i + 1);
221
+ if (pread(obj2io(o)->fd, obj2io(o)->buffer, i, 0) != i)
222
+ return (fio_str_info_s){.data = NULL, .len = 0};
223
+ obj2io(o)->buffer[i] = 0;
224
+ return (fio_str_info_s){.data = (char *)obj2io(o)->buffer, .len = i};
225
+ }
226
+
227
+ static size_t fiobj_data_iseq(const FIOBJ self, const FIOBJ other) {
228
+ int64_t len;
229
+ return ((len = fiobj_data_i(self)) == fiobj_data_i(other) &&
230
+ !memcmp(fio_io2str(self).data, fio_io2str(other).data, (size_t)len));
231
+ }
232
+
233
+ uintptr_t fiobject___noop_count(FIOBJ o);
234
+ double fiobject___noop_to_f(FIOBJ o);
235
+
236
+ const fiobj_object_vtable_s FIOBJECT_VTABLE_DATA = {
237
+ .class_name = "IO",
238
+ .dealloc = fiobj_data_dealloc,
239
+ .to_i = fiobj_data_i,
240
+ .to_str = fio_io2str,
241
+ .is_eq = fiobj_data_iseq,
242
+ .is_true = fiobj_data_is_true,
243
+ .to_f = fiobject___noop_to_f,
244
+ .count = fiobject___noop_count,
245
+ };
246
+
247
+ /* *****************************************************************************
248
+ Seeking for characters in a string
249
+ ***************************************************************************** */
250
+
251
+ #if FIO_MEMCHAR
252
+
253
+ /**
254
+ * This seems to be faster on some systems, especially for smaller distances.
255
+ *
256
+ * On newer systems, `memchr` should be faster.
257
+ */
258
+ static inline int swallow_ch(uint8_t **buffer, register uint8_t *const limit,
259
+ const uint8_t c) {
260
+ if (**buffer == c)
261
+ return 1;
262
+
263
+ #if !ALLOW_UNALIGNED_MEMORY_ACCESS || !defined(__x86_64__)
264
+ /* too short for this mess */
265
+ if ((uintptr_t)limit <= 16 + ((uintptr_t)*buffer & (~(uintptr_t)7)))
266
+ goto finish;
267
+
268
+ /* align memory */
269
+ {
270
+ const uint8_t *alignment =
271
+ (uint8_t *)(((uintptr_t)(*buffer) & (~(uintptr_t)7)) + 8);
272
+ if (limit >= alignment) {
273
+ while (*buffer < alignment) {
274
+ if (**buffer == c) {
275
+ (*buffer)++;
276
+ return 1;
277
+ }
278
+ *buffer += 1;
279
+ }
280
+ }
281
+ }
282
+ const uint8_t *limit64 = (uint8_t *)((uintptr_t)limit & (~(uintptr_t)7));
283
+ #else
284
+ const uint8_t *limit64 = (uint8_t *)limit - 7;
285
+ #endif
286
+ uint64_t wanted1 = 0x0101010101010101ULL * c;
287
+ for (; *buffer < limit64; *buffer += 8) {
288
+ const uint64_t eq1 = ~((*((uint64_t *)*buffer)) ^ wanted1);
289
+ const uint64_t t0 = (eq1 & 0x7f7f7f7f7f7f7f7fllu) + 0x0101010101010101llu;
290
+ const uint64_t t1 = (eq1 & 0x8080808080808080llu);
291
+ if ((t0 & t1)) {
292
+ break;
293
+ }
294
+ }
295
+ #if !ALLOW_UNALIGNED_MEMORY_ACCESS || !defined(__x86_64__)
296
+ finish:
297
+ #endif
298
+ while (*buffer < limit) {
299
+ if (**buffer == c) {
300
+ (*buffer)++;
301
+ return 1;
302
+ }
303
+ (*buffer)++;
304
+ }
305
+
306
+ return 0;
307
+ }
308
+ #else
309
+
310
+ static inline int swallow_ch(uint8_t **buffer, uint8_t *const limit,
311
+ const uint8_t c) {
312
+ if (limit - *buffer == 0)
313
+ return 0;
314
+ void *tmp = memchr(*buffer, c, limit - (*buffer));
315
+ if (tmp) {
316
+ *buffer = tmp;
317
+ (*buffer)++;
318
+ return 1;
319
+ }
320
+ *buffer = (uint8_t *)limit;
321
+ return 0;
322
+ }
323
+
324
+ #endif
325
+
326
+ /* *****************************************************************************
327
+ Creating the IO object
328
+ ***************************************************************************** */
329
+
330
+ /** Creates a new local in-memory IO object */
331
+ FIOBJ fiobj_data_newstr(void) {
332
+ FIOBJ o = fiobj_data_alloc(fio_malloc(4096), -1);
333
+ REQUIRE_MEM(obj2io(o)->buffer);
334
+ obj2io(o)->capa = 4096;
335
+ obj2io(o)->source.dealloc = fio_free;
336
+ return o;
337
+ }
338
+
339
+ /**
340
+ * Creates a IO object from an existing buffer. The buffer will be deallocated
341
+ * using the provided `dealloc` function pointer. Use a NULL `dealloc` function
342
+ * pointer if the buffer is static and shouldn't be freed.
343
+ */
344
+ FIOBJ fiobj_data_newstr2(void *buffer, uintptr_t length,
345
+ void (*dealloc)(void *)) {
346
+ FIOBJ o = fiobj_data_alloc(buffer, -1);
347
+ obj2io(o)->capa = length;
348
+ obj2io(o)->len = length;
349
+ obj2io(o)->source.dealloc = dealloc;
350
+ return o;
351
+ }
352
+
353
+ /** Creates a new local file IO object */
354
+ FIOBJ fiobj_data_newfd(int fd) {
355
+ FIOBJ o = fiobj_data_alloc(fio_malloc(4096), fd);
356
+ REQUIRE_MEM(obj2io(o)->buffer);
357
+ obj2io(o)->source.fpos = 0;
358
+ return o;
359
+ }
360
+
361
+ /** Creates a new local tempfile IO object */
362
+ FIOBJ fiobj_data_newtmpfile(void) {
363
+ // create a temporary file to contain the data.
364
+ int fd = fio_tmpfile();
365
+ if (fd == -1)
366
+ return 0;
367
+ return fiobj_data_newfd(fd);
368
+ }
369
+
370
+ /** Creates a slice from an existing Data object. */
371
+ FIOBJ fiobj_data_slice(FIOBJ parent, intptr_t offset, uintptr_t length) {
372
+ /* cut from the end */
373
+ if (offset < 0) {
374
+ size_t parent_len = fiobj_data_len(parent);
375
+ offset = parent_len + 1 + offset;
376
+ }
377
+ if (offset < 0)
378
+ offset = 0;
379
+ while (obj2io(parent)->fd == -2) {
380
+ /* don't slice a slice... climb the parent chain. */
381
+ offset += obj2io(parent)->capa;
382
+ parent = obj2io(parent)->source.parent;
383
+ }
384
+ size_t parent_len = fiobj_data_len(parent);
385
+ if (parent_len <= (size_t)offset) {
386
+ length = 0;
387
+ offset = parent_len;
388
+ } else if (parent_len < offset + length) {
389
+ length = parent_len - offset;
390
+ }
391
+ /* make the object */
392
+ FIOBJ o = fiobj_data_alloc(NULL, -2);
393
+ obj2io(o)->capa = offset;
394
+ obj2io(o)->len = length;
395
+ obj2io(o)->source.parent = fiobj_dup(parent);
396
+ return o;
397
+ }
398
+
399
+ /* *****************************************************************************
400
+ Saving the IO object
401
+ ***************************************************************************** */
402
+
403
+ static int fiobj_data_save_str(FIOBJ o, const char *filename) {
404
+ int target = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0777);
405
+ if (target == -1)
406
+ return -1;
407
+ errno = 0;
408
+ size_t total = 0;
409
+ do {
410
+ ssize_t act =
411
+ write(target, obj2io(o)->buffer + total, obj2io(o)->len - total);
412
+ if (act < 0)
413
+ goto error;
414
+ total += act;
415
+ } while (total < obj2io(o)->len);
416
+ close(target);
417
+ return 0;
418
+ error:
419
+ close(target);
420
+ unlink(filename);
421
+ return -1;
422
+ }
423
+
424
+ static int fiobj_data_save_file(FIOBJ o, const char *filename) {
425
+ int target = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0777);
426
+ if (target == -1)
427
+ return -1;
428
+ errno = 0;
429
+ char buf[1024];
430
+ size_t total = 0;
431
+ do {
432
+ ssize_t act = pread(obj2io(o)->fd, buf, 1024, total);
433
+ if (act == 0)
434
+ break;
435
+ if (act < 0)
436
+ goto error;
437
+ ssize_t act2 = write(target, buf, act);
438
+ if (act2 < act)
439
+ goto error;
440
+ total += act2;
441
+ } while (1);
442
+ close(target);
443
+ return 0;
444
+ error:
445
+ close(target);
446
+ unlink(filename);
447
+ return -1;
448
+ }
449
+
450
+ static int fiobj_data_save_slice(FIOBJ o, const char *filename) {
451
+ int target = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0777);
452
+ if (target == -1)
453
+ return -1;
454
+ errno = 0;
455
+ fio_str_info_s tmp;
456
+ size_t total = 0;
457
+ do {
458
+ tmp = fiobj_data_pread(obj2io(o)->source.parent, obj2io(o)->capa + total,
459
+ 4096);
460
+ if (tmp.len == 0)
461
+ break;
462
+ if (total + tmp.len > obj2io(o)->len)
463
+ tmp.len = obj2io(o)->len - total;
464
+ if (tmp.len) {
465
+ ssize_t act2 = write(target, tmp.data, tmp.len);
466
+ if (act2 < 0 || (size_t)act2 < tmp.len)
467
+ goto error;
468
+ total += act2;
469
+ }
470
+ } while (tmp.len == 4096);
471
+ close(target);
472
+ return 0;
473
+ error:
474
+ close(target);
475
+ unlink(filename);
476
+ return -1;
477
+ }
478
+
479
+ /** Creates a new local file IO object */
480
+ int fiobj_data_save(FIOBJ o, const char *filename) {
481
+ switch (obj2io(o)->fd) {
482
+ case -1:
483
+ return fiobj_data_save_str(o, filename);
484
+ break;
485
+ case -2:
486
+ return fiobj_data_save_slice(o, filename);
487
+ break;
488
+ default:
489
+ return fiobj_data_save_file(o, filename);
490
+ }
491
+ }
492
+
493
+ /* *****************************************************************************
494
+ Reading API
495
+ ***************************************************************************** */
496
+
497
+ /** Reads up to `length` bytes */
498
+ static fio_str_info_s fiobj_data_read_str(FIOBJ io, intptr_t length) {
499
+ if (obj2io(io)->pos == obj2io(io)->len) {
500
+ /* EOF */
501
+ return (fio_str_info_s){.data = NULL, .len = 0};
502
+ }
503
+
504
+ if (length <= 0) {
505
+ /* read to EOF - length */
506
+ length = (obj2io(io)->len - obj2io(io)->pos) + length;
507
+ }
508
+
509
+ if (length <= 0) {
510
+ /* We are at EOF - length or beyond */
511
+ return (fio_str_info_s){.data = NULL, .len = 0};
512
+ }
513
+
514
+ /* reading length bytes */
515
+ register size_t pos = obj2io(io)->pos;
516
+ obj2io(io)->pos = pos + length;
517
+ if (obj2io(io)->pos > obj2io(io)->len)
518
+ obj2io(io)->pos = obj2io(io)->len;
519
+ return (fio_str_info_s){
520
+ .data = (char *)(obj2io(io)->buffer + pos),
521
+ .len = (obj2io(io)->pos - pos),
522
+ };
523
+ }
524
+
525
+ /** Reads up to `length` bytes */
526
+ static fio_str_info_s fiobj_data_read_slice(FIOBJ io, intptr_t length) {
527
+ if (obj2io(io)->pos == obj2io(io)->len) {
528
+ /* EOF */
529
+ return (fio_str_info_s){.data = NULL, .len = 0};
530
+ }
531
+ if (length <= 0) {
532
+ /* read to EOF - length */
533
+ length = (obj2io(io)->len - obj2io(io)->pos) + length;
534
+ }
535
+
536
+ if (length <= 0) {
537
+ /* We are at EOF - length or beyond */
538
+ return (fio_str_info_s){.data = NULL, .len = 0};
539
+ }
540
+ register size_t pos = obj2io(io)->pos;
541
+ obj2io(io)->pos = pos + length;
542
+ if (obj2io(io)->pos > obj2io(io)->len)
543
+ obj2io(io)->pos = obj2io(io)->len;
544
+ return fiobj_data_pread(obj2io(io)->source.parent, pos + obj2io(io)->capa,
545
+ (obj2io(io)->pos - pos));
546
+ }
547
+
548
+ /** Reads up to `length` bytes */
549
+ static fio_str_info_s fiobj_data_read_file(FIOBJ io, intptr_t length) {
550
+ uintptr_t fsize = fiobj_data_get_fd_size(io);
551
+
552
+ if (length <= 0) {
553
+ /* read to EOF - length */
554
+ length = (fsize - obj2io(io)->source.fpos) + length;
555
+ }
556
+
557
+ if (length <= 0) {
558
+ /* We are at EOF - length or beyond */
559
+ errno = 0;
560
+ return (fio_str_info_s){.data = NULL, .len = 0};
561
+ }
562
+
563
+ /* reading length bytes */
564
+ if (length + obj2io(io)->pos <= obj2io(io)->len) {
565
+ /* the data already exists in the buffer */
566
+ // fprintf(stderr, "in_buffer...\n");
567
+ fio_str_info_s data = {.data =
568
+ (char *)(obj2io(io)->buffer + obj2io(io)->pos),
569
+ .len = (uintptr_t)length};
570
+ obj2io(io)->pos += length;
571
+ obj2io(io)->source.fpos += length;
572
+ return data;
573
+ } else {
574
+ /* read the data into the buffer - internal counting gets invalidated */
575
+ // fprintf(stderr, "populate buffer...\n");
576
+ obj2io(io)->len = 0;
577
+ obj2io(io)->pos = 0;
578
+ fiobj_data_pre_write(io, length);
579
+ ssize_t l;
580
+ retry_int:
581
+ l = pread(obj2io(io)->fd, obj2io(io)->buffer, length,
582
+ obj2io(io)->source.fpos);
583
+ if (l == -1 && errno == EINTR)
584
+ goto retry_int;
585
+ if (l == -1 || l == 0)
586
+ return (fio_str_info_s){.data = NULL, .len = 0};
587
+ obj2io(io)->source.fpos += l;
588
+ return (fio_str_info_s){.data = (char *)obj2io(io)->buffer, .len = l};
589
+ }
590
+ }
591
+
592
+ /**
593
+ * Reads up to `length` bytes and returns a temporary(!) C string object.
594
+ *
595
+ * The C string object will be invalidate the next time a function call to the
596
+ * IO object is made.
597
+ */
598
+ fio_str_info_s fiobj_data_read(FIOBJ io, intptr_t length) {
599
+ if (!io || !FIOBJ_TYPE_IS(io, FIOBJ_T_DATA)) {
600
+ errno = EFAULT;
601
+ return (fio_str_info_s){.data = NULL, .len = 0};
602
+ }
603
+ errno = 0;
604
+ switch (obj2io(io)->fd) {
605
+ case -1:
606
+ return fiobj_data_read_str(io, length);
607
+ break;
608
+ case -2:
609
+ return fiobj_data_read_slice(io, length);
610
+ break;
611
+ default:
612
+ return fiobj_data_read_file(io, length);
613
+ }
614
+ }
615
+
616
+ /* *****************************************************************************
617
+ Tokenize (read2ch)
618
+ ***************************************************************************** */
619
+
620
+ static fio_str_info_s fiobj_data_read2ch_str(FIOBJ io, uint8_t token) {
621
+ if (obj2io(io)->pos == obj2io(io)->len) /* EOF */
622
+ return (fio_str_info_s){.data = NULL, .len = 0};
623
+
624
+ uint8_t *pos = obj2io(io)->buffer + obj2io(io)->pos;
625
+ uint8_t *lim = obj2io(io)->buffer + obj2io(io)->len;
626
+ swallow_ch(&pos, lim, token);
627
+ fio_str_info_s ret = (fio_str_info_s){
628
+ .data = (char *)obj2io(io)->buffer + obj2io(io)->pos,
629
+ .len = (uintptr_t)(pos - obj2io(io)->buffer) - obj2io(io)->pos,
630
+ };
631
+ obj2io(io)->pos = (uintptr_t)(pos - obj2io(io)->buffer);
632
+ return ret;
633
+ }
634
+
635
+ static fio_str_info_s fiobj_data_read2ch_slice(FIOBJ io, uint8_t token) {
636
+ if (obj2io(io)->pos == obj2io(io)->len) /* EOF */
637
+ return (fio_str_info_s){.data = NULL, .len = 0};
638
+ size_t old_pos = obj2io(obj2io(io)->source.parent)->pos;
639
+ obj2io(obj2io(io)->source.parent)->pos = obj2io(io)->capa + obj2io(io)->pos;
640
+ fio_str_info_s tmp = fiobj_data_read2ch(obj2io(io)->source.parent, token);
641
+ obj2io(obj2io(io)->source.parent)->pos = old_pos;
642
+ if (tmp.len + obj2io(io)->pos > obj2io(io)->len) {
643
+ /* EOF */
644
+ tmp.len = obj2io(io)->len - obj2io(io)->pos;
645
+ obj2io(io)->pos = obj2io(io)->len;
646
+ return tmp;
647
+ }
648
+ return tmp;
649
+ }
650
+
651
+ static fio_str_info_s fiobj_data_read2ch_file(FIOBJ io, uint8_t token) {
652
+ uint8_t *pos = obj2io(io)->buffer + obj2io(io)->pos;
653
+ uint8_t *lim = obj2io(io)->buffer + obj2io(io)->len;
654
+ if (pos != lim && swallow_ch(&pos, lim, token)) {
655
+ /* newline found in existing buffer */
656
+ const uintptr_t delta =
657
+ (uintptr_t)(pos - (obj2io(io)->buffer + obj2io(io)->pos));
658
+ obj2io(io)->pos += delta;
659
+ obj2io(io)->source.fpos += delta;
660
+ return (fio_str_info_s){
661
+ .data =
662
+ (char *)(delta ? ((obj2io(io)->buffer + obj2io(io)->pos) - delta)
663
+ : NULL),
664
+ .len = delta,
665
+ };
666
+ }
667
+
668
+ obj2io(io)->pos = 0;
669
+ obj2io(io)->len = 0;
670
+
671
+ while (1) {
672
+ ssize_t tmp;
673
+ fiobj_data_pre_write(io, 4096); /* read a page at a time */
674
+ retry_int:
675
+ tmp = pread(obj2io(io)->fd, obj2io(io)->buffer + obj2io(io)->len, 4096,
676
+ obj2io(io)->source.fpos + obj2io(io)->len);
677
+ if (tmp < 0 && errno == EINTR)
678
+ goto retry_int;
679
+ if (tmp < 0 || (tmp == 0 && obj2io(io)->len == 0)) {
680
+ return (fio_str_info_s){.data = NULL, .len = 0};
681
+ }
682
+ if (tmp == 0) {
683
+ obj2io(io)->source.fpos += obj2io(io)->len;
684
+ return (fio_str_info_s){.data = (char *)obj2io(io)->buffer,
685
+ .len = obj2io(io)->len};
686
+ }
687
+ obj2io(io)->len += tmp;
688
+ pos = obj2io(io)->buffer;
689
+ lim = obj2io(io)->buffer + obj2io(io)->len;
690
+ if (swallow_ch(&pos, lim, token)) {
691
+ const uintptr_t delta =
692
+ (uintptr_t)(pos - (obj2io(io)->buffer + obj2io(io)->pos));
693
+ obj2io(io)->pos = delta;
694
+ obj2io(io)->source.fpos += delta;
695
+ return (fio_str_info_s){
696
+ .data = (char *)obj2io(io)->buffer,
697
+ .len = delta,
698
+ };
699
+ }
700
+ }
701
+ }
702
+
703
+ /**
704
+ * Reads until the `token` byte is encountered or until the end of the stream.
705
+ *
706
+ * Returns a temporary(!) C string including the end of line marker.
707
+ *
708
+ * Careful when using this call on large file streams, as the whole file
709
+ * stream might be loaded into the memory.
710
+ *
711
+ * The C string object will be invalidate the next time a function call to the
712
+ * IO object is made.
713
+ */
714
+ fio_str_info_s fiobj_data_read2ch(FIOBJ io, uint8_t token) {
715
+ if (!io || !FIOBJ_TYPE_IS(io, FIOBJ_T_DATA)) {
716
+ errno = EFAULT;
717
+ return (fio_str_info_s){.data = NULL, .len = 0};
718
+ }
719
+ switch (obj2io(io)->fd) {
720
+ case -1:
721
+ return fiobj_data_read2ch_str(io, token);
722
+ break;
723
+ case -2:
724
+ return fiobj_data_read2ch_slice(io, token);
725
+ break;
726
+ default:
727
+ return fiobj_data_read2ch_file(io, token);
728
+ }
729
+ }
730
+
731
+ /* *****************************************************************************
732
+ Position / Seeking
733
+ ***************************************************************************** */
734
+
735
+ /**
736
+ * Returns the current reading position.
737
+ */
738
+ intptr_t fiobj_data_pos(FIOBJ io) {
739
+ if (!io || !FIOBJ_TYPE_IS(io, FIOBJ_T_DATA))
740
+ return -1;
741
+ switch (obj2io(io)->fd) {
742
+ case -1: /* fallthrough */
743
+ case -2:
744
+ return obj2io(io)->pos;
745
+ break;
746
+ default:
747
+ return obj2io(io)->source.fpos;
748
+ }
749
+ }
750
+
751
+ /**
752
+ * Returns the length of the stream.
753
+ */
754
+ intptr_t fiobj_data_len(FIOBJ io) {
755
+ if (!io || !FIOBJ_TYPE_IS(io, FIOBJ_T_DATA))
756
+ return -1;
757
+ return fiobj_data_i(io);
758
+ }
759
+
760
+ /**
761
+ * Moves the reading position to the requested position.
762
+ */
763
+ void fiobj_data_seek(FIOBJ io, intptr_t position) {
764
+ if (!io || !FIOBJ_TYPE_IS(io, FIOBJ_T_DATA))
765
+ return;
766
+ switch (obj2io(io)->fd) {
767
+ case -1: /* fallthrough */
768
+ case -2:
769
+ /* String / Slice code */
770
+ if (position == 0) {
771
+ obj2io(io)->pos = 0;
772
+ return;
773
+ }
774
+ if (position > 0) {
775
+ if ((uintptr_t)position > obj2io(io)->len)
776
+ position = obj2io(io)->len;
777
+ obj2io(io)->pos = position;
778
+ return;
779
+ }
780
+ position = (0 - position);
781
+ if ((uintptr_t)position > obj2io(io)->len)
782
+ position = 0;
783
+ else
784
+ position = obj2io(io)->len - position;
785
+ obj2io(io)->pos = position;
786
+ return;
787
+ break;
788
+ default:
789
+ /* File code */
790
+ obj2io(io)->pos = 0;
791
+ obj2io(io)->len = 0;
792
+
793
+ if (position == 0) {
794
+ obj2io(io)->source.fpos = 0;
795
+ return;
796
+ }
797
+ int64_t len = fiobj_data_get_fd_size(io);
798
+ if (len < 0)
799
+ len = 0;
800
+ if (position > 0) {
801
+ if (position > len)
802
+ position = len;
803
+
804
+ obj2io(io)->source.fpos = position;
805
+ return;
806
+ }
807
+ position = (0 - position);
808
+ if (position > len)
809
+ position = 0;
810
+ else
811
+ position = len - position;
812
+ obj2io(io)->source.fpos = position;
813
+ return;
814
+ }
815
+ }
816
+
817
+ /* *****************************************************************************
818
+ `fiobj_data_pread`
819
+ ***************************************************************************** */
820
+ // switch(obj2io(o)->fd) {
821
+ // case -1:
822
+ // break;
823
+ // case -2:
824
+ // break;
825
+ // default:
826
+ // }
827
+
828
+ static fio_str_info_s fiobj_data_pread_str(FIOBJ io, intptr_t start_at,
829
+ uintptr_t length) {
830
+ if (start_at < 0)
831
+ start_at = obj2io(io)->len + start_at;
832
+ if (start_at < 0)
833
+ start_at = 0;
834
+ if ((size_t)start_at > obj2io(io)->len)
835
+ start_at = obj2io(io)->len;
836
+ if (length + start_at > obj2io(io)->len)
837
+ length = obj2io(io)->len - start_at;
838
+ if (length == 0)
839
+ return (fio_str_info_s){
840
+ .data = NULL,
841
+ .len = 0,
842
+ };
843
+ return (fio_str_info_s){
844
+ .data = (char *)obj2io(io)->buffer + start_at,
845
+ .len = length,
846
+ };
847
+ }
848
+ static fio_str_info_s fiobj_data_pread_slice(FIOBJ io, intptr_t start_at,
849
+ uintptr_t length) {
850
+ if (start_at < 0)
851
+ start_at = obj2io(io)->len + start_at;
852
+ if (start_at < 0)
853
+ start_at = 0;
854
+ if ((size_t)start_at > obj2io(io)->len)
855
+ start_at = obj2io(io)->len;
856
+ if (length + start_at > obj2io(io)->len)
857
+ length = obj2io(io)->len - start_at;
858
+ if (length == 0)
859
+ return (fio_str_info_s){
860
+ .data = NULL,
861
+ .len = 0,
862
+ };
863
+ return fiobj_data_pread(obj2io(io)->source.parent, start_at, length);
864
+ }
865
+
866
+ static fio_str_info_s fiobj_data_pread_file(FIOBJ io, intptr_t start_at,
867
+ uintptr_t length) {
868
+ const int64_t size = fiobj_data_get_fd_size(io);
869
+ if (start_at < 0)
870
+ start_at = size + start_at;
871
+ if (start_at < 0)
872
+ start_at = 0;
873
+ if (length + start_at > (uint64_t)size)
874
+ length = size - start_at;
875
+ if (length == 0) {
876
+ /* free memory once there's no more data to read */
877
+ obj2io(io)->capa = 0;
878
+ fio_free(obj2io(io)->buffer);
879
+ obj2io(io)->buffer = NULL;
880
+ return (fio_str_info_s){
881
+ .data = NULL,
882
+ .len = 0,
883
+ };
884
+ }
885
+ obj2io(io)->len = 0;
886
+ obj2io(io)->pos = 0;
887
+ fiobj_data_pre_write(io, length + 1);
888
+ ssize_t tmp = pread(obj2io(io)->fd, obj2io(io)->buffer, length, start_at);
889
+ if (tmp <= 0) {
890
+ return (fio_str_info_s){
891
+ .data = NULL,
892
+ .len = 0,
893
+ };
894
+ }
895
+ obj2io(io)->buffer[tmp] = 0;
896
+ return (fio_str_info_s){
897
+ .data = (char *)obj2io(io)->buffer,
898
+ .len = tmp,
899
+ };
900
+ }
901
+ /**
902
+ * Reads up to `length` bytes starting at `start_at` position and returns a
903
+ * temporary(!) C string object. The reading position is ignored and
904
+ * unchanged.
905
+ *
906
+ * The C string object will be invalidate the next time a function call to the
907
+ * IO object is made.
908
+ */
909
+ fio_str_info_s fiobj_data_pread(FIOBJ io, intptr_t start_at, uintptr_t length) {
910
+ if (!io || !FIOBJ_TYPE_IS(io, FIOBJ_T_DATA)) {
911
+ errno = EFAULT;
912
+ return (fio_str_info_s){
913
+ .data = NULL,
914
+ .len = 0,
915
+ };
916
+ }
917
+
918
+ errno = 0;
919
+ switch (obj2io(io)->fd) {
920
+ case -1:
921
+ return fiobj_data_pread_str(io, start_at, length);
922
+ break;
923
+ case -2:
924
+ return fiobj_data_pread_slice(io, start_at, length);
925
+ break;
926
+ default:
927
+ return fiobj_data_pread_file(io, start_at, length);
928
+ }
929
+ }
930
+
931
+ /* *****************************************************************************
932
+ Writing API
933
+ ***************************************************************************** */
934
+
935
+ /**
936
+ * Makes sure the IO object isn't attached to a static or external string.
937
+ *
938
+ * If the IO object is attached to a static or external string, the data will be
939
+ * copied to a new memory block.
940
+ */
941
+ void fiobj_data_assert_dynamic(FIOBJ io) {
942
+ if (!io) {
943
+ errno = ENFILE;
944
+ return;
945
+ }
946
+ assert(FIOBJ_TYPE(io) == FIOBJ_T_DATA);
947
+ fiobj_data_pre_write(io, 0);
948
+ return;
949
+ }
950
+
951
+ /**
952
+ * Writes `length` bytes at the end of the IO stream, ignoring the reading
953
+ * position.
954
+ *
955
+ * Behaves and returns the same value as the system call `write`.
956
+ */
957
+ intptr_t fiobj_data_write(FIOBJ io, void *buffer, uintptr_t length) {
958
+ if (!io || !FIOBJ_TYPE_IS(io, FIOBJ_T_DATA) || (!buffer && length)) {
959
+ errno = EFAULT;
960
+ return -1;
961
+ }
962
+ errno = 0;
963
+ /* Unslice slices */
964
+ if (obj2io(io)->fd == -2)
965
+ fiobj_data_assert_dynamic(io);
966
+ if (obj2io(io)->fd == -1) {
967
+ /* String Code */
968
+ fiobj_data_pre_write(io, length + 1);
969
+ memcpy(obj2io(io)->buffer + obj2io(io)->len, buffer, length);
970
+ obj2io(io)->len = obj2io(io)->len + length;
971
+ obj2io(io)->buffer[obj2io(io)->len] = 0;
972
+ return length;
973
+ }
974
+ /* File Code */
975
+ return pwrite(obj2io(io)->fd, buffer, length, fiobj_data_get_fd_size(io));
976
+ }
977
+
978
+ /**
979
+ * Writes `length` bytes at the end of the IO stream, ignoring the reading
980
+ * position, adding an EOL marker ("\r\n") to the end of the stream.
981
+ *
982
+ * Behaves and returns the same value as the system call `write`.
983
+ */
984
+ intptr_t fiobj_data_puts(FIOBJ io, void *buffer, uintptr_t length) {
985
+ if (!io || !FIOBJ_TYPE_IS(io, FIOBJ_T_DATA) || (!buffer && length)) {
986
+ errno = EFAULT;
987
+ return -1;
988
+ }
989
+ /* Unslice slices */
990
+ if (obj2io(io)->fd == -2)
991
+ fiobj_data_assert_dynamic(io);
992
+
993
+ if (obj2io(io)->fd == -1) {
994
+ /* String Code */
995
+ fiobj_data_pre_write(io, length + 2);
996
+ if (length) {
997
+ memcpy(obj2io(io)->buffer + obj2io(io)->len, buffer, length);
998
+ }
999
+ obj2io(io)->len = obj2io(io)->len + length + 2;
1000
+ obj2io(io)->buffer[obj2io(io)->len - 2] = '\r';
1001
+ obj2io(io)->buffer[obj2io(io)->len - 1] = '\n';
1002
+ return length + 2;
1003
+ }
1004
+ /* File Code */
1005
+ uintptr_t end = fiobj_data_get_fd_size(io);
1006
+ ssize_t t1 = 0, t2 = 0;
1007
+
1008
+ if (length) {
1009
+ t1 = pwrite(obj2io(io)->fd, buffer, length, end);
1010
+ if (t1 < 0)
1011
+ return t1;
1012
+ end += t1;
1013
+ }
1014
+ t2 = pwrite(obj2io(io)->fd, buffer, length, end);
1015
+ if (t2 < 0)
1016
+ return t1;
1017
+ return t1 + t2;
1018
+ }
1019
+
1020
+ #if DEBUG
1021
+
1022
+ void fiobj_data_test(void) {
1023
+ char *filename = NULL;
1024
+ FIOBJ text;
1025
+ fio_str_info_s s1, s2;
1026
+ fprintf(stderr, "=== testing fiobj_data\n");
1027
+ if (filename) {
1028
+ text = fiobj_str_buf(0);
1029
+ fiobj_str_readfile(text, filename, 0, 0);
1030
+ } else
1031
+ text = fiobj_str_new("Line 1\r\nLine 2\nLine 3 unended", 29);
1032
+ FIOBJ strio = fiobj_data_newstr();
1033
+ fprintf(stderr, "* `newstr` passed.\n");
1034
+ FIOBJ fdio = fiobj_data_newtmpfile();
1035
+ fprintf(stderr, "* `newtmpfile` passed.\n");
1036
+ fiobj_data_write(fdio, fiobj_obj2cstr(text).data, fiobj_obj2cstr(text).len);
1037
+ fiobj_data_write(strio, fiobj_obj2cstr(text).data, fiobj_obj2cstr(text).len);
1038
+ FIOBJ sliceio = fiobj_data_slice(fdio, 8, 7);
1039
+
1040
+ s1 = fiobj_data_read(sliceio, 4096);
1041
+ if (s1.len != 7 || memcmp(s1.data, fiobj_data_pread(strio, 8, 7).data, 7)) {
1042
+ fprintf(stderr, "* `fiobj_data_slice` operation FAILED!\n");
1043
+ fprintf(stderr, "* `fiobj_data_slice` s1.len = %zu s1.data = %s!\n", s1.len,
1044
+ s1.data);
1045
+ exit(-1);
1046
+ }
1047
+ s1 = fiobj_data_read(sliceio, 4096);
1048
+ if (s1.len || s1.data) {
1049
+ fprintf(stderr, "* `fiobj_data_read` operation overflow - FAILED!\n");
1050
+ exit(-1);
1051
+ }
1052
+ if (fiobj_obj2cstr(strio).len != fiobj_obj2cstr(text).len ||
1053
+ fiobj_obj2cstr(fdio).len != fiobj_obj2cstr(text).len) {
1054
+ fprintf(stderr, "* `write` operation FAILED!\n");
1055
+ exit(-1);
1056
+ }
1057
+ s1 = fiobj_data_gets(strio);
1058
+ s2 = fiobj_data_gets(fdio);
1059
+ fprintf(stderr, "str(%d): %.*s", (int)s1.len, (int)s1.len, s1.data);
1060
+ fprintf(stderr, "fd(%d): %.*s", (int)s2.len, (int)s2.len, s2.data);
1061
+ if (s1.len != s2.len || memcmp(s1.data, s2.data, s1.len)) {
1062
+ fprintf(stderr,
1063
+ "* `gets` operation FAILED! (non equal data):\n"
1064
+ "%d bytes vs. %d bytes\n"
1065
+ "%.*s vs %.*s\n",
1066
+ (int)s1.len, (int)s2.len, (int)s1.len, s1.data, (int)s2.len,
1067
+ s2.data);
1068
+ exit(-1);
1069
+ } else
1070
+ fprintf(stderr, "* `gets` operation passed (equal data).\n");
1071
+
1072
+ if (!filename) {
1073
+ intptr_t last_pos = fiobj_data_pos(fdio);
1074
+ fiobj_data_seek(sliceio, 0);
1075
+ s1 = fiobj_data_gets(sliceio);
1076
+ s2 = fiobj_data_gets(fdio);
1077
+ fiobj_data_seek(fdio, last_pos);
1078
+ if (s1.len != s2.len || memcmp(s1.data, s2.data, s1.len)) {
1079
+ fprintf(stderr,
1080
+ "* slice `gets` operation FAILED! (non equal data):\n"
1081
+ "%d bytes vs. %d bytes\n"
1082
+ "%.*s vs %.*s\n",
1083
+ (int)s1.len, (int)s2.len, (int)s1.len, s1.data, (int)s2.len,
1084
+ s2.data);
1085
+ exit(-1);
1086
+ }
1087
+ }
1088
+
1089
+ s1 = fiobj_data_read(strio, 3);
1090
+ s2 = fiobj_data_read(fdio, 3);
1091
+ if (s1.len != s2.len || memcmp(s1.data, s2.data, s1.len)) {
1092
+ fprintf(stderr,
1093
+ "* `read` operation FAILED! (non equal data):\n"
1094
+ "%d bytes vs. %d bytes\n"
1095
+ "%.*s vs %.*s\n",
1096
+ (int)s1.len, (int)s2.len, (int)s1.len, s1.data, (int)s2.len,
1097
+ s2.data);
1098
+ exit(-1);
1099
+ } else
1100
+ fprintf(stderr, "* `read` operation passed (equal data).\n");
1101
+ if (!filename) {
1102
+ s1 = fiobj_data_gets(strio);
1103
+ s2 = fiobj_data_gets(fdio);
1104
+ s1 = fiobj_data_gets(strio);
1105
+ s2 = fiobj_data_gets(fdio);
1106
+ if (s1.len != s2.len || memcmp(s1.data, s2.data, s1.len)) {
1107
+ fprintf(stderr,
1108
+ "* EOF `gets` operation FAILED! (non equal data):\n"
1109
+ "%d bytes vs. %d bytes\n"
1110
+ "%.*s vs %.*s\n",
1111
+ (int)s1.len, (int)s2.len, (int)s1.len, s1.data, (int)s2.len,
1112
+ s2.data);
1113
+ exit(-1);
1114
+ } else
1115
+ fprintf(stderr, "* EOF `gets` operation passed (equal data).\n");
1116
+ s1 = fiobj_data_gets(strio);
1117
+ s2 = fiobj_data_gets(fdio);
1118
+ if (s1.data || s2.data) {
1119
+ fprintf(stderr,
1120
+ "* EOF `gets` was not EOF?!\n"
1121
+ "str(%d): %.*s\n"
1122
+ "fd(%d): %.*s\n",
1123
+ (int)s1.len, (int)s1.len, s1.data, (int)s2.len, (int)s2.len,
1124
+ s2.data);
1125
+ exit(-1);
1126
+ }
1127
+ }
1128
+ fiobj_free(text);
1129
+ fiobj_free(strio);
1130
+ fiobj_free(fdio);
1131
+
1132
+ {
1133
+ fiobj_data_seek(sliceio, 0);
1134
+ s1 = fiobj_data_read(sliceio, 4096);
1135
+ if (s1.len != (size_t)fiobj_data_len(sliceio) || !s1.data) {
1136
+ fprintf(stderr, "* `fiobj_data_slice` data lost? FAILED!\n");
1137
+ fprintf(stderr,
1138
+ "* `fiobj_data_slice` s1.len = %zu (out of %zu) s1.data = %s!\n",
1139
+ s1.len, (size_t)fiobj_data_len(sliceio), s1.data);
1140
+ exit(-1);
1141
+ }
1142
+ size_t old_len = fiobj_data_len(sliceio);
1143
+ fiobj_data_write(sliceio, "hi", 2);
1144
+ fiobj_data_seek(sliceio, 0);
1145
+ s1 = fiobj_data_read(sliceio, 4096);
1146
+ if (s1.len != old_len + 2 || !s1.data || s1.data[s1.len - 1] != 'i') {
1147
+ fprintf(stderr, "* `fiobj_data_write` for Slice data lost? FAILED!\n");
1148
+ fprintf(stderr,
1149
+ "* `fiobj_data_slice` s1.len = %zu (out of %zu) s1.data = %s!\n",
1150
+ s1.len, (size_t)fiobj_data_len(sliceio), s1.data);
1151
+ exit(-1);
1152
+ }
1153
+ }
1154
+ fiobj_free(sliceio);
1155
+
1156
+ fprintf(stderr, "* passed.\n");
1157
+ }
1158
+
1159
+ #endif
1160
+
1161
+ #else /* require POSIX */
1162
+ #include <fiobj_data.h>
1163
+
1164
+ /** Creates a new local in-memory IO object */
1165
+ FIOBJ fiobj_data_newstr(void) { return FIOBJ_INVALID; }
1166
+
1167
+ /**
1168
+ * Creates a IO object from an existing buffer. The buffer will be deallocated
1169
+ * using the provided `dealloc` function pointer. Use a NULL `dealloc` function
1170
+ * pointer if the buffer is static and shouldn't be freed.
1171
+ */
1172
+ FIOBJ fiobj_data_newstr2(void *buffer, uintptr_t length,
1173
+ void (*dealloc)(void *)) {
1174
+ return FIOBJ_INVALID;
1175
+ }
1176
+
1177
+ /** Creates a new local tempfile IO object */
1178
+ FIOBJ fiobj_data_newtmpfile(void) { return FIOBJ_INVALID; }
1179
+
1180
+ /** Creates a new local file IO object */
1181
+ FIOBJ fiobj_data_newfd(int fd) { return FIOBJ_INVALID; }
1182
+
1183
+ int fiobj_data_save(FIOBJ io, const char *filename) { return -1; }
1184
+
1185
+ #endif /* require POSIX */