Mercurial > code
annotate pack.c @ 66:f773c76b1f3c
Added extern C tests
author | David Demelier <markand@malikania.fr> |
---|---|
date | Wed, 09 Nov 2011 21:08:43 +0100 |
parents | b2cd1fd33bb0 |
children | 820426789f17 |
rev | line source |
---|---|
56 | 1 /* |
2 * Copyright (c) 2011, David Demelier <markand@malikania.fr> | |
3 * | |
4 * Permission to use, copy, modify, and/or distribute this software for any | |
5 * purpose with or without fee is hereby granted, provided that the above | |
6 * copyright notice and this permission notice appear in all copies. | |
7 * | |
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
15 */ | |
16 | |
17 #include <sys/queue.h> | |
18 #include <stdio.h> | |
19 #include <stdlib.h> | |
20 #include <stdint.h> | |
21 #include <stdarg.h> | |
22 #include <ctype.h> | |
23 | |
24 #include "pack.h" | |
25 | |
26 /* -------------------------------------------------------- | |
27 * structure definitions | |
28 * -------------------------------------------------------- */ | |
29 | |
30 /* | |
31 * Conversion function pointer. | |
32 */ | |
33 typedef void (*convert_fn)(void *); | |
34 | |
35 /* | |
36 * Item structure store the integer into the largest data type | |
37 * uint64_t. | |
38 */ | |
39 | |
40 struct item { | |
41 size_t size; /* 8, 16, 32 or 64 bits? */ | |
42 uint64_t i; /* the data */ | |
43 convert_fn conv; /* conversion function */ | |
44 STAILQ_ENTRY(item) next; | |
45 }; | |
46 | |
47 /* | |
48 * List of item structure. | |
49 */ | |
50 | |
51 STAILQ_HEAD(item_list, item); | |
52 | |
53 /* -------------------------------------------------------- | |
54 * prototypes | |
55 * -------------------------------------------------------- */ | |
56 | |
57 static int pack_item_add(struct item_list *, const struct item *); | |
58 static int pack_parse(struct item_list *, const char *, va_list); | |
59 static size_t pack_getsize(char); | |
60 static convert_fn pack_getconvert(char); | |
61 static void pack_convert16(void *); | |
62 static void pack_convert32(void *); | |
63 static void pack_convert64(void *); | |
64 static int pack_fatal(struct item_list *); | |
65 | |
66 /* -------------------------------------------------------- | |
67 * private functions | |
68 * -------------------------------------------------------- */ | |
69 | |
70 #define LENGTH(x) (sizeof (x) / sizeof (x[0])) | |
71 | |
72 /* | |
73 * Associative structure, the `tok' member is used for the format | |
74 * and define the integer size. The convert function reverse the | |
75 * bytes if it is needed. | |
76 */ | |
77 | |
78 static struct integer { | |
79 char tok; /* format char */ | |
80 size_t tocopy; /* size */ | |
81 convert_fn convert; /* conversion function */ | |
82 } sizes[] = { | |
83 { 'c', sizeof (uint8_t), NULL }, | |
84 { 's', sizeof (uint16_t), &pack_convert16 }, | |
85 { 'i', sizeof (uint32_t), &pack_convert32 }, | |
86 { 'l', sizeof (uint64_t), &pack_convert64 } | |
87 }; | |
88 | |
89 /* | |
90 * Try to append a new item to the list. The function create a new item | |
91 * object since the pack_parse() use a stack'ed item and not an | |
92 * allocated object. Returns 0 or -1 on failure. | |
93 */ | |
94 | |
95 static int | |
96 pack_item_add(struct item_list *list, const struct item *item) | |
97 { | |
98 struct item *res; | |
99 | |
100 if ((res = malloc(sizeof (struct item))) == NULL) | |
101 return -1; | |
102 | |
103 res->size = item->size; | |
104 res->i = item->i; | |
105 res->conv = item->conv; | |
106 | |
107 STAILQ_INSERT_TAIL(list, res, next); | |
108 | |
109 return 0; | |
110 } | |
111 | |
112 /* | |
113 * Parse the format, return 0 on success or -1 on failure. | |
114 */ | |
115 | |
116 #define PACK_GETARG(i, ap, tok) \ | |
117 do { \ | |
118 switch ((tok)) { \ | |
119 case 'c': \ | |
120 case 's': \ | |
121 case 'i': \ | |
122 (i) = va_arg((ap), unsigned); \ | |
123 break; \ | |
124 case 'l': \ | |
125 (i) = va_arg((ap), uint64_t); \ | |
126 default: \ | |
127 break; \ | |
128 } \ | |
129 } while (/* CONSTCOND */ 0) | |
130 | |
131 /* | |
132 * Little helper to get number of element when the user wants a array of | |
133 * objects. Sets the nelem to 0 on array failure or 1 on normal count. | |
134 */ | |
135 | |
136 #define PACK_GETNELEM(nelem, p) \ | |
137 do { \ | |
138 if (p[1] == '[') { \ | |
139 if (p[2] != ']') \ | |
140 nelem = (int) strtol(p + 2, NULL, 10); \ | |
141 else if (p[2] == ']') \ | |
142 nelem = va_arg(ap, int); \ | |
143 \ | |
144 /* Go to the end of fmt and find ']' */ \ | |
145 while (*p != ']' && *p != '\0') \ | |
146 ++p; \ | |
147 } else \ | |
148 nelem = 1; \ | |
149 } while (/* CONSTCOND */ 0) | |
150 | |
151 static int | |
152 pack_parse(struct item_list *list, const char *fmt, va_list ap) | |
153 { | |
154 const char *p; | |
155 char tok; | |
156 struct item item; | |
157 | |
158 STAILQ_INIT(list); | |
159 | |
160 for (p = fmt; *p != '\0'; ++p) { | |
161 int i, nelem = 1; | |
162 | |
163 if (isspace(*p)) | |
164 continue; | |
165 | |
166 tok = *p; | |
167 item.size = pack_getsize(tok); | |
168 | |
169 /* Bad character */ | |
170 if (item.size == 0) | |
171 continue; | |
172 | |
173 PACK_GETNELEM(nelem, p); | |
174 if (nelem == 0) | |
175 continue; | |
176 | |
177 for (i = 0; i < nelem; ++i) { | |
178 PACK_GETARG(item.i, ap, tok); | |
179 item.conv = pack_getconvert(tok); | |
180 | |
181 if (pack_item_add(list, &item) < 0) | |
182 return pack_fatal(list); | |
183 } | |
184 } | |
185 | |
186 return 0; | |
187 } | |
188 | |
189 /* | |
190 * Get the appropriate size associated with the `tok' character. If | |
191 * the token is not found the result is 0. | |
192 */ | |
193 | |
194 static size_t | |
195 pack_getsize(char tok) | |
196 { | |
197 struct integer *s; | |
198 unsigned int i; | |
199 | |
200 for (s = sizes, i = 0; i < LENGTH(sizes); ++s, ++i) | |
201 if (s->tok == tok) | |
202 return s->tocopy; | |
203 | |
204 return 0; | |
205 } | |
206 | |
207 /* | |
208 * Return the conversion function. | |
209 */ | |
210 | |
211 static convert_fn | |
212 pack_getconvert(char tok) | |
213 { | |
214 struct integer *s; | |
215 unsigned int i; | |
216 | |
217 for (s = sizes, i = 0; i < LENGTH(sizes); ++s, ++i) | |
218 if (s->tok == tok) | |
219 return s->convert; | |
220 | |
221 return NULL; | |
222 } | |
223 | |
224 /* | |
225 * Conversion functions. They reverse the bytes without any | |
226 * check. | |
227 */ | |
228 | |
229 static void | |
230 pack_convert16(void *obj) | |
231 { | |
232 uint16_t *x = obj; | |
233 | |
234 *x = pack_swap16(*x); | |
235 } | |
236 | |
237 static void | |
238 pack_convert32(void *obj) | |
239 { | |
240 uint32_t *x = obj; | |
241 | |
242 *x = pack_swap32(*x); | |
243 } | |
244 | |
245 static void | |
246 pack_convert64(void *obj) | |
247 { | |
248 uint64_t *x = obj; | |
249 | |
250 *x = pack_swap64(*x); | |
251 } | |
252 | |
253 static int | |
254 pack_fatal(struct item_list *list) | |
255 { | |
256 struct item *item, *tmp; | |
257 | |
258 STAILQ_FOREACH_SAFE(item, list, next, tmp) | |
259 free(item); | |
260 | |
261 return -1; | |
262 } | |
263 | |
264 /* -------------------------------------------------------- | |
265 * public functions | |
266 * -------------------------------------------------------- */ | |
267 | |
268 /* | |
269 * Function that writes everything to the file `path'. These functions | |
270 * does not append to the file, so if you want successive call to append | |
271 * variables use fpack instead. | |
272 * Returns 0 on success or -1 on failure. | |
273 */ | |
274 | |
275 int | |
57
311eaa9d004c
Renamed pack_* fonction for our guidelines
David Demelier <markand@malikania.fr>
parents:
56
diff
changeset
|
276 pack_write(int ptype, const char *path, const char *fmt, ...) |
56 | 277 { |
278 va_list ap; | |
279 int status; | |
280 | |
281 va_start(ap, fmt); | |
57
311eaa9d004c
Renamed pack_* fonction for our guidelines
David Demelier <markand@malikania.fr>
parents:
56
diff
changeset
|
282 status = pack_vwrite(ptype, path, fmt, ap); |
56 | 283 va_end(ap); |
284 | |
285 return status; | |
286 } | |
287 | |
288 int | |
57
311eaa9d004c
Renamed pack_* fonction for our guidelines
David Demelier <markand@malikania.fr>
parents:
56
diff
changeset
|
289 pack_vwrite(int ptype, const char *path, const char *fmt, va_list ap) |
56 | 290 { |
291 FILE *fp; | |
292 int status; | |
293 | |
294 if ((fp = fopen(path, "w+b")) == NULL) | |
295 return -1; | |
296 | |
57
311eaa9d004c
Renamed pack_* fonction for our guidelines
David Demelier <markand@malikania.fr>
parents:
56
diff
changeset
|
297 status = pack_vfwrite(ptype, fp, fmt, ap); |
56 | 298 fclose(fp); |
299 | |
300 return status; | |
301 } | |
302 | |
303 /* | |
304 * Write to a file that is already open. The function does not call | |
305 * fclose() so you need to do it yourself later. | |
306 */ | |
307 | |
308 int | |
57
311eaa9d004c
Renamed pack_* fonction for our guidelines
David Demelier <markand@malikania.fr>
parents:
56
diff
changeset
|
309 pack_fwrite(int ptype, FILE *fp, const char *fmt, ...) |
56 | 310 { |
311 va_list ap; | |
312 int status; | |
313 | |
314 va_start(ap, fmt); | |
57
311eaa9d004c
Renamed pack_* fonction for our guidelines
David Demelier <markand@malikania.fr>
parents:
56
diff
changeset
|
315 status = pack_vfwrite(ptype, fp, fmt, ap); |
56 | 316 va_end(ap); |
317 | |
318 return status; | |
319 } | |
320 | |
321 int | |
57
311eaa9d004c
Renamed pack_* fonction for our guidelines
David Demelier <markand@malikania.fr>
parents:
56
diff
changeset
|
322 pack_vfwrite(int ptype, FILE *fp, const char *fmt, va_list ap) |
56 | 323 { |
324 struct item_list list; | |
325 int status; | |
326 | |
327 if ((status = pack_parse(&list, fmt, ap)) == 0) { | |
328 struct item *item, *tmp; | |
329 | |
330 STAILQ_FOREACH_SAFE(item, &list, next, tmp) { | |
331 /* 8 bits does not need to be converted */ | |
332 if (ptype != PACK_HOST_BYTEORDER && item->conv != NULL) | |
333 item->conv(&item->i); | |
334 | |
335 fwrite(&item->i, item->size, 1, fp); | |
336 | |
337 free(item); | |
338 } | |
339 } | |
340 | |
341 return status; | |
342 } | |
343 | |
344 /* | |
345 * Function that read the binary file and restore values to the same format | |
346 * as pack functions. Arguments must be pointer of enough space to store | |
347 * the values. | |
348 */ | |
349 | |
350 int | |
57
311eaa9d004c
Renamed pack_* fonction for our guidelines
David Demelier <markand@malikania.fr>
parents:
56
diff
changeset
|
351 pack_read(int ptype, const char *path, const char *fmt, ...) |
56 | 352 { |
353 va_list ap; | |
354 int status; | |
355 | |
356 va_start(ap, fmt); | |
57
311eaa9d004c
Renamed pack_* fonction for our guidelines
David Demelier <markand@malikania.fr>
parents:
56
diff
changeset
|
357 status = pack_vread(ptype, path, fmt, ap); |
56 | 358 va_end(ap); |
359 | |
360 return status; | |
361 } | |
362 | |
363 int | |
57
311eaa9d004c
Renamed pack_* fonction for our guidelines
David Demelier <markand@malikania.fr>
parents:
56
diff
changeset
|
364 pack_vread(int ptype, const char *path, const char *fmt, va_list ap) |
56 | 365 { |
366 FILE *fp; | |
367 int status; | |
368 | |
369 if ((fp = fopen(path, "rb")) == NULL) | |
370 return -1; | |
371 | |
57
311eaa9d004c
Renamed pack_* fonction for our guidelines
David Demelier <markand@malikania.fr>
parents:
56
diff
changeset
|
372 status = pack_vfread(ptype, fp, fmt, ap); |
56 | 373 fclose(fp); |
374 | |
375 return status; | |
376 | |
377 } | |
378 | |
379 int | |
57
311eaa9d004c
Renamed pack_* fonction for our guidelines
David Demelier <markand@malikania.fr>
parents:
56
diff
changeset
|
380 pack_fread(int ptype, FILE *fp, const char *fmt, ...) |
56 | 381 { |
382 va_list ap; | |
383 int status; | |
384 | |
385 va_start(ap, fmt); | |
57
311eaa9d004c
Renamed pack_* fonction for our guidelines
David Demelier <markand@malikania.fr>
parents:
56
diff
changeset
|
386 status = pack_vfread(ptype, fp, fmt, ap); |
56 | 387 va_end(ap); |
388 | |
389 return status; | |
390 } | |
391 | |
392 int | |
57
311eaa9d004c
Renamed pack_* fonction for our guidelines
David Demelier <markand@malikania.fr>
parents:
56
diff
changeset
|
393 pack_vfread(int ptype, FILE *fp, const char *fmt, va_list ap) |
56 | 394 { |
395 const char *p; | |
396 void *ptr; | |
397 size_t tocopy; | |
398 convert_fn convert; | |
399 | |
400 for (p = fmt; *p != '\0'; ++p) { | |
401 char tok; | |
402 int nelem, i; | |
403 | |
404 if (isspace(*p)) | |
405 continue; | |
406 | |
407 tok = *p; | |
408 tocopy = pack_getsize(tok); | |
409 | |
410 /* Bad character */ | |
411 if (tocopy == 0) | |
412 continue; | |
413 | |
414 /* Determine how many element to read and copy */ | |
415 PACK_GETNELEM(nelem, p); | |
416 if (nelem == 0) | |
417 continue; | |
418 | |
419 if ((ptr = va_arg(ap, void *)) == NULL) | |
420 continue; | |
421 | |
422 for (i = 0; i < nelem; ++i) { | |
423 fread((char *) ptr + (tocopy * i), tocopy, 1, fp); | |
424 | |
425 /* Convert if needed */ | |
426 convert = pack_getconvert(tok); | |
427 if (ptype != PACK_HOST_BYTEORDER && convert != NULL) | |
428 convert((char *) ptr + (tocopy * i)); | |
429 } | |
430 } | |
431 | |
432 return 0; | |
433 } |