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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +26 -0
- data/README.md +18 -12
- data/SPEC-Websocket-Draft.md +9 -5
- data/bin/ws-echo +3 -0
- data/examples/config.ru +0 -1
- data/examples/echo.ru +3 -1
- data/examples/redis.ru +0 -1
- data/ext/iodine/base64.c +97 -105
- data/ext/iodine/defer.c +16 -1
- data/ext/iodine/defer.h +10 -0
- data/ext/iodine/evio.c +35 -13
- data/ext/iodine/extconf.rb +1 -1
- data/ext/iodine/facil.c +12 -1
- data/ext/iodine/facil.h +3 -1
- data/ext/iodine/fio2resp.c +71 -0
- data/ext/iodine/fio2resp.h +50 -0
- data/ext/iodine/fio_cli_helper.c +404 -0
- data/ext/iodine/fio_cli_helper.h +152 -0
- data/ext/iodine/fiobj.h +631 -0
- data/ext/iodine/fiobj_alloc.c +81 -0
- data/ext/iodine/fiobj_ary.c +290 -0
- data/ext/iodine/fiobj_generic.c +260 -0
- data/ext/iodine/fiobj_hash.c +447 -0
- data/ext/iodine/fiobj_io.c +58 -0
- data/ext/iodine/fiobj_json.c +779 -0
- data/ext/iodine/fiobj_misc.c +213 -0
- data/ext/iodine/fiobj_numbers.c +113 -0
- data/ext/iodine/fiobj_primitives.c +98 -0
- data/ext/iodine/fiobj_str.c +261 -0
- data/ext/iodine/fiobj_sym.c +213 -0
- data/ext/iodine/fiobj_tests.c +474 -0
- data/ext/iodine/fiobj_types.h +290 -0
- data/ext/iodine/http1.c +54 -36
- data/ext/iodine/http1_parser.c +143 -35
- data/ext/iodine/http1_parser.h +6 -3
- data/ext/iodine/http1_response.c +0 -1
- data/ext/iodine/http_response.c +1 -1
- data/ext/iodine/iodine.c +20 -4
- data/ext/iodine/iodine_protocol.c +5 -4
- data/ext/iodine/iodine_pubsub.c +1 -1
- data/ext/iodine/random.c +5 -5
- data/ext/iodine/sha1.c +5 -8
- data/ext/iodine/sha2.c +8 -11
- data/ext/iodine/sha2.h +3 -3
- data/ext/iodine/sock.c +29 -31
- data/ext/iodine/websocket_parser.h +428 -0
- data/ext/iodine/websockets.c +112 -377
- data/ext/iodine/xor-crypt.c +16 -12
- data/lib/iodine/version.rb +1 -1
- metadata +21 -3
- 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 *)#
|
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
|
+
}
|