source: mainline/uspace/lib/bithenge/transform.c@ 1c79996

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

Bithenge: fix issues and expand coverage for test.sh

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