source: mainline/uspace/lib/bithenge/src/transform.c@ eec201d

Last change on this file since eec201d was 09ab0a9a, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Fix vertical spacing with new Ccheck revision.

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