source: mainline/uspace/app/bithenge/transform.c@ 6be4142

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

Bithenge: print transform errors; fixes and fat.bh improvements

  • Property mode set to 100644
File size: 31.1 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 <stdarg.h>
40#include <stdlib.h>
41#include "blob.h"
42#include "print.h"
43#include "transform.h"
44
45
46
47/***************** transform *****************/
48
49/** Initialize a new transform.
50 * @param[out] self Transform to initialize.
51 * @param[in] ops Operations provided by the transform.
52 * @param num_params The number of parameters required. If this is nonzero, the
53 * transform will get its own context with parameters, probably provided by a
54 * param_wrapper. If this is zero, the existing outer context will be used with
55 * whatever parameters it has, so they can be passed to any param_wrappers
56 * within.
57 * @return EOK or an error code from errno.h. */
58int bithenge_init_transform(bithenge_transform_t *self,
59 const bithenge_transform_ops_t *ops, int num_params)
60{
61 assert(ops);
62 assert(ops->apply || ops->prefix_apply);
63 assert(ops->destroy);
64 self->ops = ops;
65 self->refs = 1;
66 self->num_params = num_params;
67 return EOK;
68}
69
70static void transform_indestructible(bithenge_transform_t *self)
71{
72 assert(false);
73}
74
75/** Apply a transform. Takes ownership of nothing.
76 * @memberof bithenge_transform_t
77 * @param self The transform.
78 * @param scope The scope.
79 * @param in The input tree.
80 * @param[out] out Where the output tree will be stored.
81 * @return EOK on success or an error code from errno.h. */
82int bithenge_transform_apply(bithenge_transform_t *self,
83 bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
84{
85 assert(self);
86 assert(self->ops);
87 if (self->ops->apply)
88 return self->ops->apply(self, scope, in, out);
89
90 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
91 return EINVAL;
92 aoff64_t self_size, whole_size;
93 int rc = bithenge_transform_prefix_apply(self, scope,
94 bithenge_node_as_blob(in), out, &self_size);
95 if (rc != EOK)
96 return rc;
97 rc = bithenge_blob_size(bithenge_node_as_blob(in), &whole_size);
98 if (rc == EOK && whole_size != self_size)
99 rc = EINVAL;
100 if (rc != EOK) {
101 bithenge_node_dec_ref(*out);
102 return rc;
103 }
104 return EOK;
105}
106
107/** Find the length of the prefix of a blob this transform can use as input. In
108 * other words, figure out how many bytes this transform will use up. This
109 * method is optional and can return an error, but it must succeed for struct
110 * subtransforms. Takes ownership of nothing.
111 * @memberof bithenge_transform_t
112 * @param self The transform.
113 * @param scope The scope.
114 * @param blob The blob.
115 * @param[out] out Where the prefix length will be stored.
116 * @return EOK on success, ENOTSUP if not supported, or another error code from
117 * errno.h. */
118int bithenge_transform_prefix_length(bithenge_transform_t *self,
119 bithenge_scope_t *scope, bithenge_blob_t *blob, aoff64_t *out)
120{
121 assert(self);
122 assert(self->ops);
123 if (self->ops->prefix_length)
124 return self->ops->prefix_length(self, scope, blob, out);
125 if (!self->ops->prefix_apply)
126 return ENOTSUP;
127
128 bithenge_node_t *node;
129 int rc = bithenge_transform_prefix_apply(self, scope, blob, &node,
130 out);
131 if (rc != EOK)
132 return rc;
133 bithenge_node_dec_ref(node);
134 return EOK;
135}
136
137/** Apply this transform to a prefix of a blob. In other words, feed as much of
138 * the blob into this transform as possible. Takes ownership of nothing.
139 * @memberof bithenge_transform_t
140 * @param self The transform.
141 * @param scope The scope.
142 * @param blob The blob.
143 * @param[out] out_node Holds the result of applying this transform to the
144 * prefix.
145 * @param[out] out_size Holds the size of the prefix.
146 * @return EOK on success, ENOTSUP if not supported, or another error code from
147 * errno.h. */
148int bithenge_transform_prefix_apply(bithenge_transform_t *self,
149 bithenge_scope_t *scope, bithenge_blob_t *blob, bithenge_node_t **out_node,
150 aoff64_t *out_size)
151{
152 assert(self);
153 assert(self->ops);
154 if (self->ops->prefix_apply)
155 return self->ops->prefix_apply(self, scope, blob, out_node,
156 out_size);
157 if (!self->ops->prefix_length)
158 return ENOTSUP;
159
160 int rc = bithenge_transform_prefix_length(self, scope, blob, out_size);
161 if (rc != EOK)
162 return rc;
163 bithenge_node_t *prefix_blob;
164 bithenge_blob_inc_ref(blob);
165 rc = bithenge_new_subblob(&prefix_blob, blob, 0, *out_size);
166 if (rc != EOK)
167 return rc;
168 rc = bithenge_transform_apply(self, scope, prefix_blob, out_node);
169 bithenge_node_dec_ref(prefix_blob);
170 return rc;
171}
172
173
174
175/***************** scope *****************/
176
177/** Create a transform scope. It must be dereferenced with @a
178 * bithenge_scope_dec_ref after it is used. Takes ownership of nothing.
179 * @param[out] out Holds the new scope.
180 * @param outer The outer scope, or NULL.
181 * @return EOK on success or an error code from errno.h. */
182int bithenge_scope_new(bithenge_scope_t **out, bithenge_scope_t *outer)
183{
184 bithenge_scope_t *self = malloc(sizeof(*self));
185 if (!self)
186 return ENOMEM;
187 self->refs = 1;
188 if (outer)
189 bithenge_scope_inc_ref(outer);
190 self->outer = outer;
191 self->error = NULL;
192 self->barrier = false;
193 self->num_params = 0;
194 self->params = NULL;
195 self->current_node = NULL;
196 self->in_node = NULL;
197 *out = self;
198 return EOK;
199}
200
201/** Dereference a transform scope.
202 * @param self The scope to dereference. */
203void bithenge_scope_dec_ref(bithenge_scope_t *self)
204{
205 if (!self)
206 return;
207 if (--self->refs)
208 return;
209 bithenge_node_dec_ref(self->current_node);
210 for (int i = 0; i < self->num_params; i++)
211 bithenge_node_dec_ref(self->params[i]);
212 bithenge_scope_dec_ref(self->outer);
213 free(self->params);
214 free(self->error);
215 free(self);
216}
217
218/** Get the outer scope of a scope, which may be NULL.
219 * @param self The scope to examine.
220 * @return The outer scope, which may be NULL. */
221bithenge_scope_t *bithenge_scope_outer(bithenge_scope_t *self)
222{
223 return self->outer;
224}
225
226/** Get the error message stored in the scope, which may be NULL. The error
227 * message only exists as long as the scope does.
228 * @param scope The scope to get the error message from.
229 * @return The error message, or NULL. */
230const char *bithenge_scope_get_error(bithenge_scope_t *scope)
231{
232 return scope->error;
233}
234
235/** Set the error message for the scope. The error message is stored in the
236 * outermost scope, but if any scope already has an error message this error
237 * message is ignored.
238 * @param scope The scope.
239 * @param format The format string.
240 * @return EINVAL normally, or another error code from errno.h. */
241int bithenge_scope_error(bithenge_scope_t *scope, const char *format, ...)
242{
243 if (scope->error)
244 return EINVAL;
245 while (scope->outer) {
246 scope = scope->outer;
247 if (scope->error)
248 return EINVAL;
249 }
250 size_t space_left = 256;
251 scope->error = malloc(space_left);
252 if (!scope->error)
253 return ENOMEM;
254 char *out = scope->error;
255 va_list ap;
256 va_start(ap, format);
257
258 while (*format) {
259 if (format[0] == '%' && format[1] == 't') {
260 format += 2;
261 int rc = bithenge_print_node_to_string(&out,
262 &space_left, BITHENGE_PRINT_PYTHON,
263 va_arg(ap, bithenge_node_t *));
264 if (rc != EOK) {
265 va_end(ap);
266 return rc;
267 }
268 } else {
269 const char *end = str_chr(format, '%');
270 if (!end)
271 end = format + str_length(format);
272 size_t size = min((size_t)(end - format),
273 space_left - 1);
274 memcpy(out, format, size);
275 format = end;
276 out += size;
277 space_left -= size;
278 }
279 }
280 *out = '\0';
281
282 va_end(ap);
283 return EINVAL;
284}
285
286/** Get the current node being created, which may be NULL.
287 * @param scope The scope to get the current node from.
288 * @return The node being created, or NULL. */
289bithenge_node_t *bithenge_scope_get_current_node(bithenge_scope_t *scope)
290{
291 if (scope->current_node)
292 bithenge_node_inc_ref(scope->current_node);
293 return scope->current_node;
294}
295
296/** Set the current node being created. Takes a reference to @a node.
297 * @param scope The scope to set the current node in.
298 * @param node The current node being created, or NULL. */
299void bithenge_scope_set_current_node(bithenge_scope_t *scope,
300 bithenge_node_t *node)
301{
302 bithenge_node_dec_ref(scope->current_node);
303 scope->current_node = node;
304}
305
306/** Get the current input node, which may be NULL.
307 * @param scope The scope to get the current input node from.
308 * @return The input node, or NULL. */
309bithenge_node_t *bithenge_scope_in_node(bithenge_scope_t *scope)
310{
311 if (scope->in_node)
312 bithenge_node_inc_ref(scope->in_node);
313 return scope->in_node;
314}
315
316/** Set the current input node. Takes a reference to @a node.
317 * @param scope The scope to set the input node in.
318 * @param node The input node, or NULL. */
319void bithenge_scope_set_in_node(bithenge_scope_t *scope, bithenge_node_t *node)
320{
321 bithenge_node_dec_ref(scope->in_node);
322 scope->in_node = node;
323}
324
325/** Set a scope as a barrier.
326 * @param self The scope to change. */
327void bithenge_scope_set_barrier(bithenge_scope_t *self)
328{
329 self->barrier = true;
330}
331
332/** Check whether a scope is a barrier, meaning that variable lookup stops at
333 * it.
334 * @param self The scope to check.
335 * @return Whether the scope is a barrier. */
336bool bithenge_scope_is_barrier(bithenge_scope_t *self)
337{
338 return self->barrier;
339}
340
341/** Allocate parameters. The parameters must then be set with @a
342 * bithenge_scope_set_param. This must not be called on a scope that already
343 * has parameters.
344 * @param scope The scope in which to allocate parameters.
345 * @param num_params The number of parameters to allocate.
346 * @return EOK on success or an error code from errno.h. */
347int bithenge_scope_alloc_params(bithenge_scope_t *scope, int num_params)
348{
349 scope->params = malloc(sizeof(*scope->params) * num_params);
350 if (!scope->params)
351 return ENOMEM;
352 scope->num_params = num_params;
353 for (int i = 0; i < num_params; i++)
354 scope->params[i] = NULL;
355 return EOK;
356}
357
358/** Set a parameter. Takes a reference to @a value. Note that range checking is
359 * not done in release builds.
360 * @param scope The scope in which to allocate parameters.
361 * @param i The index of the parameter to set.
362 * @param value The value to store in the parameter.
363 * @return EOK on success or an error code from errno.h. */
364int bithenge_scope_set_param( bithenge_scope_t *scope, int i,
365 bithenge_node_t *node)
366{
367 assert(scope);
368 assert(i >= 0 && i < scope->num_params);
369 scope->params[i] = node;
370 return EOK;
371}
372
373/** Get a parameter. Note that range checking is not done in release builds.
374 * @param scope The scope to get the parameter from.
375 * @param i The index of the parameter to set.
376 * @param[out] out Stores a new reference to the parameter.
377 * @return EOK on success or an error code from errno.h. */
378int bithenge_scope_get_param(bithenge_scope_t *scope, int i,
379 bithenge_node_t **out)
380{
381 assert(scope);
382 if (scope->num_params) {
383 assert(i >= 0 && i < scope->num_params);
384 *out = scope->params[i];
385 bithenge_node_inc_ref(*out);
386 return EOK;
387 } else {
388 return bithenge_scope_get_param(scope->outer, i, out);
389 }
390}
391
392
393
394/***************** barrier_transform *****************/
395
396typedef struct {
397 bithenge_transform_t base;
398 bithenge_transform_t *transform;
399} barrier_transform_t;
400
401static inline barrier_transform_t *transform_as_barrier(
402 bithenge_transform_t *base)
403{
404 return (barrier_transform_t *)base;
405}
406
407static inline bithenge_transform_t *barrier_as_transform(
408 barrier_transform_t *self)
409{
410 return &self->base;
411}
412
413static int barrier_transform_apply(bithenge_transform_t *base,
414 bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
415{
416 barrier_transform_t *self = transform_as_barrier(base);
417 bithenge_scope_t *inner_scope;
418 int rc = bithenge_scope_new(&inner_scope, scope);
419 if (rc != EOK)
420 return rc;
421 bithenge_scope_set_barrier(inner_scope);
422 bithenge_scope_set_in_node(inner_scope, in);
423 rc = bithenge_transform_apply(self->transform, inner_scope, in, out);
424 bithenge_scope_dec_ref(inner_scope);
425 return rc;
426}
427
428static int barrier_transform_prefix_length(bithenge_transform_t *base,
429 bithenge_scope_t *scope, bithenge_blob_t *in, aoff64_t *out)
430{
431 barrier_transform_t *self = transform_as_barrier(base);
432 bithenge_scope_t *inner_scope;
433 int rc = bithenge_scope_new(&inner_scope, scope);
434 if (rc != EOK)
435 return rc;
436 bithenge_scope_set_barrier(inner_scope);
437 bithenge_scope_set_in_node(inner_scope, bithenge_blob_as_node(in));
438 rc = bithenge_transform_prefix_length(self->transform, inner_scope, in,
439 out);
440 bithenge_scope_dec_ref(inner_scope);
441 return rc;
442}
443
444static int barrier_transform_prefix_apply(bithenge_transform_t *base,
445 bithenge_scope_t *scope, bithenge_blob_t *in, bithenge_node_t **out_node,
446 aoff64_t *out_length)
447{
448 barrier_transform_t *self = transform_as_barrier(base);
449 bithenge_scope_t *inner_scope;
450 int rc = bithenge_scope_new(&inner_scope, scope);
451 if (rc != EOK)
452 return rc;
453 bithenge_scope_set_barrier(inner_scope);
454 bithenge_scope_set_in_node(inner_scope, bithenge_blob_as_node(in));
455 rc = bithenge_transform_prefix_apply(self->transform, inner_scope, in,
456 out_node, out_length);
457 bithenge_scope_dec_ref(inner_scope);
458 return rc;
459}
460
461static void barrier_transform_destroy(bithenge_transform_t *base)
462{
463 barrier_transform_t *self = transform_as_barrier(base);
464 bithenge_transform_dec_ref(self->transform);
465 free(self);
466}
467
468static const bithenge_transform_ops_t barrier_transform_ops = {
469 .apply = barrier_transform_apply,
470 .prefix_length = barrier_transform_prefix_length,
471 .prefix_apply = barrier_transform_prefix_apply,
472 .destroy = barrier_transform_destroy,
473};
474
475/** Create a wrapper transform that creates a new scope. This ensures nothing
476 * from the outer scope is passed in, other than parameters. The wrapper may
477 * have a different value for num_params. Takes a reference to @a transform,
478 * which it will use for all operations.
479 * @param[out] out Holds the created transform.
480 * @param transform The transform to wrap.
481 * @param num_params The number of parameters to require, which may be 0.
482 * @return EOK on success or an error code from errno.h. */
483int bithenge_new_barrier_transform(bithenge_transform_t **out,
484 bithenge_transform_t *transform, int num_params)
485{
486 assert(transform);
487 assert(bithenge_transform_num_params(transform) == 0);
488
489 int rc;
490 barrier_transform_t *self = malloc(sizeof(*self));
491 if (!self) {
492 rc = ENOMEM;
493 goto error;
494 }
495 rc = bithenge_init_transform(barrier_as_transform(self),
496 &barrier_transform_ops, num_params);
497 if (rc != EOK)
498 goto error;
499 self->transform = transform;
500 *out = barrier_as_transform(self);
501 return EOK;
502error:
503 bithenge_transform_dec_ref(transform);
504 free(self);
505 return rc;
506}
507
508
509
510/***************** ascii *****************/
511
512static int ascii_apply(bithenge_transform_t *self, bithenge_scope_t *scope,
513 bithenge_node_t *in, bithenge_node_t **out)
514{
515 int rc;
516 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
517 return EINVAL;
518 bithenge_blob_t *blob = bithenge_node_as_blob(in);
519 aoff64_t size;
520 rc = bithenge_blob_size(blob, &size);
521 if (rc != EOK)
522 return rc;
523
524 char *buffer = malloc(size + 1);
525 if (!buffer)
526 return ENOMEM;
527 aoff64_t size_read = size;
528 rc = bithenge_blob_read(blob, 0, buffer, &size_read);
529 if (rc != EOK) {
530 free(buffer);
531 return rc;
532 }
533 if (size_read != size) {
534 free(buffer);
535 return EINVAL;
536 }
537 buffer[size] = '\0';
538
539 /* TODO: what if the OS encoding is incompatible with ASCII? */
540 return bithenge_new_string_node(out, buffer, true);
541}
542
543static const bithenge_transform_ops_t ascii_ops = {
544 .apply = ascii_apply,
545 .destroy = transform_indestructible,
546};
547
548/** The ASCII text transform. */
549bithenge_transform_t bithenge_ascii_transform = {
550 &ascii_ops, 1, 0
551};
552
553
554
555/***************** bit *****************/
556
557static int bit_prefix_apply(bithenge_transform_t *self,
558 bithenge_scope_t *scope, bithenge_blob_t *blob, bithenge_node_t **out_node,
559 aoff64_t *out_size)
560{
561 char buffer;
562 *out_size = 1;
563 int rc = bithenge_blob_read_bits(blob, 0, &buffer, out_size, true);
564 if (rc != EOK)
565 return rc;
566 if (*out_size != 1)
567 return EINVAL;
568 return bithenge_new_boolean_node(out_node, (buffer & 1) != 0);
569}
570
571static const bithenge_transform_ops_t bit_ops = {
572 .prefix_apply = bit_prefix_apply,
573 .destroy = transform_indestructible,
574};
575
576/** A transform that decodes a bit as a boolean. */
577bithenge_transform_t bithenge_bit_transform = {
578 &bit_ops, 1, 0
579};
580
581
582
583/***************** bits_be, bits_le *****************/
584
585typedef struct {
586 bithenge_blob_t base;
587 bithenge_blob_t *bytes;
588 bool little_endian;
589} bits_xe_blob_t;
590
591static bits_xe_blob_t *blob_as_bits_xe(bithenge_blob_t *base)
592{
593 return (bits_xe_blob_t *)base;
594}
595
596static bithenge_blob_t *bits_xe_as_blob(bits_xe_blob_t *self)
597{
598 return &self->base;
599}
600
601static int bits_xe_size(bithenge_blob_t *base, aoff64_t *out)
602{
603 bits_xe_blob_t *self = blob_as_bits_xe(base);
604 int rc = bithenge_blob_size(self->bytes, out);
605 *out *= 8;
606 return rc;
607}
608
609static uint8_t reverse_byte(uint8_t val)
610{
611 val = ((val & 0x0f) << 4) ^ ((val & 0xf0) >> 4);
612 val = ((val & 0x33) << 2) ^ ((val & 0xcc) >> 2);
613 val = ((val & 0x55) << 1) ^ ((val & 0xaa) >> 1);
614 return val;
615}
616
617static int bits_xe_read_bits(bithenge_blob_t *base, aoff64_t offset,
618 char *buffer, aoff64_t *size, bool little_endian)
619{
620 bits_xe_blob_t *self = blob_as_bits_xe(base);
621 aoff64_t bytes_offset = offset / 8;
622 aoff64_t bit_offset = offset % 8;
623 aoff64_t output_num_bytes = (*size + 7) / 8;
624 aoff64_t bytes_size = (*size + bit_offset + 7) / 8;
625 bool separate_buffer = bit_offset != 0;
626 uint8_t *bytes_buffer;
627 if (separate_buffer) {
628 /* Allocate an extra byte, to make sure byte1 can be read. */
629 bytes_buffer = malloc(bytes_size + 1);
630 if (!bytes_buffer)
631 return ENOMEM;
632 } else
633 bytes_buffer = (uint8_t *)buffer;
634
635 int rc = bithenge_blob_read(self->bytes, bytes_offset,
636 (char *)bytes_buffer, &bytes_size);
637 if (rc != EOK)
638 goto end;
639 *size = min(*size, bytes_size * 8 - bit_offset);
640
641 if (little_endian != self->little_endian)
642 for (aoff64_t i = 0; i < bytes_size; i++)
643 bytes_buffer[i] = reverse_byte(bytes_buffer[i]);
644
645 if (bit_offset || separate_buffer) {
646 for (aoff64_t i = 0; i < output_num_bytes; i++) {
647 uint8_t byte0 = bytes_buffer[i];
648 uint8_t byte1 = bytes_buffer[i + 1];
649 buffer[i] = little_endian ?
650 (byte0 >> bit_offset) ^ (byte1 << (8 - bit_offset)) :
651 (byte0 << bit_offset) ^ (byte1 >> (8 - bit_offset));
652 }
653 }
654
655end:
656 if (separate_buffer)
657 free(bytes_buffer);
658 return rc;
659}
660
661static void bits_xe_destroy(bithenge_blob_t *base)
662{
663 bits_xe_blob_t *self = blob_as_bits_xe(base);
664 bithenge_blob_dec_ref(self->bytes);
665 free(self);
666}
667
668static const bithenge_random_access_blob_ops_t bits_xe_blob_ops = {
669 .size = bits_xe_size,
670 .read_bits = bits_xe_read_bits,
671 .destroy = bits_xe_destroy,
672};
673
674static int bits_xe_apply(bithenge_transform_t *self, bithenge_scope_t *scope,
675 bithenge_node_t *in, bithenge_node_t **out)
676{
677 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
678 return EINVAL;
679 bits_xe_blob_t *blob = malloc(sizeof(*blob));
680 if (!blob)
681 return ENOMEM;
682 int rc = bithenge_init_random_access_blob(bits_xe_as_blob(blob),
683 &bits_xe_blob_ops);
684 if (rc != EOK) {
685 free(blob);
686 return rc;
687 }
688 bithenge_node_inc_ref(in);
689 blob->bytes = bithenge_node_as_blob(in);
690 blob->little_endian = (self == &bithenge_bits_le_transform);
691 *out = bithenge_blob_as_node(bits_xe_as_blob(blob));
692 return EOK;
693}
694
695static const bithenge_transform_ops_t bits_xe_ops = {
696 .apply = bits_xe_apply,
697 .destroy = transform_indestructible,
698};
699
700/** A transform that converts a byte blob to a bit blob, most-significant bit
701 * first. */
702bithenge_transform_t bithenge_bits_be_transform = {
703 &bits_xe_ops, 1, 0
704};
705
706/** A transform that converts a byte blob to a bit blob, least-significant bit
707 * first. */
708bithenge_transform_t bithenge_bits_le_transform = {
709 &bits_xe_ops, 1, 0
710};
711
712
713
714/***************** invalid *****************/
715
716static int invalid_apply(bithenge_transform_t *self, bithenge_scope_t *scope,
717 bithenge_node_t *in, bithenge_node_t **out)
718{
719 return EINVAL;
720}
721
722static const bithenge_transform_ops_t invalid_ops = {
723 .apply = invalid_apply,
724 .destroy = transform_indestructible,
725};
726
727/** A transform that always raises an error. */
728bithenge_transform_t bithenge_invalid_transform = {
729 &invalid_ops, 1, 0
730};
731
732
733
734/***************** known_length *****************/
735
736static int known_length_apply(bithenge_transform_t *self,
737 bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
738{
739 bithenge_node_t *length_node;
740 int rc = bithenge_scope_get_param(scope, 0, &length_node);
741 if (rc != EOK)
742 return rc;
743 if (bithenge_node_type(length_node) != BITHENGE_NODE_INTEGER) {
744 bithenge_node_dec_ref(length_node);
745 return EINVAL;
746 }
747 bithenge_int_t length = bithenge_integer_node_value(length_node);
748 bithenge_node_dec_ref(length_node);
749
750 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
751 return EINVAL;
752 aoff64_t size;
753 rc = bithenge_blob_size(bithenge_node_as_blob(in), &size);
754 if (rc != EOK)
755 return rc;
756 if (length != (bithenge_int_t)size)
757 return EINVAL;
758
759 bithenge_node_inc_ref(in);
760 *out = in;
761 return EOK;
762}
763
764static int known_length_prefix_length(bithenge_transform_t *self,
765 bithenge_scope_t *scope, bithenge_blob_t *in, aoff64_t *out)
766{
767 bithenge_node_t *length_node;
768 int rc = bithenge_scope_get_param(scope, 0, &length_node);
769 if (rc != EOK)
770 return rc;
771 if (bithenge_node_type(length_node) != BITHENGE_NODE_INTEGER) {
772 bithenge_node_dec_ref(length_node);
773 return EINVAL;
774 }
775 bithenge_int_t length = bithenge_integer_node_value(length_node);
776 bithenge_node_dec_ref(length_node);
777
778 *out = (aoff64_t)length;
779 return EOK;
780}
781
782static const bithenge_transform_ops_t known_length_ops = {
783 .apply = known_length_apply,
784 .prefix_length = known_length_prefix_length,
785 .destroy = transform_indestructible,
786};
787
788/** Pass through a blob, but require its length to equal the first argument. */
789bithenge_transform_t bithenge_known_length_transform = {
790 &known_length_ops, 1, 1
791};
792
793static int nonzero_boolean_apply(bithenge_transform_t *self,
794 bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
795{
796 if (bithenge_node_type(in) != BITHENGE_NODE_INTEGER)
797 return EINVAL;
798 bool value = bithenge_integer_node_value(in) != 0;
799 return bithenge_new_boolean_node(out, value);
800}
801
802static const bithenge_transform_ops_t nonzero_boolean_ops = {
803 .apply = nonzero_boolean_apply,
804 .destroy = transform_indestructible,
805};
806
807/** A transform that converts integers to booleans, true if nonzero. */
808bithenge_transform_t bithenge_nonzero_boolean_transform = {
809 &nonzero_boolean_ops, 1, 0
810};
811
812static int prefix_length_1(bithenge_transform_t *self, bithenge_scope_t *scope,
813 bithenge_blob_t *blob, aoff64_t *out)
814{
815 *out = 1;
816 return EOK;
817}
818
819static int prefix_length_2(bithenge_transform_t *self, bithenge_scope_t *scope,
820 bithenge_blob_t *blob, aoff64_t *out)
821{
822 *out = 2;
823 return EOK;
824}
825
826static int prefix_length_4(bithenge_transform_t *self, bithenge_scope_t *scope,
827 bithenge_blob_t *blob, aoff64_t *out)
828{
829 *out = 4;
830 return EOK;
831}
832
833static int prefix_length_8(bithenge_transform_t *self, bithenge_scope_t *scope,
834 bithenge_blob_t *blob, aoff64_t *out)
835{
836 *out = 8;
837 return EOK;
838}
839
840#define MAKE_UINT_TRANSFORM(NAME, TYPE, ENDIAN, PREFIX_LENGTH_FUNC) \
841 static int NAME##_apply(bithenge_transform_t *self, \
842 bithenge_scope_t *scope, bithenge_node_t *in, \
843 bithenge_node_t **out) \
844 { \
845 int rc; \
846 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB) \
847 return EINVAL; \
848 bithenge_blob_t *blob = bithenge_node_as_blob(in); \
849 \
850 /* Read too many bytes; success means the blob is too long. */ \
851 TYPE val[2]; \
852 aoff64_t size = sizeof(val[0]) + 1; \
853 rc = bithenge_blob_read(blob, 0, (char *)val, &size); \
854 if (rc != EOK) \
855 return rc; \
856 if (size != sizeof(val[0])) \
857 return EINVAL; \
858 \
859 return bithenge_new_integer_node(out, ENDIAN(val[0])); \
860 } \
861 \
862 static const bithenge_transform_ops_t NAME##_ops = { \
863 .apply = NAME##_apply, \
864 .prefix_length = PREFIX_LENGTH_FUNC, \
865 .destroy = transform_indestructible, \
866 }; \
867 \
868 bithenge_transform_t bithenge_##NAME##_transform = { \
869 &NAME##_ops, 1, 0 \
870 }
871
872MAKE_UINT_TRANSFORM(uint8 , uint8_t , , prefix_length_1);
873MAKE_UINT_TRANSFORM(uint16le, uint16_t, uint16_t_le2host, prefix_length_2);
874MAKE_UINT_TRANSFORM(uint16be, uint16_t, uint16_t_be2host, prefix_length_2);
875MAKE_UINT_TRANSFORM(uint32le, uint32_t, uint32_t_le2host, prefix_length_4);
876MAKE_UINT_TRANSFORM(uint32be, uint32_t, uint32_t_be2host, prefix_length_4);
877MAKE_UINT_TRANSFORM(uint64le, uint64_t, uint32_t_le2host, prefix_length_8);
878MAKE_UINT_TRANSFORM(uint64be, uint64_t, uint32_t_be2host, prefix_length_8);
879
880
881
882/***************** uint_be, uint_le *****************/
883
884static int uint_xe_prefix_apply(bithenge_transform_t *self,
885 bithenge_scope_t *scope, bithenge_blob_t *blob, bithenge_node_t **out_node,
886 aoff64_t *out_size)
887{
888 bool little_endian = (self == &bithenge_uint_le_transform);
889 bithenge_node_t *num_bits_node;
890 int rc = bithenge_scope_get_param(scope, 0, &num_bits_node);
891 if (rc != EOK)
892 return rc;
893 if (bithenge_node_type(num_bits_node) != BITHENGE_NODE_INTEGER) {
894 bithenge_node_dec_ref(num_bits_node);
895 return EINVAL;
896 }
897 bithenge_int_t num_bits = bithenge_integer_node_value(num_bits_node);
898 bithenge_node_dec_ref(num_bits_node);
899 if (num_bits < 0)
900 return EINVAL;
901 if ((size_t)num_bits > sizeof(bithenge_int_t) * 8 - 1)
902 return EINVAL;
903
904 *out_size = num_bits;
905 uint8_t buffer[sizeof(bithenge_int_t)];
906 rc = bithenge_blob_read_bits(blob, 0, (char *)buffer, out_size,
907 little_endian);
908 if (rc != EOK)
909 return rc;
910 if (*out_size != (aoff64_t)num_bits)
911 return EINVAL;
912
913 bithenge_int_t result = 0;
914 bithenge_int_t num_easy_bytes = num_bits / 8;
915 if (little_endian) {
916 for (bithenge_int_t i = 0; i < num_easy_bytes; i++)
917 result += buffer[i] << 8 * i;
918 if (num_bits % 8)
919 result += (buffer[num_easy_bytes] &
920 ((1 << num_bits % 8) - 1)) << 8 * num_easy_bytes;
921 } else {
922 for (bithenge_int_t i = 0; i < num_easy_bytes; i++)
923 result += buffer[i] << (num_bits - 8 * (i + 1));
924 if (num_bits % 8)
925 result += buffer[num_easy_bytes] >> (8 - num_bits % 8);
926 }
927
928 return bithenge_new_integer_node(out_node, result);
929}
930
931static const bithenge_transform_ops_t uint_xe_ops = {
932 .prefix_apply = uint_xe_prefix_apply,
933 .destroy = transform_indestructible,
934};
935
936/** A transform that reads an unsigned integer from an arbitrary number of
937 * bits, most-significant bit first. */
938bithenge_transform_t bithenge_uint_be_transform = {
939 &uint_xe_ops, 1, 1
940};
941
942/** A transform that reads an unsigned integer from an arbitrary number of
943 * bits, least-significant bit first. */
944bithenge_transform_t bithenge_uint_le_transform = {
945 &uint_xe_ops, 1, 1
946};
947
948
949
950/***************** zero_terminated *****************/
951
952static int zero_terminated_apply(bithenge_transform_t *self,
953 bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
954{
955 int rc;
956 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
957 return EINVAL;
958 bithenge_blob_t *blob = bithenge_node_as_blob(in);
959 aoff64_t size;
960 rc = bithenge_blob_size(blob, &size);
961 if (rc != EOK)
962 return rc;
963 if (size < 1)
964 return EINVAL;
965 char ch;
966 aoff64_t size_read = 1;
967 rc = bithenge_blob_read(blob, size - 1, &ch, &size_read);
968 if (rc != EOK)
969 return rc;
970 if (size_read != 1 || ch != '\0')
971 return EINVAL;
972 bithenge_blob_inc_ref(blob);
973 return bithenge_new_subblob(out, blob, 0, size - 1);
974}
975
976static int zero_terminated_prefix_length(bithenge_transform_t *self,
977 bithenge_scope_t *scope, bithenge_blob_t *blob, aoff64_t *out)
978{
979 int rc;
980 char buffer[4096];
981 aoff64_t offset = 0, size_read = sizeof(buffer);
982 do {
983 rc = bithenge_blob_read(blob, offset, buffer, &size_read);
984 if (rc != EOK)
985 return rc;
986 char *found = memchr(buffer, '\0', size_read);
987 if (found) {
988 *out = found - buffer + offset + 1;
989 return EOK;
990 }
991 offset += size_read;
992 } while (size_read == sizeof(buffer));
993 return EINVAL;
994}
995
996static const bithenge_transform_ops_t zero_terminated_ops = {
997 .apply = zero_terminated_apply,
998 .prefix_length = zero_terminated_prefix_length,
999 .destroy = transform_indestructible,
1000};
1001
1002/** The zero-terminated data transform. */
1003bithenge_transform_t bithenge_zero_terminated_transform = {
1004 &zero_terminated_ops, 1, 0
1005};
1006
1007static bithenge_named_transform_t primitive_transforms[] = {
1008 {"ascii", &bithenge_ascii_transform},
1009 {"bit", &bithenge_bit_transform},
1010 {"bits_be", &bithenge_bits_be_transform},
1011 {"bits_le", &bithenge_bits_le_transform},
1012 {"known_length", &bithenge_known_length_transform},
1013 {"nonzero_boolean", &bithenge_nonzero_boolean_transform},
1014 {"uint8", &bithenge_uint8_transform},
1015 {"uint16be", &bithenge_uint16be_transform},
1016 {"uint16le", &bithenge_uint16le_transform},
1017 {"uint32be", &bithenge_uint32be_transform},
1018 {"uint32le", &bithenge_uint32le_transform},
1019 {"uint64be", &bithenge_uint64be_transform},
1020 {"uint64le", &bithenge_uint64le_transform},
1021 {"uint_be", &bithenge_uint_be_transform},
1022 {"uint_le", &bithenge_uint_le_transform},
1023 {"zero_terminated", &bithenge_zero_terminated_transform},
1024 {NULL, NULL}
1025};
1026
1027/** An array of named built-in transforms. */
1028bithenge_named_transform_t *bithenge_primitive_transforms = primitive_transforms;
1029
1030/** @}
1031 */
Note: See TracBrowser for help on using the repository browser.