iodine 0.4.8 → 0.4.10

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 (53) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +26 -0
  4. data/README.md +18 -12
  5. data/SPEC-Websocket-Draft.md +9 -5
  6. data/bin/ws-echo +3 -0
  7. data/examples/config.ru +0 -1
  8. data/examples/echo.ru +3 -1
  9. data/examples/redis.ru +0 -1
  10. data/ext/iodine/base64.c +97 -105
  11. data/ext/iodine/defer.c +16 -1
  12. data/ext/iodine/defer.h +10 -0
  13. data/ext/iodine/evio.c +35 -13
  14. data/ext/iodine/extconf.rb +1 -1
  15. data/ext/iodine/facil.c +12 -1
  16. data/ext/iodine/facil.h +3 -1
  17. data/ext/iodine/fio2resp.c +71 -0
  18. data/ext/iodine/fio2resp.h +50 -0
  19. data/ext/iodine/fio_cli_helper.c +404 -0
  20. data/ext/iodine/fio_cli_helper.h +152 -0
  21. data/ext/iodine/fiobj.h +631 -0
  22. data/ext/iodine/fiobj_alloc.c +81 -0
  23. data/ext/iodine/fiobj_ary.c +290 -0
  24. data/ext/iodine/fiobj_generic.c +260 -0
  25. data/ext/iodine/fiobj_hash.c +447 -0
  26. data/ext/iodine/fiobj_io.c +58 -0
  27. data/ext/iodine/fiobj_json.c +779 -0
  28. data/ext/iodine/fiobj_misc.c +213 -0
  29. data/ext/iodine/fiobj_numbers.c +113 -0
  30. data/ext/iodine/fiobj_primitives.c +98 -0
  31. data/ext/iodine/fiobj_str.c +261 -0
  32. data/ext/iodine/fiobj_sym.c +213 -0
  33. data/ext/iodine/fiobj_tests.c +474 -0
  34. data/ext/iodine/fiobj_types.h +290 -0
  35. data/ext/iodine/http1.c +54 -36
  36. data/ext/iodine/http1_parser.c +143 -35
  37. data/ext/iodine/http1_parser.h +6 -3
  38. data/ext/iodine/http1_response.c +0 -1
  39. data/ext/iodine/http_response.c +1 -1
  40. data/ext/iodine/iodine.c +20 -4
  41. data/ext/iodine/iodine_protocol.c +5 -4
  42. data/ext/iodine/iodine_pubsub.c +1 -1
  43. data/ext/iodine/random.c +5 -5
  44. data/ext/iodine/sha1.c +5 -8
  45. data/ext/iodine/sha2.c +8 -11
  46. data/ext/iodine/sha2.h +3 -3
  47. data/ext/iodine/sock.c +29 -31
  48. data/ext/iodine/websocket_parser.h +428 -0
  49. data/ext/iodine/websockets.c +112 -377
  50. data/ext/iodine/xor-crypt.c +16 -12
  51. data/lib/iodine/version.rb +1 -1
  52. metadata +21 -3
  53. data/ext/iodine/empty.h +0 -26
