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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 18b6a88 was 18b6a88, checked in by Jiri Svoboda <jiri@…>, 7 years ago

More ccheck fixes, sometimes with manual intervention.

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