isomorfeus-iodine 0.7.45 → 0.7.46

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,433 +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
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