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

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

Bithenge: rename transform parameters "scopes" and add room for parameters

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