source: mainline/uspace/app/bithenge/transform.c@ b8d45e9e

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

Bithenge: make each transform definition have its own scope

  • Property mode set to 100644
File size: 33.0 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.
[03cad47]46 * @param num_params The number of parameters required. If this is nonzero, the
47 * transform will get its own context with parameters, probably provided by a
48 * param_wrapper. If this is zero, the existing outer context will be used with
[4056ad0]49 * whatever parameters it has, so they can be passed to any param_wrappers
50 * within.
[04a7435f]51 * @return EOK or an error code from errno.h. */
[978ccaf1]52int bithenge_init_transform(bithenge_transform_t *self,
[03cad47]53 const bithenge_transform_ops_t *ops, int num_params)
[04a7435f]54{
55 assert(ops);
[cb4a66d2]56 assert(ops->apply || ops->prefix_apply);
[04a7435f]57 assert(ops->destroy);
[978ccaf1]58 self->ops = ops;
59 self->refs = 1;
[03cad47]60 self->num_params = num_params;
[04a7435f]61 return EOK;
62}
63
[978ccaf1]64static void transform_indestructible(bithenge_transform_t *self)
[d5070ef]65{
[0d1a8fd]66 assert(false);
[d5070ef]67}
68
[cb4a66d2]69/** Apply a transform. Takes ownership of nothing.
70 * @memberof bithenge_transform_t
71 * @param self The transform.
72 * @param scope The scope.
73 * @param in The input tree.
74 * @param[out] out Where the output tree will be stored.
75 * @return EOK on success or an error code from errno.h. */
76int bithenge_transform_apply(bithenge_transform_t *self,
77 bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
78{
79 assert(self);
80 assert(self->ops);
81 if (self->ops->apply)
82 return self->ops->apply(self, scope, in, out);
83
84 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
85 return EINVAL;
86 aoff64_t self_size, whole_size;
87 int rc = bithenge_transform_prefix_apply(self, scope,
88 bithenge_node_as_blob(in), out, &self_size);
89 if (rc != EOK)
90 return rc;
91 rc = bithenge_blob_size(bithenge_node_as_blob(in), &whole_size);
92 if (rc == EOK && whole_size != self_size)
93 rc = EINVAL;
94 if (rc != EOK) {
95 bithenge_node_dec_ref(*out);
96 return rc;
97 }
98 return EOK;
99}
100
101/** Find the length of the prefix of a blob this transform can use as input. In
102 * other words, figure out how many bytes this transform will use up. This
103 * method is optional and can return an error, but it must succeed for struct
104 * subtransforms. Takes ownership of nothing.
105 * @memberof bithenge_transform_t
106 * @param self The transform.
107 * @param scope The scope.
108 * @param blob The blob.
109 * @param[out] out Where the prefix length will be stored.
110 * @return EOK on success, ENOTSUP if not supported, or another error code from
111 * errno.h. */
112int bithenge_transform_prefix_length(bithenge_transform_t *self,
113 bithenge_scope_t *scope, bithenge_blob_t *blob, aoff64_t *out)
114{
115 assert(self);
116 assert(self->ops);
117 if (self->ops->prefix_length)
118 return self->ops->prefix_length(self, scope, blob, out);
119 if (!self->ops->prefix_apply)
120 return ENOTSUP;
121
122 bithenge_node_t *node;
123 int rc = bithenge_transform_prefix_apply(self, scope, blob, &node,
124 out);
125 if (rc != EOK)
126 return rc;
127 bithenge_node_dec_ref(node);
128 return EOK;
129}
130
131/** Apply this transform to a prefix of a blob. In other words, feed as much of
132 * the blob into this transform as possible. Takes ownership of nothing.
133 * @memberof bithenge_transform_t
134 * @param self The transform.
135 * @param scope The scope.
136 * @param blob The blob.
137 * @param[out] out_node Holds the result of applying this transform to the
138 * prefix.
139 * @param[out] out_size Holds the size of the prefix.
140 * @return EOK on success, ENOTSUP if not supported, or another error code from
141 * errno.h. */
142int bithenge_transform_prefix_apply(bithenge_transform_t *self,
143 bithenge_scope_t *scope, bithenge_blob_t *blob, bithenge_node_t **out_node,
144 aoff64_t *out_size)
145{
146 assert(self);
147 assert(self->ops);
148 if (self->ops->prefix_apply)
149 return self->ops->prefix_apply(self, scope, blob, out_node,
150 out_size);
151 if (!self->ops->prefix_length)
152 return ENOTSUP;
153
154 int rc = bithenge_transform_prefix_length(self, scope, blob, out_size);
155 if (rc != EOK)
156 return rc;
157 bithenge_node_t *prefix_blob;
158 bithenge_blob_inc_ref(blob);
159 rc = bithenge_new_subblob(&prefix_blob, blob, 0, *out_size);
160 if (rc != EOK)
161 return rc;
162 rc = bithenge_transform_apply(self, scope, prefix_blob, out_node);
163 bithenge_node_dec_ref(prefix_blob);
164 return rc;
165}
166
[b8d45e9e]167/** Initialize a transform scope. It must be destroyed with @a
168 * bithenge_scope_destroy after it is used.
169 * @param[out] scope The scope to initialize. */
170void bithenge_scope_init(bithenge_scope_t *scope)
171{
172 scope->num_params = 0;
173 scope->params = NULL;
174 scope->current_node = NULL;
175}
176
177/** Destroy a transform scope.
178 * @param scope The scope to destroy.
179 * @return EOK on success or an error code from errno.h. */
180void bithenge_scope_destroy(bithenge_scope_t *scope)
181{
182 bithenge_node_dec_ref(scope->current_node);
183 for (int i = 0; i < scope->num_params; i++)
184 bithenge_node_dec_ref(scope->params[i]);
185 free(scope->params);
186}
187
188/** Copy a scope.
189 * @param[out] out The scope to fill in; must have been initialized with @a
190 * bithenge_scope_init.
191 * @param scope The scope to copy.
192 * @return EOK on success or an error code from errno.h. */
193int bithenge_scope_copy(bithenge_scope_t *out, bithenge_scope_t *scope)
194{
195 out->params = malloc(sizeof(*out->params) * scope->num_params);
196 if (!out->params)
197 return ENOMEM;
198 memcpy(out->params, scope->params, sizeof(*out->params) *
199 scope->num_params);
200 out->num_params = scope->num_params;
201 for (int i = 0; i < out->num_params; i++)
202 bithenge_node_inc_ref(out->params[i]);
203 out->current_node = scope->current_node;
204 if (out->current_node)
205 bithenge_node_inc_ref(out->current_node);
206 return EOK;
207}
208
209/** Set the current node being created. Takes a reference to @a node.
210 * @param scope The scope to set the current node in.
211 * @param node The current node being created, or NULL.
212 * @return EOK on success or an error code from errno.h. */
213void bithenge_scope_set_current_node(bithenge_scope_t *scope,
214 bithenge_node_t *node)
215{
216 bithenge_node_dec_ref(scope->current_node);
217 scope->current_node = node;
218}
219
220/** Get the current node being created, which may be NULL.
221 * @param scope The scope to get the current node from.
222 * @return The node being created, or NULL. */
223bithenge_node_t *bithenge_scope_get_current_node(bithenge_scope_t *scope)
224{
225 if (scope->current_node)
226 bithenge_node_inc_ref(scope->current_node);
227 return scope->current_node;
228}
229
230/** Allocate parameters. The parameters must then be set with @a
231 * bithenge_scope_set_param. This must not be called on a scope that already
232 * has parameters.
233 * @param scope The scope in which to allocate parameters.
234 * @param num_params The number of parameters to allocate.
235 * @return EOK on success or an error code from errno.h. */
236int bithenge_scope_alloc_params(bithenge_scope_t *scope, int num_params)
237{
238 scope->params = malloc(sizeof(*scope->params) * num_params);
239 if (!scope->params)
240 return ENOMEM;
241 scope->num_params = num_params;
242 for (int i = 0; i < num_params; i++)
243 scope->params[i] = NULL;
244 return EOK;
245}
246
247/** Set a parameter. Takes a reference to @a value. Note that range checking is
248 * not done in release builds.
249 * @param scope The scope in which to allocate parameters.
250 * @param i The index of the parameter to set.
251 * @param value The value to store in the parameter.
252 * @return EOK on success or an error code from errno.h. */
253int bithenge_scope_set_param( bithenge_scope_t *scope, int i,
254 bithenge_node_t *node)
255{
256 assert(scope);
257 assert(i >= 0 && i < scope->num_params);
258 scope->params[i] = node;
259 return EOK;
260}
261
262/** Get a parameter. Note that range checking is not done in release builds.
263 * @param scope The scope to get the parameter from.
264 * @param i The index of the parameter to set.
265 * @param[out] out Stores a new reference to the parameter.
266 * @return EOK on success or an error code from errno.h. */
267int bithenge_scope_get_param(bithenge_scope_t *scope, int i,
268 bithenge_node_t **out)
269{
270 assert(scope);
271 assert(i >= 0 && i < scope->num_params);
272 *out = scope->params[i];
273 bithenge_node_inc_ref(*out);
274 return EOK;
275}
276
[4056ad0]277typedef struct {
278 bithenge_transform_t base;
279 bithenge_transform_t *transform;
[b8d45e9e]280} scope_transform_t;
[4056ad0]281
[b8d45e9e]282static inline scope_transform_t *transform_as_param(
[4056ad0]283 bithenge_transform_t *base)
284{
[b8d45e9e]285 return (scope_transform_t *)base;
[4056ad0]286}
287
288static inline bithenge_transform_t *param_as_transform(
[b8d45e9e]289 scope_transform_t *self)
[4056ad0]290{
291 return &self->base;
292}
293
[b8d45e9e]294static int scope_transform_apply(bithenge_transform_t *base,
[4056ad0]295 bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
296{
[b8d45e9e]297 scope_transform_t *self = transform_as_param(base);
298 bithenge_scope_t inner_scope;
299 bithenge_scope_init(&inner_scope);
300 int rc = bithenge_scope_copy(&inner_scope, scope);
301 if (rc != EOK)
302 goto error;
303 bithenge_scope_set_current_node(&inner_scope, NULL);
304 rc = bithenge_transform_apply(self->transform, scope, in, out);
305error:
306 bithenge_scope_destroy(&inner_scope);
307 return rc;
[4056ad0]308}
309
[b8d45e9e]310static int scope_transform_prefix_length(bithenge_transform_t *base,
[4056ad0]311 bithenge_scope_t *scope, bithenge_blob_t *in, aoff64_t *out)
312{
[b8d45e9e]313 scope_transform_t *self = transform_as_param(base);
[4056ad0]314 return bithenge_transform_prefix_length(self->transform, scope, in,
315 out);
316}
317
[b8d45e9e]318static void scope_transform_destroy(bithenge_transform_t *base)
[4056ad0]319{
[b8d45e9e]320 scope_transform_t *self = transform_as_param(base);
[4056ad0]321 bithenge_transform_dec_ref(self->transform);
322 free(self);
323}
324
[b8d45e9e]325static const bithenge_transform_ops_t scope_transform_ops = {
326 .apply = scope_transform_apply,
327 .prefix_length = scope_transform_prefix_length,
328 .destroy = scope_transform_destroy,
[4056ad0]329};
330
[b8d45e9e]331/** Create a wrapper transform that creates a new outer scope. This ensures
332 * nothing from the transform's users is passed in, other than parameters. The
333 * wrapper may have a different value for num_params. Takes a reference to
334 * @a transform, which it will use for all operations.
[4056ad0]335 * @param[out] out Holds the created transform.
336 * @param transform The transform to wrap.
[b8d45e9e]337 * @param num_params The number of parameters to require, which may be 0.
[4056ad0]338 * @return EOK on success or an error code from errno.h. */
[b8d45e9e]339int bithenge_new_scope_transform(bithenge_transform_t **out,
[4056ad0]340 bithenge_transform_t *transform, int num_params)
341{
342 assert(transform);
343 assert(bithenge_transform_num_params(transform) == 0);
344
345 int rc;
[b8d45e9e]346 scope_transform_t *self = malloc(sizeof(*self));
[4056ad0]347 if (!self) {
348 rc = ENOMEM;
349 goto error;
350 }
351 rc = bithenge_init_transform(param_as_transform(self),
[b8d45e9e]352 &scope_transform_ops, num_params);
[4056ad0]353 if (rc != EOK)
354 goto error;
355 self->transform = transform;
356 *out = param_as_transform(self);
357 return EOK;
358error:
359 bithenge_transform_dec_ref(transform);
360 free(self);
361 return rc;
362}
363
[5a7c0e6]364static int ascii_apply(bithenge_transform_t *self, bithenge_scope_t *scope,
365 bithenge_node_t *in, bithenge_node_t **out)
[600f5d1]366{
367 int rc;
368 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
369 return EINVAL;
370 bithenge_blob_t *blob = bithenge_node_as_blob(in);
371 aoff64_t size;
372 rc = bithenge_blob_size(blob, &size);
373 if (rc != EOK)
374 return rc;
375
376 char *buffer = malloc(size + 1);
377 if (!buffer)
378 return ENOMEM;
379 aoff64_t size_read = size;
380 rc = bithenge_blob_read(blob, 0, buffer, &size_read);
381 if (rc != EOK) {
382 free(buffer);
383 return rc;
384 }
385 if (size_read != size) {
386 free(buffer);
387 return EINVAL;
388 }
389 buffer[size] = '\0';
390
391 /* TODO: what if the OS encoding is incompatible with ASCII? */
392 return bithenge_new_string_node(out, buffer, true);
393}
394
395static const bithenge_transform_ops_t ascii_ops = {
396 .apply = ascii_apply,
397 .destroy = transform_indestructible,
398};
399
400/** The ASCII text transform. */
401bithenge_transform_t bithenge_ascii_transform = {
[03cad47]402 &ascii_ops, 1, 0
[600f5d1]403};
404
[78d3a00]405static int invalid_apply(bithenge_transform_t *self, bithenge_scope_t *scope,
406 bithenge_node_t *in, bithenge_node_t **out)
407{
408 return EINVAL;
409}
410
411static const bithenge_transform_ops_t invalid_ops = {
412 .apply = invalid_apply,
413 .destroy = transform_indestructible,
414};
415
416/** A transform that always raises an error. */
417bithenge_transform_t bithenge_invalid_transform = {
418 &invalid_ops, 1, 0
419};
420
[4056ad0]421static int known_length_apply(bithenge_transform_t *self,
422 bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
423{
424 bithenge_node_t *length_node;
425 int rc = bithenge_scope_get_param(scope, 0, &length_node);
426 if (rc != EOK)
427 return rc;
428 if (bithenge_node_type(length_node) != BITHENGE_NODE_INTEGER) {
429 bithenge_node_dec_ref(length_node);
430 return EINVAL;
431 }
432 bithenge_int_t length = bithenge_integer_node_value(length_node);
433 bithenge_node_dec_ref(length_node);
434
435 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
436 return EINVAL;
437 aoff64_t size;
438 rc = bithenge_blob_size(bithenge_node_as_blob(in), &size);
439 if (rc != EOK)
440 return rc;
441 if (length != (bithenge_int_t)size)
442 return EINVAL;
443
444 bithenge_node_inc_ref(in);
445 *out = in;
446 return EOK;
447}
448
449static int known_length_prefix_length(bithenge_transform_t *self,
450 bithenge_scope_t *scope, bithenge_blob_t *in, aoff64_t *out)
451{
452 bithenge_node_t *length_node;
453 int rc = bithenge_scope_get_param(scope, 0, &length_node);
454 if (rc != EOK)
455 return rc;
456 if (bithenge_node_type(length_node) != BITHENGE_NODE_INTEGER) {
457 bithenge_node_dec_ref(length_node);
458 return EINVAL;
459 }
460 bithenge_int_t length = bithenge_integer_node_value(length_node);
461 bithenge_node_dec_ref(length_node);
462
463 *out = (aoff64_t)length;
464 return EOK;
465}
466
467static const bithenge_transform_ops_t known_length_ops = {
468 .apply = known_length_apply,
469 .prefix_length = known_length_prefix_length,
470 .destroy = transform_indestructible,
471};
472
473/** Pass through a blob, but require its length to equal the first argument. */
474bithenge_transform_t bithenge_known_length_transform = {
475 &known_length_ops, 1, 1
476};
477
[5a7c0e6]478static int prefix_length_1(bithenge_transform_t *self, bithenge_scope_t *scope,
479 bithenge_blob_t *blob, aoff64_t *out)
[d5070ef]480{
[5f4cf872]481 *out = 1;
482 return EOK;
[d5070ef]483}
484
[5a7c0e6]485static int prefix_length_2(bithenge_transform_t *self, bithenge_scope_t *scope,
486 bithenge_blob_t *blob, aoff64_t *out)
[0d1a8fd]487{
[5f4cf872]488 *out = 2;
489 return EOK;
[0d1a8fd]490}
491
[5a7c0e6]492static int prefix_length_4(bithenge_transform_t *self, bithenge_scope_t *scope,
493 bithenge_blob_t *blob, aoff64_t *out)
[d5070ef]494{
495 *out = 4;
496 return EOK;
497}
498
[5a7c0e6]499static int prefix_length_8(bithenge_transform_t *self, bithenge_scope_t *scope,
500 bithenge_blob_t *blob, aoff64_t *out)
[5f4cf872]501{
502 *out = 8;
503 return EOK;
504}
[d5070ef]505
[5f4cf872]506#define MAKE_UINT_TRANSFORM(NAME, TYPE, ENDIAN, PREFIX_LENGTH_FUNC) \
507 static int NAME##_apply(bithenge_transform_t *self, \
[5a7c0e6]508 bithenge_scope_t *scope, bithenge_node_t *in, \
[43788b2]509 bithenge_node_t **out) \
[5f4cf872]510 { \
511 int rc; \
512 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB) \
513 return EINVAL; \
514 bithenge_blob_t *blob = bithenge_node_as_blob(in); \
515 \
516 /* Read too many bytes; success means the blob is too long. */ \
517 TYPE val[2]; \
518 aoff64_t size = sizeof(val[0]) + 1; \
519 rc = bithenge_blob_read(blob, 0, (char *)val, &size); \
520 if (rc != EOK) \
521 return rc; \
522 if (size != sizeof(val[0])) \
523 return EINVAL; \
524 \
525 return bithenge_new_integer_node(out, ENDIAN(val[0])); \
526 } \
527 \
528 static const bithenge_transform_ops_t NAME##_ops = { \
529 .apply = NAME##_apply, \
530 .prefix_length = PREFIX_LENGTH_FUNC, \
531 .destroy = transform_indestructible, \
532 }; \
533 \
534 bithenge_transform_t bithenge_##NAME##_transform = { \
[03cad47]535 &NAME##_ops, 1, 0 \
[5f4cf872]536 }
[d5070ef]537
[5f4cf872]538MAKE_UINT_TRANSFORM(uint8 , uint8_t , , prefix_length_1);
539MAKE_UINT_TRANSFORM(uint16le, uint16_t, uint16_t_le2host, prefix_length_2);
540MAKE_UINT_TRANSFORM(uint16be, uint16_t, uint16_t_be2host, prefix_length_2);
541MAKE_UINT_TRANSFORM(uint32le, uint32_t, uint32_t_le2host, prefix_length_4);
542MAKE_UINT_TRANSFORM(uint32be, uint32_t, uint32_t_be2host, prefix_length_4);
543MAKE_UINT_TRANSFORM(uint64le, uint64_t, uint32_t_le2host, prefix_length_8);
544MAKE_UINT_TRANSFORM(uint64be, uint64_t, uint32_t_be2host, prefix_length_8);
[0d1a8fd]545
[600f5d1]546static int zero_terminated_apply(bithenge_transform_t *self,
[5a7c0e6]547 bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
[600f5d1]548{
549 int rc;
550 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
551 return EINVAL;
552 bithenge_blob_t *blob = bithenge_node_as_blob(in);
553 aoff64_t size;
554 rc = bithenge_blob_size(blob, &size);
555 if (rc != EOK)
556 return rc;
557 if (size < 1)
558 return EINVAL;
559 char ch;
560 aoff64_t size_read = 1;
561 rc = bithenge_blob_read(blob, size - 1, &ch, &size_read);
562 if (rc != EOK)
563 return rc;
564 if (size_read != 1 || ch != '\0')
565 return EINVAL;
566 bithenge_blob_inc_ref(blob);
567 return bithenge_new_subblob(out, blob, 0, size - 1);
568}
569
570static int zero_terminated_prefix_length(bithenge_transform_t *self,
[5a7c0e6]571 bithenge_scope_t *scope, bithenge_blob_t *blob, aoff64_t *out)
[600f5d1]572{
573 int rc;
574 char buffer[4096];
575 aoff64_t offset = 0, size_read = sizeof(buffer);
576 do {
577 rc = bithenge_blob_read(blob, offset, buffer, &size_read);
578 if (rc != EOK)
579 return rc;
580 char *found = memchr(buffer, '\0', size_read);
581 if (found) {
582 *out = found - buffer + offset + 1;
583 return EOK;
584 }
585 offset += size_read;
586 } while (size_read == sizeof(buffer));
587 return EINVAL;
588}
589
590static const bithenge_transform_ops_t zero_terminated_ops = {
591 .apply = zero_terminated_apply,
592 .prefix_length = zero_terminated_prefix_length,
593 .destroy = transform_indestructible,
594};
595
596/** The zero-terminated data transform. */
597bithenge_transform_t bithenge_zero_terminated_transform = {
[03cad47]598 &zero_terminated_ops, 1, 0
[600f5d1]599};
600
[0d1a8fd]601static bithenge_named_transform_t primitive_transforms[] = {
[600f5d1]602 {"ascii", &bithenge_ascii_transform},
[4056ad0]603 {"known_length", &bithenge_known_length_transform},
[5f4cf872]604 {"uint8", &bithenge_uint8_transform},
605 {"uint16le", &bithenge_uint16le_transform},
606 {"uint16be", &bithenge_uint16be_transform},
[0d1a8fd]607 {"uint32le", &bithenge_uint32le_transform},
608 {"uint32be", &bithenge_uint32be_transform},
[5f4cf872]609 {"uint64le", &bithenge_uint64le_transform},
610 {"uint64be", &bithenge_uint64be_transform},
[600f5d1]611 {"zero_terminated", &bithenge_zero_terminated_transform},
[0d1a8fd]612 {NULL, NULL}
613};
614
615/** An array of named built-in transforms. */
616bithenge_named_transform_t *bithenge_primitive_transforms = primitive_transforms;
[d5070ef]617
[b8d45e9e]618typedef struct {
619 bithenge_transform_t base;
620 bithenge_named_transform_t *subtransforms;
621 size_t num_subtransforms;
622} struct_transform_t;
623
[04a7435f]624typedef struct {
625 bithenge_node_t base;
[32eb01b]626 bithenge_scope_t scope;
[b8d45e9e]627 struct_transform_t *transform;
[04a7435f]628 bithenge_blob_t *blob;
[d7c8e39f]629 aoff64_t *ends;
630 size_t num_ends;
[84e8a70]631 bool prefix;
[04a7435f]632} struct_node_t;
633
634static bithenge_node_t *struct_as_node(struct_node_t *node)
635{
636 return &node->base;
637}
638
639static struct_node_t *node_as_struct(bithenge_node_t *node)
640{
641 return (struct_node_t *)node;
642}
643
644static bithenge_transform_t *struct_as_transform(struct_transform_t *xform)
645{
646 return &xform->base;
647}
648
649static struct_transform_t *transform_as_struct(bithenge_transform_t *xform)
650{
651 return (struct_transform_t *)xform;
652}
653
[d7c8e39f]654static int struct_node_field_offset(struct_node_t *self, aoff64_t *out,
655 size_t index)
[04a7435f]656{
[d7c8e39f]657 if (index == 0) {
658 *out = 0;
659 return EOK;
660 }
661 index--;
662 aoff64_t prev_offset =
663 self->num_ends ? self->ends[self->num_ends - 1] : 0;
664 for (; self->num_ends <= index; self->num_ends++) {
665 bithenge_node_t *subblob_node;
666 bithenge_blob_inc_ref(self->blob);
667 int rc = bithenge_new_offset_blob(&subblob_node, self->blob,
668 prev_offset);
669 if (rc != EOK)
670 return rc;
[04a7435f]671
[d7c8e39f]672 bithenge_blob_t *subblob = bithenge_node_as_blob(subblob_node);
673 aoff64_t field_size;
674 rc = bithenge_transform_prefix_length(
675 self->transform->subtransforms[self->num_ends].transform,
676 &self->scope, subblob, &field_size);
677 bithenge_node_dec_ref(subblob_node);
678 if (rc != EOK)
679 return rc;
[04a7435f]680
[d7c8e39f]681 prev_offset = self->ends[self->num_ends] =
682 prev_offset + field_size;
683 }
684 *out = self->ends[index];
685 return EOK;
686}
[04a7435f]687
[d7c8e39f]688static int struct_node_subtransform(struct_node_t *self, bithenge_node_t **out,
689 size_t index)
690{
[cb4a66d2]691 aoff64_t start_pos;
[d7c8e39f]692 int rc = struct_node_field_offset(self, &start_pos, index);
693 if (rc != EOK)
694 return rc;
[04a7435f]695
[cb4a66d2]696 if (index == self->num_ends) {
697 /* We can apply the subtransform and cache its prefix length at
698 * the same time. */
699 bithenge_node_t *blob_node;
700 bithenge_blob_inc_ref(self->blob);
701 rc = bithenge_new_offset_blob(&blob_node, self->blob,
702 start_pos);
703 if (rc != EOK)
704 return rc;
[04a7435f]705
[cb4a66d2]706 aoff64_t size;
707 rc = bithenge_transform_prefix_apply(
708 self->transform->subtransforms[index].transform,
709 &self->scope, bithenge_node_as_blob(blob_node), out,
710 &size);
711 bithenge_node_dec_ref(blob_node);
712 if (rc != EOK)
713 return rc;
714
715 self->ends[self->num_ends++] = start_pos + size;
716 } else {
717 aoff64_t end_pos;
718 int rc = struct_node_field_offset(self, &end_pos, index + 1);
719 if (rc != EOK)
720 return rc;
721
722 bithenge_node_t *blob_node;
723 bithenge_blob_inc_ref(self->blob);
724 rc = bithenge_new_subblob(&blob_node, self->blob, start_pos,
725 end_pos - start_pos);
726 if (rc != EOK)
727 return rc;
728
729 rc = bithenge_transform_apply(
730 self->transform->subtransforms[index].transform,
731 &self->scope, blob_node, out);
732 bithenge_node_dec_ref(blob_node);
733 if (rc != EOK)
734 return rc;
735 }
[d7c8e39f]736
737 return EOK;
[04a7435f]738}
739
740static int struct_node_for_each(bithenge_node_t *base,
741 bithenge_for_each_func_t func, void *data)
742{
743 int rc = EOK;
[d7c8e39f]744 struct_node_t *self = node_as_struct(base);
[04a7435f]745 bithenge_named_transform_t *subxforms =
[d7c8e39f]746 self->transform->subtransforms;
[04a7435f]747
748 for (size_t i = 0; subxforms[i].transform; i++) {
[d7c8e39f]749 bithenge_node_t *subxform_result;
750 rc = struct_node_subtransform(self, &subxform_result, i);
[04a7435f]751 if (rc != EOK)
[d7c8e39f]752 return rc;
753
754 if (subxforms[i].name) {
755 bithenge_node_t *name_node;
756 rc = bithenge_new_string_node(&name_node,
757 subxforms[i].name, false);
758 if (rc == EOK) {
759 rc = func(name_node, subxform_result, data);
760 subxform_result = NULL;
761 }
762 } else {
763 if (bithenge_node_type(subxform_result) !=
764 BITHENGE_NODE_INTERNAL) {
765 rc = EINVAL;
766 } else {
767 rc = bithenge_node_for_each(subxform_result,
768 func, data);
769 }
770 }
771 bithenge_node_dec_ref(subxform_result);
772 if (rc != EOK)
773 return rc;
[04a7435f]774 }
775
[d7c8e39f]776 if (!self->prefix) {
777 aoff64_t blob_size, end_pos;
778 rc = bithenge_blob_size(self->blob, &blob_size);
[84e8a70]779 if (rc != EOK)
[d7c8e39f]780 return rc;
781 rc = struct_node_field_offset(self, &end_pos,
782 self->transform->num_subtransforms);
783 if (rc != EOK)
784 return rc;
785 if (blob_size != end_pos) {
[84e8a70]786 rc = EINVAL;
[d7c8e39f]787 return rc;
[84e8a70]788 }
[04a7435f]789 }
790
791 return rc;
792}
793
[d7c8e39f]794static int struct_node_get(bithenge_node_t *base, bithenge_node_t *key,
795 bithenge_node_t **out)
796{
797 struct_node_t *self = node_as_struct(base);
798
799 if (bithenge_node_type(key) != BITHENGE_NODE_STRING) {
800 bithenge_node_dec_ref(key);
801 return ENOENT;
802 }
803 const char *name = bithenge_string_node_value(key);
804
805 for (size_t i = 0; self->transform->subtransforms[i].transform; i++) {
806 if (self->transform->subtransforms[i].name
807 && !str_cmp(name, self->transform->subtransforms[i].name)) {
808 bithenge_node_dec_ref(key);
809 return struct_node_subtransform(self, out, i);
810 }
811 }
812
813 for (size_t i = 0; self->transform->subtransforms[i].transform; i++) {
814 if (self->transform->subtransforms[i].name)
815 continue;
816 bithenge_node_t *subxform_result;
817 int rc = struct_node_subtransform(self, &subxform_result, i);
818 if (rc != EOK) {
819 bithenge_node_dec_ref(key);
820 return rc;
821 }
822 if (bithenge_node_type(subxform_result) !=
823 BITHENGE_NODE_INTERNAL) {
824 bithenge_node_dec_ref(subxform_result);
825 bithenge_node_dec_ref(key);
826 return EINVAL;
827 }
828 bithenge_node_inc_ref(key);
829 rc = bithenge_node_get(subxform_result, key, out);
830 bithenge_node_dec_ref(subxform_result);
831 if (rc != ENOENT) {
832 bithenge_node_dec_ref(key);
833 return rc;
834 }
835 }
836
837 bithenge_node_dec_ref(key);
838 return ENOENT;
839}
840
[978ccaf1]841static void struct_node_destroy(bithenge_node_t *base)
[04a7435f]842{
843 struct_node_t *node = node_as_struct(base);
[84e8a70]844
845 /* We didn't inc_ref for the scope in struct_transform_make_node, so
846 * make sure it doesn't try to dec_ref. */
847 node->scope.current_node = NULL;
848 bithenge_scope_destroy(&node->scope);
849
[04a7435f]850 bithenge_transform_dec_ref(struct_as_transform(node->transform));
851 bithenge_blob_dec_ref(node->blob);
[d7c8e39f]852 free(node->ends);
[04a7435f]853 free(node);
854}
855
856static const bithenge_internal_node_ops_t struct_node_ops = {
857 .for_each = struct_node_for_each,
[d7c8e39f]858 .get = struct_node_get,
[04a7435f]859 .destroy = struct_node_destroy,
860};
861
[84e8a70]862static int struct_transform_make_node(struct_transform_t *self,
863 bithenge_node_t **out, bithenge_scope_t *scope, bithenge_blob_t *blob,
864 bool prefix)
[04a7435f]865{
866 struct_node_t *node = malloc(sizeof(*node));
867 if (!node)
868 return ENOMEM;
[d7c8e39f]869
[32eb01b]870 bithenge_scope_init(&node->scope);
871 int rc = bithenge_scope_copy(&node->scope, scope);
872 if (rc != EOK) {
873 free(node);
874 return rc;
875 }
[d7c8e39f]876
877 node->ends = malloc(sizeof(*node->ends) * self->num_subtransforms);
878 if (!node->ends) {
879 bithenge_scope_destroy(&node->scope);
880 free(node);
881 return ENOMEM;
882 }
883
[32eb01b]884 rc = bithenge_init_internal_node(struct_as_node(node),
[04a7435f]885 &struct_node_ops);
886 if (rc != EOK) {
[32eb01b]887 bithenge_scope_destroy(&node->scope);
[d7c8e39f]888 free(node->ends);
[04a7435f]889 free(node);
890 return rc;
891 }
[d7c8e39f]892
[84e8a70]893 bithenge_transform_inc_ref(struct_as_transform(self));
894 bithenge_blob_inc_ref(blob);
[978ccaf1]895 node->transform = self;
[84e8a70]896 node->blob = blob;
897 node->prefix = prefix;
[d7c8e39f]898 node->num_ends = 0;
[04a7435f]899 *out = struct_as_node(node);
[84e8a70]900
901 /* We should inc_ref(*out) here, but that would make a cycle. Instead,
902 * we leave it 1 too low, so that when the only remaining use of *out
903 * is the scope, *out will be destroyed. Also see the comment in
904 * struct_node_destroy. */
[f85ca3f]905 bithenge_scope_set_current_node(&node->scope, *out);
[84e8a70]906
[04a7435f]907 return EOK;
908}
909
[84e8a70]910static int struct_transform_apply(bithenge_transform_t *base,
911 bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
912{
913 struct_transform_t *self = transform_as_struct(base);
914 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
915 return EINVAL;
916 return struct_transform_make_node(self, out, scope,
917 bithenge_node_as_blob(in), false);
918}
919
[978ccaf1]920static int struct_transform_prefix_length(bithenge_transform_t *base,
[5a7c0e6]921 bithenge_scope_t *scope, bithenge_blob_t *blob, aoff64_t *out)
[04a7435f]922{
[978ccaf1]923 struct_transform_t *self = transform_as_struct(base);
[84e8a70]924 bithenge_node_t *struct_node;
925 int rc = struct_transform_make_node(self, &struct_node, scope, blob,
926 true);
927 if (rc != EOK)
928 return rc;
929
[d7c8e39f]930 rc = struct_node_field_offset(node_as_struct(struct_node), out,
931 self->num_subtransforms);
[84e8a70]932 bithenge_node_dec_ref(struct_node);
933 return rc;
[04a7435f]934}
935
[cb4a66d2]936static int struct_transform_prefix_apply(bithenge_transform_t *base,
937 bithenge_scope_t *scope, bithenge_blob_t *blob, bithenge_node_t **out_node,
938 aoff64_t *out_size)
939{
940 struct_transform_t *self = transform_as_struct(base);
941 int rc = struct_transform_make_node(self, out_node, scope, blob,
942 true);
943 if (rc != EOK)
944 return rc;
945
946 rc = struct_node_field_offset(node_as_struct(*out_node), out_size,
947 self->num_subtransforms);
948 if (rc != EOK) {
949 bithenge_node_dec_ref(*out_node);
950 return rc;
951 }
952
953 return EOK;
954}
955
[04a7435f]956static void free_subtransforms(bithenge_named_transform_t *subtransforms)
957{
958 for (size_t i = 0; subtransforms[i].transform; i++) {
959 free((void *)subtransforms[i].name);
960 bithenge_transform_dec_ref(subtransforms[i].transform);
961 }
962 free(subtransforms);
963}
964
[978ccaf1]965static void struct_transform_destroy(bithenge_transform_t *base)
[04a7435f]966{
[978ccaf1]967 struct_transform_t *self = transform_as_struct(base);
968 free_subtransforms(self->subtransforms);
969 free(self);
[04a7435f]970}
971
972static bithenge_transform_ops_t struct_transform_ops = {
973 .apply = struct_transform_apply,
974 .prefix_length = struct_transform_prefix_length,
[cb4a66d2]975 .prefix_apply = struct_transform_prefix_apply,
[04a7435f]976 .destroy = struct_transform_destroy,
977};
978
979/** Create a struct transform. The transform will apply its subtransforms
980 * sequentially to a blob to create an internal node. Each result is either
981 * given a key from @a subtransforms or, if the name is NULL, the result's keys
982 * and values are merged into the struct transform's result. This function
983 * takes ownership of @a subtransforms and the names and references therein.
984 * @param[out] out Stores the created transform.
985 * @param subtransforms The subtransforms and field names.
986 * @return EOK on success or an error code from errno.h. */
987int bithenge_new_struct(bithenge_transform_t **out,
988 bithenge_named_transform_t *subtransforms)
989{
990 int rc;
[600f5d1]991 struct_transform_t *self = malloc(sizeof(*self));
[978ccaf1]992 if (!self) {
[04a7435f]993 rc = ENOMEM;
994 goto error;
995 }
[978ccaf1]996 rc = bithenge_init_transform(struct_as_transform(self),
[03cad47]997 &struct_transform_ops, 0);
[04a7435f]998 if (rc != EOK)
999 goto error;
[978ccaf1]1000 self->subtransforms = subtransforms;
[d7c8e39f]1001 self->num_subtransforms = 0;
1002 for (self->num_subtransforms = 0;
1003 subtransforms[self->num_subtransforms].transform;
1004 self->num_subtransforms++);
[978ccaf1]1005 *out = struct_as_transform(self);
[04a7435f]1006 return EOK;
1007error:
1008 free_subtransforms(subtransforms);
[978ccaf1]1009 free(self);
[04a7435f]1010 return rc;
1011}
1012
[600f5d1]1013typedef struct {
1014 bithenge_transform_t base;
1015 bithenge_transform_t **xforms;
1016 size_t num;
1017} compose_transform_t;
1018
1019static bithenge_transform_t *compose_as_transform(compose_transform_t *xform)
1020{
1021 return &xform->base;
1022}
1023
1024static compose_transform_t *transform_as_compose(bithenge_transform_t *xform)
1025{
1026 return (compose_transform_t *)xform;
1027}
1028
[5a7c0e6]1029static int compose_apply(bithenge_transform_t *base, bithenge_scope_t *scope,
1030 bithenge_node_t *in, bithenge_node_t **out)
[600f5d1]1031{
1032 int rc;
1033 compose_transform_t *self = transform_as_compose(base);
1034 bithenge_node_inc_ref(in);
1035
1036 /* i ranges from (self->num - 1) to 0 inside the loop. */
1037 for (size_t i = self->num; i--; ) {
1038 bithenge_node_t *tmp;
[5a7c0e6]1039 rc = bithenge_transform_apply(self->xforms[i], scope, in,
[43788b2]1040 &tmp);
[600f5d1]1041 bithenge_node_dec_ref(in);
1042 if (rc != EOK)
1043 return rc;
1044 in = tmp;
1045 }
1046
1047 *out = in;
1048 return rc;
1049}
1050
1051static int compose_prefix_length(bithenge_transform_t *base,
[5a7c0e6]1052 bithenge_scope_t *scope, bithenge_blob_t *blob, aoff64_t *out)
[600f5d1]1053{
1054 compose_transform_t *self = transform_as_compose(base);
1055 return bithenge_transform_prefix_length(self->xforms[self->num - 1],
[5a7c0e6]1056 scope, blob, out);
[600f5d1]1057}
1058
1059static void compose_destroy(bithenge_transform_t *base)
1060{
1061 compose_transform_t *self = transform_as_compose(base);
1062 for (size_t i = 0; i < self->num; i++)
1063 bithenge_transform_dec_ref(self->xforms[i]);
1064 free(self->xforms);
1065 free(self);
1066}
1067
1068static const bithenge_transform_ops_t compose_transform_ops = {
1069 .apply = compose_apply,
1070 .prefix_length = compose_prefix_length,
1071 .destroy = compose_destroy,
1072};
1073
1074/** Create a composition of multiple transforms. When the result is applied to a
1075 * node, each transform is applied in turn, with the last transform applied
1076 * first. @a xforms may contain any number of transforms or no transforms at
1077 * all. This function takes ownership of @a xforms and the references therein.
1078 * @param[out] out Holds the result.
1079 * @param[in] xforms The transforms to apply.
1080 * @param num The number of transforms.
1081 * @return EOK on success or an error code from errno.h. */
1082int bithenge_new_composed_transform(bithenge_transform_t **out,
1083 bithenge_transform_t **xforms, size_t num)
1084{
1085 if (num == 0) {
1086 /* TODO: optimize */
1087 } else if (num == 1) {
1088 *out = xforms[0];
1089 free(xforms);
1090 return EOK;
1091 }
1092
1093 int rc;
1094 compose_transform_t *self = malloc(sizeof(*self));
1095 if (!self) {
1096 rc = ENOMEM;
1097 goto error;
1098 }
1099 rc = bithenge_init_transform(compose_as_transform(self),
[03cad47]1100 &compose_transform_ops, 0);
[600f5d1]1101 if (rc != EOK)
1102 goto error;
1103 self->xforms = xforms;
1104 self->num = num;
1105 *out = compose_as_transform(self);
1106 return EOK;
1107error:
1108 for (size_t i = 0; i < num; i++)
1109 bithenge_transform_dec_ref(xforms[i]);
1110 free(xforms);
1111 free(self);
1112 return rc;
1113}
1114
[d5070ef]1115/** @}
1116 */
Note: See TracBrowser for help on using the repository browser.