merge
[dcpu16] / common.c
1 /* common.c
2 * Utility functions shared between modules, but not exported.
3 */
4
5 #include <stdlib.h>
6 #include <stdio.h>
7 #include <string.h>
8 #include <errno.h>
9 #include <assert.h>
10 #include <time.h>
11 #include <sys/time.h>
12
13 #ifdef __MACH__
14 #include <mach/clock.h>
15 #include <mach/mach.h>
16 #endif /* __MACH__ */
17
18 #include "common.h"
19 #include "dcpu16.h"
20
21 /* initialize a generic dynamic array struct */
22 int dynarray_init(struct dynamic_array *da, size_t entry_size, size_t grow_size) {
23 assert(da);
24 assert(entry_size);
25 assert(grow_size);
26
27 if (entry_size == 0 || grow_size == 0) {
28 fprintf(stderr, "%s: internal error: sizes cannot be zero\n", __func__);
29 errno = EINVAL;
30 return -1;
31 }
32
33 da->allocated = 0;
34 da->entries = 0;
35 da->entry_size = entry_size;
36 da->grow_size = grow_size;
37 da->a = malloc(da->entry_size * da->grow_size);
38 if (da->a == NULL) {
39 fprintf(stderr, "%s():%s\n", "malloc", strerror(errno));
40 return -1;
41 }
42 da->allocated = da->grow_size;
43 return 0;
44 }
45
46 /* allocate and initialize a new generic dynamic array */
47 struct dynamic_array *dynarray_new(size_t entry_size, size_t grow_size) {
48 struct dynamic_array *da;
49
50 assert(entry_size);
51 assert(grow_size);
52
53 da = calloc(1, sizeof *da);
54 if (da == NULL) {
55 fprintf(stderr, "%s():%s\n", "calloc", strerror(errno));
56 return NULL;
57 }
58
59 if (dynarray_init(da, entry_size, grow_size)) {
60 fprintf(stderr, "%s():%s\n", "dynarray_init", strerror(errno));
61 free(da);
62 return NULL;
63 }
64
65 return da;
66 }
67
68 void dynarray_empty(struct dynamic_array *da, void (*free_element)(void *)) {
69 while ( da->entries-- ) {
70 void *element = (void *)DYNARRAY_ITEM(*da, da->entries);
71
72 free_element(element);
73 }
74 free(da->a);
75 da->a = NULL;
76 da->allocated = 0;
77 }
78
79 /* copy item onto end of array */
80 void *dynarray_add(struct dynamic_array *da, void *item) {
81 void *dst;
82
83 assert(da);
84 assert(item);
85
86 /* make room, make room */
87 if (da->entries == da->allocated) {
88 size_t new_allocated = da->allocated + da->grow_size;
89 void *tmp_ptr = realloc(da->a, new_allocated * da->entry_size);
90 if (tmp_ptr == NULL) {
91 fprintf(stderr, "%s():%s\n", "realloc", strerror(errno));
92 return NULL;
93 }
94 da->a = tmp_ptr;
95 da->allocated = new_allocated;
96
97 }
98
99 dst = DYNARRAY_ITEM(*da, da->entries);
100 memcpy(dst, item, da->entry_size);
101
102 da->entries++;
103
104 return dst;
105 }
106
107 /* simplified strtoul with range checking */
108 int str_to_word(char *s) {
109 unsigned long l;
110 char *ep;
111
112 assert(s);
113
114 errno = 0;
115 l = strtoul(s, &ep, 0);
116
117 if (errno
118 || !(*s && *ep == '\0') ) {
119 /* out of range of conversion, or invalid character encountered */
120 return -1;
121 }
122
123 if (l >= DCPU16_RAM) {
124 /* out of range for our needs */
125 errno = ERANGE;
126 return -1;
127 }
128
129 return l;
130 }
131
132 /* just like strtok_r, but ignores separators within quotes */
133 char *strqtok_r(char *str, const char *sep, int esc, const char *quote, char **lastq, char **lasts) {
134 int escaped = 0;
135 int retry;
136 char *tok,
137 *lastq_ret = NULL,
138 *src,
139 *dst;
140
141 if (str) {
142 *lasts = str;
143 *lastq = NULL;
144 }
145
146 /* next token starts after any leading seps */
147 while (**lasts && strchr(sep, **lasts)) {
148 **lasts = '\0';
149 (*lasts)++;
150 }
151
152 tok = *lasts;
153 if (*tok == '\0')
154 return NULL;
155
156 do {
157 retry = 0;
158 while (**lasts) {
159 /* the previous character was the escape, do not consider this character any further */
160 if (escaped) {
161 escaped = 0;
162 (*lasts)++;
163 continue;
164 }
165
166 /* this character is the escape, do not consider the next character */
167 if (**lasts == esc) {
168 escaped = 1;
169 (*lasts)++;
170 continue;
171 }
172
173 /* we have a quote open, only consider matching quote to close */
174 if (*lastq) {
175 if (**lasts == **lastq)
176 *lastq = NULL;
177 (*lasts)++;
178 continue;
179 }
180
181 /* this character is an opening quote, remember what it is */
182 if (strchr(quote, **lasts)) {
183 *lastq = *lasts;
184 (*lasts)++;
185 continue;
186 }
187
188 /* this character is a separator, separate and be done */
189 if (strchr(sep, **lasts)) {
190 **lasts = '\0';
191 (*lasts)++;
192 break;
193 }
194 (*lasts)++;
195 }
196
197 /* were we left with an unmatched quote?
198 remember where we had trouble
199 try everything following lonely quote again, but pretend quote is there */
200 if (*lastq) {
201 lastq_ret = *lastq;
202 *lasts = *lastq + 1;
203 *lastq = NULL;
204 retry = 1;
205 }
206 } while (retry);
207
208 /* now strip escape characters */
209 for (src = dst = tok; *src; src++, dst++) {
210 if (*src == esc) {
211 src++;
212 if (*src == '\0')
213 break;
214 }
215 *dst = *src;
216 }
217 *dst = *src;
218
219 /* remember where we had trouble */
220 if (lastq_ret)
221 *lastq = lastq_ret;
222
223 return tok;
224 }
225
226 /* like gettimeofday, except with nanoseconds */
227 inline
228 int gettimespecofday(struct timespec *ts) {
229 int retval = 0;
230 #ifdef __MACH__
231 clock_serv_t cclock;
232 mach_timespec_t mts;
233
234 host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
235 clock_get_time(cclock, &mts);
236 mach_port_deallocate(mach_task_self(), cclock);
237 ts->tv_sec = mts.tv_sec;
238 ts->tv_nsec = mts.tv_nsec;
239 #else /* __MACH__ */
240 retval = clock_gettime(CLOCK_REALTIME, ts);
241 #endif /* __MACH__ */
242
243 return retval;
244 }
245
246 inline
247 int timespec_add(struct timespec *result, const struct timespec *x) {
248 result->tv_sec += x->tv_sec;
249 result->tv_nsec += x->tv_nsec;
250
251 result->tv_sec += result->tv_nsec / 1000000000;
252 result->tv_nsec %= 1000000000;
253
254 return 0;
255 }
256
257 inline
258 int timespec_subtract(struct timespec *result, struct timespec *x, struct timespec *y) {
259 if (x->tv_nsec < y->tv_nsec) {
260 int z = (y->tv_nsec - x->tv_nsec) / 1000000000 + 1;
261 y->tv_nsec -= 1000000000 * z;
262 y->tv_sec += z;
263 }
264 if (x->tv_nsec - y->tv_nsec > 1000000000) {
265 int z = (x->tv_nsec - y->tv_nsec) / 1000000000;
266 y->tv_nsec += 1000000000 * z;
267 y->tv_sec -= z;
268 }
269 result->tv_sec = x->tv_sec - y->tv_sec;
270 result->tv_nsec = x->tv_nsec - y->tv_nsec;
271
272 return x->tv_sec < y->tv_sec;
273 }
274
275 int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y) {
276 /* Perform the carry for the later subtraction by updating y. */
277 if (x->tv_usec < y->tv_usec) {
278 int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
279 y->tv_usec -= 1000000 * nsec;
280 y->tv_sec += nsec;
281 }
282 if (x->tv_usec - y->tv_usec > 1000000) {
283 int nsec = (x->tv_usec - y->tv_usec) / 1000000;
284 y->tv_usec += 1000000 * nsec;
285 y->tv_sec -= nsec;
286 }
287
288 /* Compute the time remaining to wait.
289 tv_usec is certainly positive. */
290 result->tv_sec = x->tv_sec - y->tv_sec;
291 result->tv_usec = x->tv_usec - y->tv_usec;
292
293 /* Return 1 if result is negative. */
294 return x->tv_sec < y->tv_sec;
295 }