@@ -0,0 +1,213 @@
1
+ /*
2
+ Copyright: Boaz segev, 2017
3
+ License: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+ #include "fiobj_types.h"
8
+
9
+ /* *****************************************************************************
10
+ Number and Float Helpers
11
+ ***************************************************************************** */
12
+ static const char hex_notation[] = {'0', '1', '2', '3', '4', '5', '6', '7',
13
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
14
+
15
+ /**
16
+ * A helper function that converts between String data to a signed int64_t.
17
+ *
18
+ * Numbers are assumed to be in base 10. `0x##` (or `x##`) and `0b##` (or
19
+ * `b##`) are recognized as base 16 and base 2 (binary MSB first)
20
+ * respectively.
21
+ */
22
+ int64_t fio_atol(char **pstr) {
23
+ char *str = *pstr;
24
+ uint64_t result = 0;
25
+ uint8_t invert = 0;
26
+ while (str[0] == '0')
27
+ str++;
28
+ while (str[0] == '-') {
29
+ invert ^= 1;
30
+ str++;
31
+ }
32
+ while (str[0] == '0')
33
+ str++;
34
+ if (str[0] == 'b' || str[0] == 'B') {
35
+ /* base 2 */
36
+ str++;
37
+ while (str[0] == '0' || str[0] == '1') {
38
+ result = (result << 1) | (str[0] == '1');
39
+ str++;
40
+ }
41
+ } else if (str[0] == 'x' || str[0] == 'X') {
42
+ /* base 16 */
43
+ uint8_t tmp;
44
+ str++;
45
+ while (1) {
46
+ if (str[0] >= '0' && str[0] <= '9')
47
+ tmp = str[0] - '0';
48
+ else if (str[0] >= 'A' && str[0] <= 'F')
49
+ tmp = str[0] - ('A' - 10);
50
+ else if (str[0] >= 'a' && str[0] <= 'f')
51
+ tmp = str[0] - ('a' - 10);
52
+ else
53
+ goto finish;
54
+ result = (result << 4) | tmp;
55
+ str++;
56
+ }
57
+ } else {
58
+ /* base 10 */
59
+ const char *end = str;
60
+ while (end[0] >= '0' && end[0] <= '9' && (uintptr_t)(end - str) < 22)
61
+ end++;
62
+ if ((uintptr_t)(end - str) > 21) /* too large for a number */
63
+ return 0;
64
+
65
+ while (str < end) {
66
+ result = (result * 10) + (str[0] - '0');
67
+ str++;
68
+ }
69
+ }
70
+ finish:
71
+ if (invert)
72
+ result = 0 - result;
73
+ *pstr = str;
74
+ return (int64_t)result;
75
+ }
76
+
77
+ /** A helper function that convers between String data to a signed double. */
78
+ double fio_atof(char **pstr) { return strtold(*pstr, pstr); }
79
+
80
+ /* *****************************************************************************
81
+ String Helpers
82
+ ***************************************************************************** */
83
+
84
+ /**
85
+ * A helper function that convers between a signed int64_t to a string.
86
+ *
87
+ * No overflow guard is provided, make sure there's at least 66 bytes
88
+ * available (for base 2).
89
+ *
90
+ * Supports base 2, base 10 and base 16. An unsupported base will silently
91
+ * default to base 10. Prefixes aren't added (i.e., no "0x" or "0b" at the
92
+ * beginning of the string).
93
+ *
94
+ * Returns the number of bytes actually written (excluding the NUL
95
+ * terminator).
96
+ */
97
+ size_t fio_ltoa(char *dest, int64_t num, uint8_t base) {
98
+ if (!num) {
99
+ *(dest++) = '0';
100
+ *(dest++) = 0;
101
+ return 1;
102
+ }
103
+
104
+ size_t len = 0;
105
+
106
+ if (base == 2) {
107
+ uint64_t n = num; /* avoid bit shifting inconsistencies with signed bit */
108
+ uint8_t i = 0; /* counting bits */
109
+
110
+ while ((i < 64) && (n & 0x8000000000000000) == 0) {
111
+ n = n << 1;
112
+ i++;
113
+ }
114
+ /* make sure the Binary representation doesn't appear signed. */
115
+ if (i) {
116
+ dest[len++] = '0';
117
+ }
118
+ /* write to dest. */
119
+ while (i < 64) {
120
+ dest[len++] = ((n & 0x8000000000000000) ? '1' : '0');
121
+ n = n << 1;
122
+ i++;
123
+ }
124
+ dest[len] = 0;
125
+ return len;
126
+
127
+ } else if (base == 16) {
128
+ uint64_t n = num; /* avoid bit shifting inconsistencies with signed bit */
129
+ uint8_t i = 0; /* counting bytes */
130
+ uint8_t tmp = 0;
131
+ while (i < 8 && (n & 0xFF00000000000000) == 0) {
132
+ n = n << 8;
133
+ i++;
134
+ }
135
+ /* make sure the Hex representation doesn't appear signed. */
136
+ if (i && (n & 0x8000000000000000)) {
137
+ dest[len++] = '0';
138
+ dest[len++] = '0';
139
+ }
140
+ /* write the damn thing */
141
+ while (i < 8) {
142
+ tmp = (n & 0xF000000000000000) >> 60;
143
+ dest[len++] = hex_notation[tmp];
144
+ tmp = (n & 0x0F00000000000000) >> 56;
145
+ dest[len++] = hex_notation[tmp];
146
+ i++;
147
+ n = n << 8;
148
+ }
149
+ dest[len] = 0;
150
+ return len;
151
+ }
152
+
153
+ /* fallback to base 10 */
154
+ uint64_t rem = 0;
155
+ uint64_t factor = 1;
156
+ if (num < 0) {
157
+ dest[len++] = '-';
158
+ num = 0 - num;
159
+ }
160
+
161
+ while (num / factor)
162
+ factor *= 10;
163
+
164
+ while (factor > 1) {
165
+ factor = factor / 10;
166
+ rem = (rem * 10);
167
+ dest[len++] = '0' + ((num / factor) - rem);
168
+ rem += ((num / factor) - rem);
169
+ }
170
+ dest[len] = 0;
171
+ return len;
172
+ }
173
+
174
+ /**
175
+ * A helper function that convers between a double to a string.
176
+ *
177
+ * No overflow guard is provided, make sure there's at least 130 bytes
178
+ * available (for base 2).
179
+ *
180
+ * Supports base 2, base 10 and base 16. An unsupported base will silently
181
+ * default to base 10. Prefixes aren't added (i.e., no "0x" or "0b" at the
182
+ * beginning of the string).
183
+ *
184
+ * Returns the number of bytes actually written (excluding the NUL
185
+ * terminator).
186
+ */
187
+ size_t fio_ftoa(char *dest, double num, uint8_t base) {
188
+ if (base == 2 || base == 16) {
189
+ /* handle the binary / Hex representation the same as if it were an
190
+ * int64_t
191
+ */
192
+ int64_t *i = (void *)&num;
193
+ return fio_ltoa(dest, *i, base);
194
+ }
195
+
196
+ size_t written = sprintf(dest, "%g", num);
197
+ uint8_t need_zero = 1;
198
+ char *start = dest;
199
+ while (*start) {
200
+ if (*start == ',') // locale issues?
201
+ *start = '.';
202
+ if (*start == '.' || *start == 'e') {
203
+ need_zero = 0;
204
+ break;
205
+ }
206
+ start++;
207
+ }
208
+ if (need_zero) {
209
+ dest[written++] = '.';
210
+ dest[written++] = '0';
211
+ }
212
+ return written;
213
+ }
@@ -0,0 +1,113 @@
1
+ /*
2
+ Copyright: Boaz segev, 2017
3
+ License: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+
8
+ #include "fiobj_types.h"
9
+
10
+ /* *****************************************************************************
11
+ Numbers VTable
12
+ ***************************************************************************** */
13
+
14
+ static __thread char num_buffer[512];
15
+
16
+ static int64_t fio_i2i(fiobj_s *o) { return obj2num(o)->i; }
17
+ static int64_t fio_f2i(fiobj_s *o) {
18
+ return (int64_t)floorl(((fio_float_s *)o)->f);
19
+ }
20
+ static double fio_i2f(fiobj_s *o) { return (double)obj2num(o)->i; }
21
+ static double fio_f2f(fiobj_s *o) { return obj2float(o)->f; }
22
+
23
+ static fio_cstr_s fio_i2str(fiobj_s *o) {
24
+ return (fio_cstr_s){
25
+ .buffer = num_buffer, .len = fio_ltoa(num_buffer, obj2num(o)->i, 10),
26
+ };
27
+ }
28
+ static fio_cstr_s fio_f2str(fiobj_s *o) {
29
+ if (isnan(obj2float(o)->f))
30
+ return (fio_cstr_s){.buffer = "NaN", .len = 3};
31
+ else if (isinf(obj2float(o)->f)) {
32
+ if (obj2float(o)->f > 0)
33
+ return (fio_cstr_s){.buffer = "Infinity", .len = 8};
34
+ else
35
+ return (fio_cstr_s){.buffer = "-Infinity", .len = 9};
36
+ }
37
+ return (fio_cstr_s){
38
+ .buffer = num_buffer, .len = fio_ftoa(num_buffer, obj2float(o)->f, 10),
39
+ };
40
+ }
41
+
42
+ static int fiobj_i_is_eq(fiobj_s *self, fiobj_s *other) {
43
+ if (!other || other->type != self->type ||
44
+ obj2num(self)->i != obj2num(other)->i)
45
+ return 0;
46
+ return 1;
47
+ }
48
+ static int fiobj_f_is_eq(fiobj_s *self, fiobj_s *other) {
49
+ if (!other || other->type != self->type ||
50
+ obj2float(self)->f != obj2float(other)->f)
51
+ return 0;
52
+ return 1;
53
+ }
54
+
55
+ static struct fiobj_vtable_s FIOBJ_VTABLE_INT = {
56
+ .free = fiobj_simple_dealloc,
57
+ .to_i = fio_i2i,
58
+ .to_f = fio_i2f,
59
+ .to_str = fio_i2str,
60
+ .is_eq = fiobj_i_is_eq,
61
+ .count = fiobj_noop_count,
62
+ .each1 = fiobj_noop_each1,
63
+ };
64
+
65
+ static struct fiobj_vtable_s FIOBJ_VTABLE_FLOAT = {
66
+ .free = fiobj_simple_dealloc,
67
+ .to_i = fio_f2i,
68
+ .to_f = fio_f2f,
69
+ .to_str = fio_f2str,
70
+ .is_eq = fiobj_f_is_eq,
71
+ .count = fiobj_noop_count,
72
+ .each1 = fiobj_noop_each1,
73
+ };
74
+
75
+ /* *****************************************************************************
76
+ Number API
77
+ ***************************************************************************** */
78
+
79
+ /** Creates a Number object. Remember to use `fiobj_free`. */
80
+ fiobj_s *fiobj_num_new(int64_t num) {
81
+ fiobj_head_s *head;
82
+ head = malloc(sizeof(*head) + sizeof(fio_num_s));
83
+ if (!head)
84
+ perror("ERROR: fiobj number couldn't allocate memory"), exit(errno);
85
+ *head = (fiobj_head_s){
86
+ .ref = 1, .vtable = &FIOBJ_VTABLE_INT,
87
+ };
88
+ *obj2num(HEAD2OBJ(head)) = (fio_num_s){.i = num, .type = FIOBJ_T_NUMBER};
89
+ return HEAD2OBJ(head);
90
+ }
91
+
92
+ /** Mutates a Number object's value. Effects every object's reference! */
93
+ void fiobj_num_set(fiobj_s *target, int64_t num) { obj2num(target)->i = num; }
94
+
95
+ /* *****************************************************************************
96
+ Float API
97
+ ***************************************************************************** */
98
+
99
+ /** Creates a Float object. Remember to use `fiobj_free`. */
100
+ fiobj_s *fiobj_float_new(double num) {
101
+ fiobj_head_s *head;
102
+ head = malloc(sizeof(*head) + sizeof(fio_float_s));
103
+ if (!head)
104
+ perror("ERROR: fiobj float couldn't allocate memory"), exit(errno);
105
+ *head = (fiobj_head_s){
106
+ .ref = 1, .vtable = &FIOBJ_VTABLE_FLOAT,
107
+ };
108
+ *obj2float(HEAD2OBJ(head)) = (fio_float_s){.f = num, .type = FIOBJ_T_FLOAT};
109
+ return HEAD2OBJ(head);
110
+ }
111
+
112
+ /** Mutates a Float object's value. Effects every object's reference! */
113
+ void fiobj_float_set(fiobj_s *obj, double num) { obj2float(obj)->f = num; }
@@ -0,0 +1,98 @@
1
+ /*
2
+ Copyright: Boaz segev, 2017
3
+ License: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+ #include "fiobj_types.h"
8
+
9
+ /* *****************************************************************************
10
+ NULL, TRUE, FALSE VTable
11
+ ***************************************************************************** */
12
+
13
+ static int fiobj_simple_is_eq(fiobj_s *self, fiobj_s *other) {
14
+ if (!other || other->type != self->type)
15
+ return 0;
16
+ return 1;
17
+ }
18
+
19
+ static fio_cstr_s fio_true2str(fiobj_s *obj) {
20
+ return (fio_cstr_s){.buffer = "true", .len = 4};
21
+ (void)obj;
22
+ }
23
+ static fio_cstr_s fio_false2str(fiobj_s *obj) {
24
+ return (fio_cstr_s){.buffer = "false", .len = 5};
25
+ (void)obj;
26
+ }
27
+ static fio_cstr_s fio_null2str(fiobj_s *obj) {
28
+ return (fio_cstr_s){.buffer = "null", .len = 4};
29
+ (void)obj;
30
+ }
31
+ static int64_t fio_true2i(fiobj_s *obj) {
32
+ return 1;
33
+ (void)obj;
34
+ }
35
+ static double fio_true2f(fiobj_s *obj) {
36
+ return 1;
37
+ (void)obj;
38
+ }
39
+
40
+ static struct fiobj_vtable_s FIOBJ_VTABLE_NULL = {
41
+ .free = fiobj_simple_dealloc,
42
+ .to_i = fiobj_noop_i,
43
+ .to_f = fiobj_noop_f,
44
+ .to_str = fio_null2str,
45
+ .is_eq = fiobj_simple_is_eq,
46
+ .count = fiobj_noop_count,
47
+ .each1 = fiobj_noop_each1,
48
+ };
49
+ static struct fiobj_vtable_s FIOBJ_VTABLE_TRUE = {
50
+ .free = fiobj_simple_dealloc,
51
+ .to_i = fio_true2i,
52
+ .to_f = fio_true2f,
53
+ .to_str = fio_true2str,
54
+ .is_eq = fiobj_simple_is_eq,
55
+ .count = fiobj_noop_count,
56
+ .each1 = fiobj_noop_each1,
57
+ };
58
+ static struct fiobj_vtable_s FIOBJ_VTABLE_FALSE = {
59
+ .free = fiobj_simple_dealloc,
60
+ .to_i = fiobj_noop_i,
61
+ .to_f = fiobj_noop_f,
62
+ .to_str = fio_false2str,
63
+ .is_eq = fiobj_simple_is_eq,
64
+ .count = fiobj_noop_count,
65
+ .each1 = fiobj_noop_each1,
66
+ };
67
+
68
+ /* *****************************************************************************
69
+ NULL, TRUE, FALSE API
70
+ ***************************************************************************** */
71
+
72
+ inline static fiobj_s *fiobj_simple_alloc(fiobj_type_en t,
73
+ struct fiobj_vtable_s *vt) {
74
+ fiobj_head_s *head;
75
+ head = malloc(sizeof(*head) + sizeof(fiobj_s));
76
+ if (!head)
77
+ perror("ERROR: fiobj primitive couldn't allocate memory"), exit(errno);
78
+ *head = (fiobj_head_s){
79
+ .ref = 1, .vtable = vt,
80
+ };
81
+ HEAD2OBJ(head)->type = t;
82
+ return HEAD2OBJ(head);
83
+ }
84
+
85
+ /** Retruns a NULL object. */
86
+ fiobj_s *fiobj_null(void) {
87
+ return fiobj_simple_alloc(FIOBJ_T_NULL, &FIOBJ_VTABLE_NULL);
88
+ }
89
+
90
+ /** Retruns a FALSE object. */
91
+ fiobj_s *fiobj_false(void) {
92
+ return fiobj_simple_alloc(FIOBJ_T_FALSE, &FIOBJ_VTABLE_FALSE);
93
+ }
94
+
95
+ /** Retruns a TRUE object. */
96
+ fiobj_s *fiobj_true(void) {
97
+ return fiobj_simple_alloc(FIOBJ_T_TRUE, &FIOBJ_VTABLE_TRUE);
98
+ }
@@ -0,0 +1,261 @@
1
+ /*
2
+ Copyright: Boaz segev, 2017
3
+ License: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+
8
+ #include "fiobj_types.h"
9
+
10
+ /* *****************************************************************************
11
+ String VTable
12
+ ***************************************************************************** */
13
+
14
+ static void fiobj_str_dealloc(fiobj_s *o) {
15
+ free(obj2str(o)->str);
16
+ free(&OBJ2HEAD(o));
17
+ }
18
+
19
+ static void fiobj_str_dealloc_static(fiobj_s *o) { free(&OBJ2HEAD(o)); }
20
+
21
+ static int fiobj_str_is_eq(fiobj_s *self, fiobj_s *other) {
22
+ if (!other || other->type != self->type ||
23
+ obj2str(self)->len != obj2str(other)->len)
24
+ return 0;
25
+ return self == other || obj2str(self)->str == obj2str(other)->str ||
26
+ !memcmp(obj2str(self)->str, obj2str(other)->str, obj2str(self)->len);
27
+ }
28
+
29
+ static fio_cstr_s fio_str2str(fiobj_s *o) {
30
+ return (fio_cstr_s){.buffer = obj2str(o)->str, .len = obj2str(o)->len};
31
+ }
32
+ static int64_t fio_str2i(fiobj_s *o) {
33
+ char *s = obj2str(o)->str;
34
+ return fio_atol(&s);
35
+ }
36
+ static double fio_str2f(fiobj_s *o) {
37
+ char *s = obj2str(o)->str;
38
+ return fio_atof(&s);
39
+ }
40
+
41
+ static struct fiobj_vtable_s FIOBJ_VTABLE_STRING = {
42
+ .free = fiobj_str_dealloc,
43
+ .to_i = fio_str2i,
44
+ .to_f = fio_str2f,
45
+ .to_str = fio_str2str,
46
+ .is_eq = fiobj_str_is_eq,
47
+ .count = fiobj_noop_count,
48
+ .each1 = fiobj_noop_each1,
49
+ };
50
+
51
+ static struct fiobj_vtable_s FIOBJ_VTABLE_STATIC_STRING = {
52
+ .free = fiobj_str_dealloc_static,
53
+ .to_i = fio_str2i,
54
+ .to_f = fio_str2f,
55
+ .to_str = fio_str2str,
56
+ .is_eq = fiobj_str_is_eq,
57
+ .count = fiobj_noop_count,
58
+ .each1 = fiobj_noop_each1,
59
+ };
60
+
61
+ /* *****************************************************************************
62
+ String API
63
+ ***************************************************************************** */
64
+
65
+ static inline fiobj_s *fiobj_str_alloc(size_t len) {
66
+ fiobj_head_s *head;
67
+ head = malloc(sizeof(*head) + sizeof(fio_str_s));
68
+ if (!head)
69
+ perror("ERROR: fiobj string couldn't allocate memory"), exit(errno);
70
+ *head = (fiobj_head_s){
71
+ .ref = 1, .vtable = &FIOBJ_VTABLE_STRING,
72
+ };
73
+ *obj2str(HEAD2OBJ(head)) = (fio_str_s){
74
+ .type = FIOBJ_T_STRING, .len = len, .capa = len, .str = malloc(len + 1),
75
+ };
76
+ if (!obj2str(HEAD2OBJ(head))->str)
77
+ perror("ERROR: fiobj string couldn't allocate memory"), exit(errno);
78
+ obj2str(HEAD2OBJ(head))->str[len] = 0;
79
+ return HEAD2OBJ(head);
80
+ }
81
+
82
+ /** Creates a String object. Remember to use `fiobj_free`. */
83
+ fiobj_s *fiobj_str_new(const char *str, size_t len) {
84
+ fiobj_s *s = fiobj_str_alloc(len);
85
+ if (str)
86
+ memcpy(obj2str(s)->str, str, len);
87
+ return s;
88
+ }
89
+
90
+ /** Creates a buffer String object. Remember to use `fiobj_free`. */
91
+ fiobj_s *fiobj_str_buf(size_t capa) {
92
+ if (capa)
93
+ capa = capa - 1;
94
+ else
95
+ capa = fiobj_memory_page_size();
96
+ fiobj_s *s = fiobj_str_alloc(capa);
97
+ fiobj_str_clear(s);
98
+ return s;
99
+ }
100
+
101
+ /**
102
+ * Creates a static String object from a static C string. Remember `fiobj_free`.
103
+ *
104
+ * This variation avoids allocating memory for an existing static String.
105
+ *
106
+ * The object still needs to be frees, but the string isn't copied and isn't
107
+ * freed.
108
+ *
109
+ * NOTICE: static strings can't be written to.
110
+ */
111
+ fiobj_s *fiobj_str_static(const char *str, size_t len) {
112
+ fiobj_head_s *head;
113
+ head = malloc(sizeof(*head) + sizeof(fio_str_s));
114
+ if (!head)
115
+ perror("ERROR: fiobj string couldn't allocate memory"), exit(errno);
116
+ *head = (fiobj_head_s){
117
+ .ref = 1, .vtable = &FIOBJ_VTABLE_STATIC_STRING,
118
+ };
119
+ *obj2str(HEAD2OBJ(head)) = (fio_str_s){
120
+ .type = FIOBJ_T_STRING,
121
+ .len = (len ? len : strlen(str)),
122
+ .capa = 0,
123
+ .str = (char *)str,
124
+ .is_static = 1,
125
+ };
126
+ return HEAD2OBJ(head);
127
+ }
128
+
129
+ /** Creates a copy from an existing String. Remember to use `fiobj_free`. */
130
+ fiobj_s *fiobj_str_copy(fiobj_s *src) {
131
+ fio_cstr_s s = fiobj_obj2cstr(src);
132
+ return fiobj_str_new(s.data, s.len);
133
+ }
134
+
135
+ /** Creates a String object using a printf like interface. */
136
+ __attribute__((format(printf, 1, 0))) fiobj_s *
137
+ fiobj_strvprintf(const char *format, va_list argv) {
138
+ fiobj_s *str = NULL;
139
+ va_list argv_cpy;
140
+ va_copy(argv_cpy, argv);
141
+ int len = vsnprintf(NULL, 0, format, argv_cpy);
142
+ va_end(argv_cpy);
143
+ if (len == 0)
144
+ str = fiobj_str_new("", 0);
145
+ if (len <= 0)
146
+ return str;
147
+ str = fiobj_str_new(NULL, len);
148
+ vsnprintf(((fio_str_s *)(str))->str, len + 1, format, argv);
149
+ return str;
150
+ }
151
+ __attribute__((format(printf, 1, 2))) fiobj_s *
152
+ fiobj_strprintf(const char *format, ...) {
153
+ va_list argv;
154
+ va_start(argv, format);
155
+ fiobj_s *str = fiobj_strvprintf(format, argv);
156
+ va_end(argv);
157
+ return str;
158
+ }
159
+
160
+ /** Confirms the requested capacity is available and allocates as required. */
161
+ void fiobj_str_capa_assert(fiobj_s *str, size_t size) {
162
+ if (str->type != FIOBJ_T_STRING || ((fio_str_s *)str)->capa == 0 ||
163
+ ((fio_str_s *)str)->capa >= size + 1)
164
+ return;
165
+ /* it's better to crash than live without memory... */
166
+ ((fio_str_s *)str)->str = realloc(((fio_str_s *)str)->str, size + 1);
167
+ ((fio_str_s *)str)->capa = size + 1;
168
+ ((fio_str_s *)str)->str[size] = 0;
169
+ return;
170
+ }
171
+
172
+ /** Return's a String's capacity, if any. */
173
+ size_t fiobj_str_capa(fiobj_s *str) {
174
+ if (str->type != FIOBJ_T_STRING)
175
+ return 0;
176
+ return ((fio_str_s *)str)->capa;
177
+ }
178
+
179
+ /** Resizes a String object, allocating more memory if required. */
180
+ void fiobj_str_resize(fiobj_s *str, size_t size) {
181
+ if (str->type != FIOBJ_T_STRING)
182
+ return;
183
+ fiobj_str_capa_assert(str, size);
184
+ ((fio_str_s *)str)->len = size;
185
+ ((fio_str_s *)str)->str[size] = 0;
186
+ return;
187
+ }
188
+
189
+ /** Deallocates any unnecessary memory (if supported by OS). */
190
+ void fiobj_str_minimize(fiobj_s *str) {
191
+ if (str->type != FIOBJ_T_STRING)
192
+ return;
193
+ ((fio_str_s *)str)->capa = ((fio_str_s *)str)->len + 1;
194
+ ((fio_str_s *)str)->str =
195
+ realloc(((fio_str_s *)str)->str, ((fio_str_s *)str)->capa);
196
+ return;
197
+ }
198
+
199
+ /** Empties a String's data. */
200
+ void fiobj_str_clear(fiobj_s *str) {
201
+ if (str->type != FIOBJ_T_STRING)
202
+ return;
203
+ ((fio_str_s *)str)->str[0] = 0;
204
+ ((fio_str_s *)str)->len = 0;
205
+ }
206
+
207
+ /**
208
+ * Writes data at the end of the string, resizing the string as required.
209
+ * Returns the new length of the String
210
+ */
211
+ size_t fiobj_str_write(fiobj_s *dest, const char *data, size_t len) {
212
+ if (dest->type != FIOBJ_T_STRING)
213
+ return 0;
214
+ fiobj_str_resize(dest, obj2str(dest)->len + len);
215
+ if (len < 8) {
216
+ size_t pos = obj2str(dest)->len;
217
+ while (len) {
218
+ len--;
219
+ pos--;
220
+ obj2str(dest)->str[pos] = data[len];
221
+ }
222
+ } else {
223
+ memcpy(((fio_str_s *)dest)->str + ((fio_str_s *)dest)->len - len, data,
224
+ len);
225
+ }
226
+ // ((fio_str_s *)dest)->str[((fio_str_s *)dest)->len] = 0; // see str_resize
227
+ return ((fio_str_s *)dest)->len;
228
+ }
229
+ /**
230
+ * Writes data at the end of the string, resizing the string as required.
231
+ * Returns the new length of the String
232
+ */
233
+ size_t fiobj_str_write2(fiobj_s *dest, const char *format, ...) {
234
+ if (dest->type != FIOBJ_T_STRING)
235
+ return 0;
236
+ va_list argv;
237
+ va_start(argv, format);
238
+ int len = vsnprintf(NULL, 0, format, argv);
239
+ va_end(argv);
240
+ if (len <= 0)
241
+ return ((fio_str_s *)dest)->len;
242
+ fiobj_str_resize(dest, ((fio_str_s *)dest)->len + len);
243
+ va_start(argv, format);
244
+ vsnprintf(((fio_str_s *)(dest))->str + ((fio_str_s *)dest)->len - len,
245
+ len + 1, format, argv);
246
+ va_end(argv);
247
+ // ((fio_str_s *)dest)->str[((fio_str_s *)dest)->len] = 0; // see str_resize
248
+ return ((fio_str_s *)dest)->len;
249
+ }
250
+ /**
251
+ * Writes data at the end of the string, resizing the string as required.
252
+ * Returns the new length of the String
253
+ */
254
+ size_t fiobj_str_join(fiobj_s *dest, fiobj_s *obj) {
255
+ if (dest->type != FIOBJ_T_STRING)
256
+ return 0;
257
+ fio_cstr_s o = fiobj_obj2cstr(obj);
258
+ if (o.len == 0)
259
+ return obj2str(dest)->len;
260
+ return fiobj_str_write(dest, o.data, o.len);
261
+ }