source: mainline/uspace/app/bithenge/transform.c@ 5f4cf872

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 5f4cf872 was 5f4cf872, checked in by Sean Bartell <wingedtachikoma@…>, 13 years ago

Bithenge: add more uint transforms

  • Property mode set to 100644
File size: 16.8 KB
Line 
1/*
2 * Copyright (c) 2012 Sean Bartell
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup bithenge
30 * @{
31 */
32/**
33 * @file
34 * Transforms.
35 */
36
37#include <assert.h>
38#include <errno.h>
39#include <stdlib.h>
40#include "blob.h"
41#include "transform.h"
42
43/** Initialize a new transform.
44 * @param[out] self Transform to initialize.
45 * @param[in] ops Operations provided by the transform.
46 * @return EOK or an error code from errno.h. */
47int bithenge_init_transform(bithenge_transform_t *self,
48 const bithenge_transform_ops_t *ops)
49{
50 assert(ops);
51 assert(ops->apply);
52 assert(ops->destroy);
53 self->ops = ops;
54 self->refs = 1;
55 return EOK;
56}
57
58static void transform_indestructible(bithenge_transform_t *self)
59{
60 assert(false);
61}
62
63static int ascii_apply(bithenge_transform_t *self,
64 bithenge_node_t *in, bithenge_node_t **out)
65{
66 int rc;
67 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
68 return EINVAL;
69 bithenge_blob_t *blob = bithenge_node_as_blob(in);
70 aoff64_t size;
71 rc = bithenge_blob_size(blob, &size);
72 if (rc != EOK)
73 return rc;
74
75 char *buffer = malloc(size + 1);
76 if (!buffer)
77 return ENOMEM;
78 aoff64_t size_read = size;
79 rc = bithenge_blob_read(blob, 0, buffer, &size_read);
80 if (rc != EOK) {
81 free(buffer);
82 return rc;
83 }
84 if (size_read != size) {
85 free(buffer);
86 return EINVAL;
87 }
88 buffer[size] = '\0';
89
90 /* TODO: what if the OS encoding is incompatible with ASCII? */
91 return bithenge_new_string_node(out, buffer, true);
92}
93
94static const bithenge_transform_ops_t ascii_ops = {
95 .apply = ascii_apply,
96 .destroy = transform_indestructible,
97};
98
99/** The ASCII text transform. */
100bithenge_transform_t bithenge_ascii_transform = {
101 &ascii_ops, 1
102};
103
104static int prefix_length_1(bithenge_transform_t *self, bithenge_blob_t *blob,
105 aoff64_t *out)
106{
107 *out = 1;
108 return EOK;
109}
110
111static int prefix_length_2(bithenge_transform_t *self, bithenge_blob_t *blob,
112 aoff64_t *out)
113{
114 *out = 2;
115 return EOK;
116}
117
118static int prefix_length_4(bithenge_transform_t *self, bithenge_blob_t *blob,
119 aoff64_t *out)
120{
121 *out = 4;
122 return EOK;
123}
124
125static int prefix_length_8(bithenge_transform_t *self, bithenge_blob_t *blob,
126 aoff64_t *out)
127{
128 *out = 8;
129 return EOK;
130}
131
132#define MAKE_UINT_TRANSFORM(NAME, TYPE, ENDIAN, PREFIX_LENGTH_FUNC) \
133 static int NAME##_apply(bithenge_transform_t *self, \
134 bithenge_node_t *in, bithenge_node_t **out) \
135 { \
136 int rc; \
137 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB) \
138 return EINVAL; \
139 bithenge_blob_t *blob = bithenge_node_as_blob(in); \
140 \
141 /* Read too many bytes; success means the blob is too long. */ \
142 TYPE val[2]; \
143 aoff64_t size = sizeof(val[0]) + 1; \
144 rc = bithenge_blob_read(blob, 0, (char *)val, &size); \
145 if (rc != EOK) \
146 return rc; \
147 if (size != sizeof(val[0])) \
148 return EINVAL; \
149 \
150 return bithenge_new_integer_node(out, ENDIAN(val[0])); \
151 } \
152 \
153 static const bithenge_transform_ops_t NAME##_ops = { \
154 .apply = NAME##_apply, \
155 .prefix_length = PREFIX_LENGTH_FUNC, \
156 .destroy = transform_indestructible, \
157 }; \
158 \
159 bithenge_transform_t bithenge_##NAME##_transform = { \
160 &NAME##_ops, 1 \
161 }
162
163MAKE_UINT_TRANSFORM(uint8 , uint8_t , , prefix_length_1);
164MAKE_UINT_TRANSFORM(uint16le, uint16_t, uint16_t_le2host, prefix_length_2);
165MAKE_UINT_TRANSFORM(uint16be, uint16_t, uint16_t_be2host, prefix_length_2);
166MAKE_UINT_TRANSFORM(uint32le, uint32_t, uint32_t_le2host, prefix_length_4);
167MAKE_UINT_TRANSFORM(uint32be, uint32_t, uint32_t_be2host, prefix_length_4);
168MAKE_UINT_TRANSFORM(uint64le, uint64_t, uint32_t_le2host, prefix_length_8);
169MAKE_UINT_TRANSFORM(uint64be, uint64_t, uint32_t_be2host, prefix_length_8);
170
171static int zero_terminated_apply(bithenge_transform_t *self,
172 bithenge_node_t *in, bithenge_node_t **out)
173{
174 int rc;
175 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
176 return EINVAL;
177 bithenge_blob_t *blob = bithenge_node_as_blob(in);
178 aoff64_t size;
179 rc = bithenge_blob_size(blob, &size);
180 if (rc != EOK)
181 return rc;
182 if (size < 1)
183 return EINVAL;
184 char ch;
185 aoff64_t size_read = 1;
186 rc = bithenge_blob_read(blob, size - 1, &ch, &size_read);
187 if (rc != EOK)
188 return rc;
189 if (size_read != 1 || ch != '\0')
190 return EINVAL;
191 bithenge_blob_inc_ref(blob);
192 return bithenge_new_subblob(out, blob, 0, size - 1);
193}
194
195static int zero_terminated_prefix_length(bithenge_transform_t *self,
196 bithenge_blob_t *blob, aoff64_t *out)
197{
198 int rc;
199 char buffer[4096];
200 aoff64_t offset = 0, size_read = sizeof(buffer);
201 do {
202 rc = bithenge_blob_read(blob, offset, buffer, &size_read);
203 if (rc != EOK)
204 return rc;
205 char *found = memchr(buffer, '\0', size_read);
206 if (found) {
207 *out = found - buffer + offset + 1;
208 return EOK;
209 }
210 offset += size_read;
211 } while (size_read == sizeof(buffer));
212 return EINVAL;
213}
214
215static const bithenge_transform_ops_t zero_terminated_ops = {
216 .apply = zero_terminated_apply,
217 .prefix_length = zero_terminated_prefix_length,
218 .destroy = transform_indestructible,
219};
220
221/** The zero-terminated data transform. */
222bithenge_transform_t bithenge_zero_terminated_transform = {
223 &zero_terminated_ops, 1
224};
225
226static bithenge_named_transform_t primitive_transforms[] = {
227 {"ascii", &bithenge_ascii_transform},
228 {"uint8", &bithenge_uint8_transform},
229 {"uint16le", &bithenge_uint16le_transform},
230 {"uint16be", &bithenge_uint16be_transform},
231 {"uint32le", &bithenge_uint32le_transform},
232 {"uint32be", &bithenge_uint32be_transform},
233 {"uint64le", &bithenge_uint64le_transform},
234 {"uint64be", &bithenge_uint64be_transform},
235 {"zero_terminated", &bithenge_zero_terminated_transform},
236 {NULL, NULL}
237};
238
239/** An array of named built-in transforms. */
240bithenge_named_transform_t *bithenge_primitive_transforms = primitive_transforms;
241
242typedef struct {
243 bithenge_node_t base;
244 struct struct_transform *transform;
245 bithenge_blob_t *blob;
246} struct_node_t;
247
248typedef struct struct_transform {
249 bithenge_transform_t base;
250 bithenge_named_transform_t *subtransforms;
251} struct_transform_t;
252
253static bithenge_node_t *struct_as_node(struct_node_t *node)
254{
255 return &node->base;
256}
257
258static struct_node_t *node_as_struct(bithenge_node_t *node)
259{
260 return (struct_node_t *)node;
261}
262
263static bithenge_transform_t *struct_as_transform(struct_transform_t *xform)
264{
265 return &xform->base;
266}
267
268static struct_transform_t *transform_as_struct(bithenge_transform_t *xform)
269{
270 return (struct_transform_t *)xform;
271}
272
273static int struct_node_for_one(const char *name,
274 bithenge_transform_t *subxform, bithenge_blob_t **blob,
275 bithenge_for_each_func_t func, void *data)
276{
277 int rc;
278 bithenge_node_t *subxform_result = NULL;
279
280 aoff64_t sub_size;
281 rc = bithenge_transform_prefix_length(subxform, *blob, &sub_size);
282 if (rc != EOK)
283 goto error;
284
285 bithenge_node_t *subblob_node;
286 bithenge_blob_inc_ref(*blob);
287 rc = bithenge_new_subblob(&subblob_node, *blob, 0, sub_size);
288 if (rc != EOK)
289 goto error;
290
291 rc = bithenge_transform_apply(subxform, subblob_node,
292 &subxform_result);
293 bithenge_node_dec_ref(subblob_node);
294 if (rc != EOK)
295 goto error;
296
297 if (name) {
298 bithenge_node_t *name_node;
299 rc = bithenge_new_string_node(&name_node, name, false);
300 if (rc != EOK)
301 goto error;
302 rc = func(name_node, subxform_result, data);
303 subxform_result = NULL;
304 if (rc != EOK)
305 goto error;
306 } else {
307 if (bithenge_node_type(subxform_result) !=
308 BITHENGE_NODE_INTERNAL) {
309 rc = EINVAL;
310 goto error;
311 }
312 rc = bithenge_node_for_each(subxform_result, func, data);
313 if (rc != EOK)
314 goto error;
315 }
316
317 bithenge_node_t *blob_node;
318 rc = bithenge_new_offset_blob(&blob_node, *blob, sub_size);
319 *blob = NULL;
320 if (rc != EOK)
321 goto error;
322 *blob = bithenge_node_as_blob(blob_node);
323
324error:
325 bithenge_node_dec_ref(subxform_result);
326 return rc;
327}
328
329static int struct_node_for_each(bithenge_node_t *base,
330 bithenge_for_each_func_t func, void *data)
331{
332 int rc = EOK;
333 struct_node_t *struct_node = node_as_struct(base);
334 bithenge_named_transform_t *subxforms =
335 struct_node->transform->subtransforms;
336
337 bithenge_node_t *blob_node = NULL;
338 bithenge_blob_t *blob = NULL;
339 bithenge_blob_inc_ref(struct_node->blob);
340 rc = bithenge_new_offset_blob(&blob_node, struct_node->blob, 0);
341 if (rc != EOK) {
342 blob = NULL;
343 goto error;
344 }
345 blob = bithenge_node_as_blob(blob_node);
346
347 for (size_t i = 0; subxforms[i].transform; i++) {
348 rc = struct_node_for_one(subxforms[i].name,
349 subxforms[i].transform, &blob, func, data);
350 if (rc != EOK)
351 goto error;
352 }
353
354 aoff64_t remaining;
355 rc = bithenge_blob_size(blob, &remaining);
356 if (rc != EOK)
357 goto error;
358 if (remaining != 0) {
359 rc = EINVAL;
360 goto error;
361 }
362
363error:
364 bithenge_blob_dec_ref(blob);
365 return rc;
366}
367
368static void struct_node_destroy(bithenge_node_t *base)
369{
370 struct_node_t *node = node_as_struct(base);
371 bithenge_transform_dec_ref(struct_as_transform(node->transform));
372 bithenge_blob_dec_ref(node->blob);
373 free(node);
374}
375
376static const bithenge_internal_node_ops_t struct_node_ops = {
377 .for_each = struct_node_for_each,
378 .destroy = struct_node_destroy,
379};
380
381static int struct_transform_apply(bithenge_transform_t *base,
382 bithenge_node_t *in, bithenge_node_t **out)
383{
384 struct_transform_t *self = transform_as_struct(base);
385 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
386 return EINVAL;
387 struct_node_t *node = malloc(sizeof(*node));
388 if (!node)
389 return ENOMEM;
390 int rc = bithenge_init_internal_node(struct_as_node(node),
391 &struct_node_ops);
392 if (rc != EOK) {
393 free(node);
394 return rc;
395 }
396 bithenge_transform_inc_ref(base);
397 node->transform = self;
398 bithenge_node_inc_ref(in);
399 node->blob = bithenge_node_as_blob(in);
400 *out = struct_as_node(node);
401 return EOK;
402}
403
404static int struct_transform_prefix_length(bithenge_transform_t *base,
405 bithenge_blob_t *blob, aoff64_t *out)
406{
407 struct_transform_t *self = transform_as_struct(base);
408 int rc = EOK;
409 bithenge_node_t *node;
410 bithenge_blob_inc_ref(blob);
411 rc = bithenge_new_offset_blob(&node, blob, 0);
412 blob = NULL;
413 if (rc != EOK)
414 goto error;
415 blob = bithenge_node_as_blob(node);
416 *out = 0;
417 for (size_t i = 0; self->subtransforms[i].transform; i++) {
418 bithenge_transform_t *subxform =
419 self->subtransforms[i].transform;
420 aoff64_t sub_size;
421 rc = bithenge_transform_prefix_length(subxform, blob, &sub_size);
422 if (rc != EOK)
423 goto error;
424 *out += sub_size;
425 rc = bithenge_new_offset_blob(&node, blob, sub_size);
426 blob = NULL;
427 if (rc != EOK)
428 goto error;
429 blob = bithenge_node_as_blob(node);
430 }
431error:
432 bithenge_blob_dec_ref(blob);
433 return EOK;
434}
435
436static void free_subtransforms(bithenge_named_transform_t *subtransforms)
437{
438 for (size_t i = 0; subtransforms[i].transform; i++) {
439 free((void *)subtransforms[i].name);
440 bithenge_transform_dec_ref(subtransforms[i].transform);
441 }
442 free(subtransforms);
443}
444
445static void struct_transform_destroy(bithenge_transform_t *base)
446{
447 struct_transform_t *self = transform_as_struct(base);
448 free_subtransforms(self->subtransforms);
449 free(self);
450}
451
452static bithenge_transform_ops_t struct_transform_ops = {
453 .apply = struct_transform_apply,
454 .prefix_length = struct_transform_prefix_length,
455 .destroy = struct_transform_destroy,
456};
457
458/** Create a struct transform. The transform will apply its subtransforms
459 * sequentially to a blob to create an internal node. Each result is either
460 * given a key from @a subtransforms or, if the name is NULL, the result's keys
461 * and values are merged into the struct transform's result. This function
462 * takes ownership of @a subtransforms and the names and references therein.
463 * @param[out] out Stores the created transform.
464 * @param subtransforms The subtransforms and field names.
465 * @return EOK on success or an error code from errno.h. */
466int bithenge_new_struct(bithenge_transform_t **out,
467 bithenge_named_transform_t *subtransforms)
468{
469 int rc;
470 struct_transform_t *self = malloc(sizeof(*self));
471 if (!self) {
472 rc = ENOMEM;
473 goto error;
474 }
475 rc = bithenge_init_transform(struct_as_transform(self),
476 &struct_transform_ops);
477 if (rc != EOK)
478 goto error;
479 self->subtransforms = subtransforms;
480 *out = struct_as_transform(self);
481 return EOK;
482error:
483 free_subtransforms(subtransforms);
484 free(self);
485 return rc;
486}
487
488typedef struct {
489 bithenge_transform_t base;
490 bithenge_transform_t **xforms;
491 size_t num;
492} compose_transform_t;
493
494static bithenge_transform_t *compose_as_transform(compose_transform_t *xform)
495{
496 return &xform->base;
497}
498
499static compose_transform_t *transform_as_compose(bithenge_transform_t *xform)
500{
501 return (compose_transform_t *)xform;
502}
503
504static int compose_apply(bithenge_transform_t *base, bithenge_node_t *in,
505 bithenge_node_t **out)
506{
507 int rc;
508 compose_transform_t *self = transform_as_compose(base);
509 bithenge_node_inc_ref(in);
510
511 /* i ranges from (self->num - 1) to 0 inside the loop. */
512 for (size_t i = self->num; i--; ) {
513 bithenge_node_t *tmp;
514 rc = bithenge_transform_apply(self->xforms[i], in, &tmp);
515 bithenge_node_dec_ref(in);
516 if (rc != EOK)
517 return rc;
518 in = tmp;
519 }
520
521 *out = in;
522 return rc;
523}
524
525static int compose_prefix_length(bithenge_transform_t *base,
526 bithenge_blob_t *blob, aoff64_t *out)
527{
528 compose_transform_t *self = transform_as_compose(base);
529 return bithenge_transform_prefix_length(self->xforms[self->num - 1],
530 blob, out);
531}
532
533static void compose_destroy(bithenge_transform_t *base)
534{
535 compose_transform_t *self = transform_as_compose(base);
536 for (size_t i = 0; i < self->num; i++)
537 bithenge_transform_dec_ref(self->xforms[i]);
538 free(self->xforms);
539 free(self);
540}
541
542static const bithenge_transform_ops_t compose_transform_ops = {
543 .apply = compose_apply,
544 .prefix_length = compose_prefix_length,
545 .destroy = compose_destroy,
546};
547
548/** Create a composition of multiple transforms. When the result is applied to a
549 * node, each transform is applied in turn, with the last transform applied
550 * first. @a xforms may contain any number of transforms or no transforms at
551 * all. This function takes ownership of @a xforms and the references therein.
552 * @param[out] out Holds the result.
553 * @param[in] xforms The transforms to apply.
554 * @param num The number of transforms.
555 * @return EOK on success or an error code from errno.h. */
556int bithenge_new_composed_transform(bithenge_transform_t **out,
557 bithenge_transform_t **xforms, size_t num)
558{
559 if (num == 0) {
560 /* TODO: optimize */
561 } else if (num == 1) {
562 *out = xforms[0];
563 free(xforms);
564 return EOK;
565 }
566
567 int rc;
568 compose_transform_t *self = malloc(sizeof(*self));
569 if (!self) {
570 rc = ENOMEM;
571 goto error;
572 }
573 rc = bithenge_init_transform(compose_as_transform(self),
574 &compose_transform_ops);
575 if (rc != EOK)
576 goto error;
577 self->xforms = xforms;
578 self->num = num;
579 *out = compose_as_transform(self);
580 return EOK;
581error:
582 for (size_t i = 0; i < num; i++)
583 bithenge_transform_dec_ref(xforms[i]);
584 free(xforms);
585 free(self);
586 return rc;
587}
588
589/** @}
590 */
Note: See TracBrowser for help on using the repository browser.