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
RevLine 
[d5070ef]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
[0d1a8fd]37#include <assert.h>
[d5070ef]38#include <errno.h>
[0d1a8fd]39#include <stdlib.h>
[d5070ef]40#include "blob.h"
41#include "transform.h"
42
[04a7435f]43/** Initialize a new transform.
[978ccaf1]44 * @param[out] self Transform to initialize.
[04a7435f]45 * @param[in] ops Operations provided by the transform.
46 * @return EOK or an error code from errno.h. */
[978ccaf1]47int bithenge_init_transform(bithenge_transform_t *self,
[04a7435f]48 const bithenge_transform_ops_t *ops)
49{
50 assert(ops);
51 assert(ops->apply);
52 assert(ops->destroy);
[978ccaf1]53 self->ops = ops;
54 self->refs = 1;
[04a7435f]55 return EOK;
56}
57
[978ccaf1]58static void transform_indestructible(bithenge_transform_t *self)
[d5070ef]59{
[0d1a8fd]60 assert(false);
[d5070ef]61}
62
[600f5d1]63static int ascii_apply(bithenge_transform_t *self,
[43788b2]64 bithenge_transform_context_t *context, bithenge_node_t *in,
65 bithenge_node_t **out)
[600f5d1]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
[43788b2]105static int prefix_length_1(bithenge_transform_t *self,
106 bithenge_transform_context_t *context, bithenge_blob_t *blob,
[5f4cf872]107 aoff64_t *out)
[d5070ef]108{
[5f4cf872]109 *out = 1;
110 return EOK;
[d5070ef]111}
112
[43788b2]113static int prefix_length_2(bithenge_transform_t *self,
114 bithenge_transform_context_t *context, bithenge_blob_t *blob,
[5f4cf872]115 aoff64_t *out)
[0d1a8fd]116{
[5f4cf872]117 *out = 2;
118 return EOK;
[0d1a8fd]119}
120
[43788b2]121static int prefix_length_4(bithenge_transform_t *self,
122 bithenge_transform_context_t *context, bithenge_blob_t *blob,
[0d1a8fd]123 aoff64_t *out)
[d5070ef]124{
125 *out = 4;
126 return EOK;
127}
128
[43788b2]129static int prefix_length_8(bithenge_transform_t *self,
130 bithenge_transform_context_t *context, bithenge_blob_t *blob,
[5f4cf872]131 aoff64_t *out)
132{
133 *out = 8;
134 return EOK;
135}
[d5070ef]136
[5f4cf872]137#define MAKE_UINT_TRANSFORM(NAME, TYPE, ENDIAN, PREFIX_LENGTH_FUNC) \
138 static int NAME##_apply(bithenge_transform_t *self, \
[43788b2]139 bithenge_transform_context_t *context, bithenge_node_t *in, \
140 bithenge_node_t **out) \
[5f4cf872]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 }
[d5070ef]168
[5f4cf872]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);
[0d1a8fd]176
[600f5d1]177static int zero_terminated_apply(bithenge_transform_t *self,
[43788b2]178 bithenge_transform_context_t *context, bithenge_node_t *in,
179 bithenge_node_t **out)
[600f5d1]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,
[43788b2]203 bithenge_transform_context_t *context, bithenge_blob_t *blob,
204 aoff64_t *out)
[600f5d1]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
[0d1a8fd]234static bithenge_named_transform_t primitive_transforms[] = {
[600f5d1]235 {"ascii", &bithenge_ascii_transform},
[5f4cf872]236 {"uint8", &bithenge_uint8_transform},
237 {"uint16le", &bithenge_uint16le_transform},
238 {"uint16be", &bithenge_uint16be_transform},
[0d1a8fd]239 {"uint32le", &bithenge_uint32le_transform},
240 {"uint32be", &bithenge_uint32be_transform},
[5f4cf872]241 {"uint64le", &bithenge_uint64le_transform},
242 {"uint64be", &bithenge_uint64be_transform},
[600f5d1]243 {"zero_terminated", &bithenge_zero_terminated_transform},
[0d1a8fd]244 {NULL, NULL}
245};
246
247/** An array of named built-in transforms. */
248bithenge_named_transform_t *bithenge_primitive_transforms = primitive_transforms;
[d5070ef]249
[04a7435f]250typedef struct {
251 bithenge_node_t base;
252 struct struct_transform *transform;
[43788b2]253 bithenge_transform_context_t *context;
[04a7435f]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,
[43788b2]283 bithenge_transform_t *subxform, bithenge_transform_context_t *context,
284 bithenge_blob_t **blob, bithenge_for_each_func_t func, void *data)
[04a7435f]285{
286 int rc;
287 bithenge_node_t *subxform_result = NULL;
288
289 aoff64_t sub_size;
[43788b2]290 rc = bithenge_transform_prefix_length(subxform, context, *blob,
291 &sub_size);
[04a7435f]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
[43788b2]301 rc = bithenge_transform_apply(subxform, context, subblob_node,
[04a7435f]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,
[43788b2]359 subxforms[i].transform, struct_node->context, &blob, func,
360 data);
[04a7435f]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
[978ccaf1]379static void struct_node_destroy(bithenge_node_t *base)
[04a7435f]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
[978ccaf1]392static int struct_transform_apply(bithenge_transform_t *base,
[43788b2]393 bithenge_transform_context_t *context, bithenge_node_t *in,
394 bithenge_node_t **out)
[04a7435f]395{
[978ccaf1]396 struct_transform_t *self = transform_as_struct(base);
[04a7435f]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 }
[978ccaf1]408 bithenge_transform_inc_ref(base);
409 node->transform = self;
[04a7435f]410 bithenge_node_inc_ref(in);
[43788b2]411 node->context = context;
[04a7435f]412 node->blob = bithenge_node_as_blob(in);
413 *out = struct_as_node(node);
414 return EOK;
415}
416
[978ccaf1]417static int struct_transform_prefix_length(bithenge_transform_t *base,
[43788b2]418 bithenge_transform_context_t *context, bithenge_blob_t *blob,
419 aoff64_t *out)
[04a7435f]420{
[978ccaf1]421 struct_transform_t *self = transform_as_struct(base);
[04a7435f]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;
[978ccaf1]431 for (size_t i = 0; self->subtransforms[i].transform; i++) {
[04a7435f]432 bithenge_transform_t *subxform =
[978ccaf1]433 self->subtransforms[i].transform;
[04a7435f]434 aoff64_t sub_size;
[43788b2]435 rc = bithenge_transform_prefix_length(subxform, context, blob,
436 &sub_size);
[04a7435f]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
[978ccaf1]460static void struct_transform_destroy(bithenge_transform_t *base)
[04a7435f]461{
[978ccaf1]462 struct_transform_t *self = transform_as_struct(base);
463 free_subtransforms(self->subtransforms);
464 free(self);
[04a7435f]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;
[600f5d1]485 struct_transform_t *self = malloc(sizeof(*self));
[978ccaf1]486 if (!self) {
[04a7435f]487 rc = ENOMEM;
488 goto error;
489 }
[978ccaf1]490 rc = bithenge_init_transform(struct_as_transform(self),
[04a7435f]491 &struct_transform_ops);
492 if (rc != EOK)
493 goto error;
[978ccaf1]494 self->subtransforms = subtransforms;
495 *out = struct_as_transform(self);
[04a7435f]496 return EOK;
497error:
498 free_subtransforms(subtransforms);
[978ccaf1]499 free(self);
[04a7435f]500 return rc;
501}
502
[600f5d1]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
[43788b2]519static int compose_apply(bithenge_transform_t *base,
520 bithenge_transform_context_t *context, bithenge_node_t *in,
[600f5d1]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;
[43788b2]530 rc = bithenge_transform_apply(self->xforms[i], context, in,
531 &tmp);
[600f5d1]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,
[43788b2]543 bithenge_transform_context_t *context, bithenge_blob_t *blob,
544 aoff64_t *out)
[600f5d1]545{
546 compose_transform_t *self = transform_as_compose(base);
547 return bithenge_transform_prefix_length(self->xforms[self->num - 1],
[43788b2]548 context, blob, out);
[600f5d1]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
[d5070ef]607/** @}
608 */
Note: See TracBrowser for help on using the repository browser.