source: mainline/uspace/app/bithenge/transform.c@ 43788b2

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

Bithenge: add transform context in preparation for parameters

  • Property mode set to 100644
File size: 17.5 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_transform_context_t *context, bithenge_node_t *in,
65 bithenge_node_t **out)
66{
67 int rc;
68 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
69 return EINVAL;
70 bithenge_blob_t *blob = bithenge_node_as_blob(in);
71 aoff64_t size;
72 rc = bithenge_blob_size(blob, &size);
73 if (rc != EOK)
74 return rc;
75
76 char *buffer = malloc(size + 1);
77 if (!buffer)
78 return ENOMEM;
79 aoff64_t size_read = size;
80 rc = bithenge_blob_read(blob, 0, buffer, &size_read);
81 if (rc != EOK) {
82 free(buffer);
83 return rc;
84 }
85 if (size_read != size) {
86 free(buffer);
87 return EINVAL;
88 }
89 buffer[size] = '\0';
90
91 /* TODO: what if the OS encoding is incompatible with ASCII? */
92 return bithenge_new_string_node(out, buffer, true);
93}
94
95static const bithenge_transform_ops_t ascii_ops = {
96 .apply = ascii_apply,
97 .destroy = transform_indestructible,
98};
99
100/** The ASCII text transform. */
101bithenge_transform_t bithenge_ascii_transform = {
102 &ascii_ops, 1
103};
104
105static int prefix_length_1(bithenge_transform_t *self,
106 bithenge_transform_context_t *context, bithenge_blob_t *blob,
107 aoff64_t *out)
108{
109 *out = 1;
110 return EOK;
111}
112
113static int prefix_length_2(bithenge_transform_t *self,
114 bithenge_transform_context_t *context, bithenge_blob_t *blob,
115 aoff64_t *out)
116{
117 *out = 2;
118 return EOK;
119}
120
121static int prefix_length_4(bithenge_transform_t *self,
122 bithenge_transform_context_t *context, bithenge_blob_t *blob,
123 aoff64_t *out)
124{
125 *out = 4;
126 return EOK;
127}
128
129static int prefix_length_8(bithenge_transform_t *self,
130 bithenge_transform_context_t *context, bithenge_blob_t *blob,
131 aoff64_t *out)
132{
133 *out = 8;
134 return EOK;
135}
136
137#define MAKE_UINT_TRANSFORM(NAME, TYPE, ENDIAN, PREFIX_LENGTH_FUNC) \
138 static int NAME##_apply(bithenge_transform_t *self, \
139 bithenge_transform_context_t *context, bithenge_node_t *in, \
140 bithenge_node_t **out) \
141 { \
142 int rc; \
143 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB) \
144 return EINVAL; \
145 bithenge_blob_t *blob = bithenge_node_as_blob(in); \
146 \
147 /* Read too many bytes; success means the blob is too long. */ \
148 TYPE val[2]; \
149 aoff64_t size = sizeof(val[0]) + 1; \
150 rc = bithenge_blob_read(blob, 0, (char *)val, &size); \
151 if (rc != EOK) \
152 return rc; \
153 if (size != sizeof(val[0])) \
154 return EINVAL; \
155 \
156 return bithenge_new_integer_node(out, ENDIAN(val[0])); \
157 } \
158 \
159 static const bithenge_transform_ops_t NAME##_ops = { \
160 .apply = NAME##_apply, \
161 .prefix_length = PREFIX_LENGTH_FUNC, \
162 .destroy = transform_indestructible, \
163 }; \
164 \
165 bithenge_transform_t bithenge_##NAME##_transform = { \
166 &NAME##_ops, 1 \
167 }
168
169MAKE_UINT_TRANSFORM(uint8 , uint8_t , , prefix_length_1);
170MAKE_UINT_TRANSFORM(uint16le, uint16_t, uint16_t_le2host, prefix_length_2);
171MAKE_UINT_TRANSFORM(uint16be, uint16_t, uint16_t_be2host, prefix_length_2);
172MAKE_UINT_TRANSFORM(uint32le, uint32_t, uint32_t_le2host, prefix_length_4);
173MAKE_UINT_TRANSFORM(uint32be, uint32_t, uint32_t_be2host, prefix_length_4);
174MAKE_UINT_TRANSFORM(uint64le, uint64_t, uint32_t_le2host, prefix_length_8);
175MAKE_UINT_TRANSFORM(uint64be, uint64_t, uint32_t_be2host, prefix_length_8);
176
177static int zero_terminated_apply(bithenge_transform_t *self,
178 bithenge_transform_context_t *context, bithenge_node_t *in,
179 bithenge_node_t **out)
180{
181 int rc;
182 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
183 return EINVAL;
184 bithenge_blob_t *blob = bithenge_node_as_blob(in);
185 aoff64_t size;
186 rc = bithenge_blob_size(blob, &size);
187 if (rc != EOK)
188 return rc;
189 if (size < 1)
190 return EINVAL;
191 char ch;
192 aoff64_t size_read = 1;
193 rc = bithenge_blob_read(blob, size - 1, &ch, &size_read);
194 if (rc != EOK)
195 return rc;
196 if (size_read != 1 || ch != '\0')
197 return EINVAL;
198 bithenge_blob_inc_ref(blob);
199 return bithenge_new_subblob(out, blob, 0, size - 1);
200}
201
202static int zero_terminated_prefix_length(bithenge_transform_t *self,
203 bithenge_transform_context_t *context, bithenge_blob_t *blob,
204 aoff64_t *out)
205{
206 int rc;
207 char buffer[4096];
208 aoff64_t offset = 0, size_read = sizeof(buffer);
209 do {
210 rc = bithenge_blob_read(blob, offset, buffer, &size_read);
211 if (rc != EOK)
212 return rc;
213 char *found = memchr(buffer, '\0', size_read);
214 if (found) {
215 *out = found - buffer + offset + 1;
216 return EOK;
217 }
218 offset += size_read;
219 } while (size_read == sizeof(buffer));
220 return EINVAL;
221}
222
223static const bithenge_transform_ops_t zero_terminated_ops = {
224 .apply = zero_terminated_apply,
225 .prefix_length = zero_terminated_prefix_length,
226 .destroy = transform_indestructible,
227};
228
229/** The zero-terminated data transform. */
230bithenge_transform_t bithenge_zero_terminated_transform = {
231 &zero_terminated_ops, 1
232};
233
234static bithenge_named_transform_t primitive_transforms[] = {
235 {"ascii", &bithenge_ascii_transform},
236 {"uint8", &bithenge_uint8_transform},
237 {"uint16le", &bithenge_uint16le_transform},
238 {"uint16be", &bithenge_uint16be_transform},
239 {"uint32le", &bithenge_uint32le_transform},
240 {"uint32be", &bithenge_uint32be_transform},
241 {"uint64le", &bithenge_uint64le_transform},
242 {"uint64be", &bithenge_uint64be_transform},
243 {"zero_terminated", &bithenge_zero_terminated_transform},
244 {NULL, NULL}
245};
246
247/** An array of named built-in transforms. */
248bithenge_named_transform_t *bithenge_primitive_transforms = primitive_transforms;
249
250typedef struct {
251 bithenge_node_t base;
252 struct struct_transform *transform;
253 bithenge_transform_context_t *context;
254 bithenge_blob_t *blob;
255} struct_node_t;
256
257typedef struct struct_transform {
258 bithenge_transform_t base;
259 bithenge_named_transform_t *subtransforms;
260} struct_transform_t;
261
262static bithenge_node_t *struct_as_node(struct_node_t *node)
263{
264 return &node->base;
265}
266
267static struct_node_t *node_as_struct(bithenge_node_t *node)
268{
269 return (struct_node_t *)node;
270}
271
272static bithenge_transform_t *struct_as_transform(struct_transform_t *xform)
273{
274 return &xform->base;
275}
276
277static struct_transform_t *transform_as_struct(bithenge_transform_t *xform)
278{
279 return (struct_transform_t *)xform;
280}
281
282static int struct_node_for_one(const char *name,
283 bithenge_transform_t *subxform, bithenge_transform_context_t *context,
284 bithenge_blob_t **blob, bithenge_for_each_func_t func, void *data)
285{
286 int rc;
287 bithenge_node_t *subxform_result = NULL;
288
289 aoff64_t sub_size;
290 rc = bithenge_transform_prefix_length(subxform, context, *blob,
291 &sub_size);
292 if (rc != EOK)
293 goto error;
294
295 bithenge_node_t *subblob_node;
296 bithenge_blob_inc_ref(*blob);
297 rc = bithenge_new_subblob(&subblob_node, *blob, 0, sub_size);
298 if (rc != EOK)
299 goto error;
300
301 rc = bithenge_transform_apply(subxform, context, subblob_node,
302 &subxform_result);
303 bithenge_node_dec_ref(subblob_node);
304 if (rc != EOK)
305 goto error;
306
307 if (name) {
308 bithenge_node_t *name_node;
309 rc = bithenge_new_string_node(&name_node, name, false);
310 if (rc != EOK)
311 goto error;
312 rc = func(name_node, subxform_result, data);
313 subxform_result = NULL;
314 if (rc != EOK)
315 goto error;
316 } else {
317 if (bithenge_node_type(subxform_result) !=
318 BITHENGE_NODE_INTERNAL) {
319 rc = EINVAL;
320 goto error;
321 }
322 rc = bithenge_node_for_each(subxform_result, func, data);
323 if (rc != EOK)
324 goto error;
325 }
326
327 bithenge_node_t *blob_node;
328 rc = bithenge_new_offset_blob(&blob_node, *blob, sub_size);
329 *blob = NULL;
330 if (rc != EOK)
331 goto error;
332 *blob = bithenge_node_as_blob(blob_node);
333
334error:
335 bithenge_node_dec_ref(subxform_result);
336 return rc;
337}
338
339static int struct_node_for_each(bithenge_node_t *base,
340 bithenge_for_each_func_t func, void *data)
341{
342 int rc = EOK;
343 struct_node_t *struct_node = node_as_struct(base);
344 bithenge_named_transform_t *subxforms =
345 struct_node->transform->subtransforms;
346
347 bithenge_node_t *blob_node = NULL;
348 bithenge_blob_t *blob = NULL;
349 bithenge_blob_inc_ref(struct_node->blob);
350 rc = bithenge_new_offset_blob(&blob_node, struct_node->blob, 0);
351 if (rc != EOK) {
352 blob = NULL;
353 goto error;
354 }
355 blob = bithenge_node_as_blob(blob_node);
356
357 for (size_t i = 0; subxforms[i].transform; i++) {
358 rc = struct_node_for_one(subxforms[i].name,
359 subxforms[i].transform, struct_node->context, &blob, func,
360 data);
361 if (rc != EOK)
362 goto error;
363 }
364
365 aoff64_t remaining;
366 rc = bithenge_blob_size(blob, &remaining);
367 if (rc != EOK)
368 goto error;
369 if (remaining != 0) {
370 rc = EINVAL;
371 goto error;
372 }
373
374error:
375 bithenge_blob_dec_ref(blob);
376 return rc;
377}
378
379static void struct_node_destroy(bithenge_node_t *base)
380{
381 struct_node_t *node = node_as_struct(base);
382 bithenge_transform_dec_ref(struct_as_transform(node->transform));
383 bithenge_blob_dec_ref(node->blob);
384 free(node);
385}
386
387static const bithenge_internal_node_ops_t struct_node_ops = {
388 .for_each = struct_node_for_each,
389 .destroy = struct_node_destroy,
390};
391
392static int struct_transform_apply(bithenge_transform_t *base,
393 bithenge_transform_context_t *context, bithenge_node_t *in,
394 bithenge_node_t **out)
395{
396 struct_transform_t *self = transform_as_struct(base);
397 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
398 return EINVAL;
399 struct_node_t *node = malloc(sizeof(*node));
400 if (!node)
401 return ENOMEM;
402 int rc = bithenge_init_internal_node(struct_as_node(node),
403 &struct_node_ops);
404 if (rc != EOK) {
405 free(node);
406 return rc;
407 }
408 bithenge_transform_inc_ref(base);
409 node->transform = self;
410 bithenge_node_inc_ref(in);
411 node->context = context;
412 node->blob = bithenge_node_as_blob(in);
413 *out = struct_as_node(node);
414 return EOK;
415}
416
417static int struct_transform_prefix_length(bithenge_transform_t *base,
418 bithenge_transform_context_t *context, bithenge_blob_t *blob,
419 aoff64_t *out)
420{
421 struct_transform_t *self = transform_as_struct(base);
422 int rc = EOK;
423 bithenge_node_t *node;
424 bithenge_blob_inc_ref(blob);
425 rc = bithenge_new_offset_blob(&node, blob, 0);
426 blob = NULL;
427 if (rc != EOK)
428 goto error;
429 blob = bithenge_node_as_blob(node);
430 *out = 0;
431 for (size_t i = 0; self->subtransforms[i].transform; i++) {
432 bithenge_transform_t *subxform =
433 self->subtransforms[i].transform;
434 aoff64_t sub_size;
435 rc = bithenge_transform_prefix_length(subxform, context, blob,
436 &sub_size);
437 if (rc != EOK)
438 goto error;
439 *out += sub_size;
440 rc = bithenge_new_offset_blob(&node, blob, sub_size);
441 blob = NULL;
442 if (rc != EOK)
443 goto error;
444 blob = bithenge_node_as_blob(node);
445 }
446error:
447 bithenge_blob_dec_ref(blob);
448 return EOK;
449}
450
451static void free_subtransforms(bithenge_named_transform_t *subtransforms)
452{
453 for (size_t i = 0; subtransforms[i].transform; i++) {
454 free((void *)subtransforms[i].name);
455 bithenge_transform_dec_ref(subtransforms[i].transform);
456 }
457 free(subtransforms);
458}
459
460static void struct_transform_destroy(bithenge_transform_t *base)
461{
462 struct_transform_t *self = transform_as_struct(base);
463 free_subtransforms(self->subtransforms);
464 free(self);
465}
466
467static bithenge_transform_ops_t struct_transform_ops = {
468 .apply = struct_transform_apply,
469 .prefix_length = struct_transform_prefix_length,
470 .destroy = struct_transform_destroy,
471};
472
473/** Create a struct transform. The transform will apply its subtransforms
474 * sequentially to a blob to create an internal node. Each result is either
475 * given a key from @a subtransforms or, if the name is NULL, the result's keys
476 * and values are merged into the struct transform's result. This function
477 * takes ownership of @a subtransforms and the names and references therein.
478 * @param[out] out Stores the created transform.
479 * @param subtransforms The subtransforms and field names.
480 * @return EOK on success or an error code from errno.h. */
481int bithenge_new_struct(bithenge_transform_t **out,
482 bithenge_named_transform_t *subtransforms)
483{
484 int rc;
485 struct_transform_t *self = malloc(sizeof(*self));
486 if (!self) {
487 rc = ENOMEM;
488 goto error;
489 }
490 rc = bithenge_init_transform(struct_as_transform(self),
491 &struct_transform_ops);
492 if (rc != EOK)
493 goto error;
494 self->subtransforms = subtransforms;
495 *out = struct_as_transform(self);
496 return EOK;
497error:
498 free_subtransforms(subtransforms);
499 free(self);
500 return rc;
501}
502
503typedef struct {
504 bithenge_transform_t base;
505 bithenge_transform_t **xforms;
506 size_t num;
507} compose_transform_t;
508
509static bithenge_transform_t *compose_as_transform(compose_transform_t *xform)
510{
511 return &xform->base;
512}
513
514static compose_transform_t *transform_as_compose(bithenge_transform_t *xform)
515{
516 return (compose_transform_t *)xform;
517}
518
519static int compose_apply(bithenge_transform_t *base,
520 bithenge_transform_context_t *context, bithenge_node_t *in,
521 bithenge_node_t **out)
522{
523 int rc;
524 compose_transform_t *self = transform_as_compose(base);
525 bithenge_node_inc_ref(in);
526
527 /* i ranges from (self->num - 1) to 0 inside the loop. */
528 for (size_t i = self->num; i--; ) {
529 bithenge_node_t *tmp;
530 rc = bithenge_transform_apply(self->xforms[i], context, in,
531 &tmp);
532 bithenge_node_dec_ref(in);
533 if (rc != EOK)
534 return rc;
535 in = tmp;
536 }
537
538 *out = in;
539 return rc;
540}
541
542static int compose_prefix_length(bithenge_transform_t *base,
543 bithenge_transform_context_t *context, bithenge_blob_t *blob,
544 aoff64_t *out)
545{
546 compose_transform_t *self = transform_as_compose(base);
547 return bithenge_transform_prefix_length(self->xforms[self->num - 1],
548 context, blob, out);
549}
550
551static void compose_destroy(bithenge_transform_t *base)
552{
553 compose_transform_t *self = transform_as_compose(base);
554 for (size_t i = 0; i < self->num; i++)
555 bithenge_transform_dec_ref(self->xforms[i]);
556 free(self->xforms);
557 free(self);
558}
559
560static const bithenge_transform_ops_t compose_transform_ops = {
561 .apply = compose_apply,
562 .prefix_length = compose_prefix_length,
563 .destroy = compose_destroy,
564};
565
566/** Create a composition of multiple transforms. When the result is applied to a
567 * node, each transform is applied in turn, with the last transform applied
568 * first. @a xforms may contain any number of transforms or no transforms at
569 * all. This function takes ownership of @a xforms and the references therein.
570 * @param[out] out Holds the result.
571 * @param[in] xforms The transforms to apply.
572 * @param num The number of transforms.
573 * @return EOK on success or an error code from errno.h. */
574int bithenge_new_composed_transform(bithenge_transform_t **out,
575 bithenge_transform_t **xforms, size_t num)
576{
577 if (num == 0) {
578 /* TODO: optimize */
579 } else if (num == 1) {
580 *out = xforms[0];
581 free(xforms);
582 return EOK;
583 }
584
585 int rc;
586 compose_transform_t *self = malloc(sizeof(*self));
587 if (!self) {
588 rc = ENOMEM;
589 goto error;
590 }
591 rc = bithenge_init_transform(compose_as_transform(self),
592 &compose_transform_ops);
593 if (rc != EOK)
594 goto error;
595 self->xforms = xforms;
596 self->num = num;
597 *out = compose_as_transform(self);
598 return EOK;
599error:
600 for (size_t i = 0; i < num; i++)
601 bithenge_transform_dec_ref(xforms[i]);
602 free(xforms);
603 free(self);
604 return rc;
605}
606
607/** @}
608 */
Note: See TracBrowser for help on using the repository browser.