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