source: mainline/uspace/lib/bithenge/src/blob.c@ 09553a0

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

Fix block comment formatting (ccheck).

  • Property mode set to 100644
File size: 13.2 KB
RevLine 
[a54bd98]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 * Raw binary blobs.
35 */
36
[743ce51]37#include <assert.h>
[a54bd98]38#include <errno.h>
39#include <stdlib.h>
[6cd10ac]40#include "common.h"
[8fc0f47c]41#include <bithenge/blob.h>
42#include <bithenge/tree.h>
[a54bd98]43
44/** Initialize a random access blob.
45 * @memberof bithenge_blob_t
46 * @param[out] blob The blob to initialize.
[743ce51]47 * @param[in] ops Operations providing random access support. This pointer must
[a54bd98]48 * be valid until the blob is destroyed.
49 * @return EOK on success or an error code from errno.h.
50 */
[b7fd2a0]51errno_t bithenge_init_random_access_blob(bithenge_blob_t *blob,
[743ce51]52 const bithenge_random_access_blob_ops_t *ops)
53{
54 assert(blob);
55 assert(ops);
56 assert(ops->destroy);
[0ce0103]57 assert(ops->read || ops->read_bits);
[743ce51]58 assert(ops->size);
59
[1a3b953]60 if (bithenge_should_fail())
61 return ENOMEM;
62
[5c925ce]63 blob->base.type = BITHENGE_NODE_BLOB;
[f2da0bb]64 blob->base.refs = 1;
[5c925ce]65 blob->base.blob_ops = ops;
[a54bd98]66 return EOK;
67}
68
[b7fd2a0]69static errno_t sequential_buffer(bithenge_sequential_blob_t *blob, aoff64_t end)
[743ce51]70{
[a54bd98]71 bool need_realloc = false;
72 while (end > blob->buffer_size) {
73 blob->buffer_size = max(4096, 2 * blob->buffer_size);
74 need_realloc = true;
75 }
76 if (need_realloc) {
77 char *buffer = realloc(blob->buffer, blob->buffer_size);
78 if (!buffer)
[743ce51]79 return ENOMEM;
[a54bd98]80 blob->buffer = buffer;
81 }
[743ce51]82 aoff64_t size = end - blob->data_size;
[b7fd2a0]83 errno_t rc = blob->ops->read(blob, blob->buffer + blob->data_size, &size);
[a54bd98]84 if (rc != EOK)
85 return rc;
86 blob->data_size += size;
87 return EOK;
88}
89
[978ccaf1]90static inline bithenge_sequential_blob_t *blob_as_sequential(
[1923501]91 bithenge_blob_t *base)
92{
93 return (bithenge_sequential_blob_t *)base;
94}
95
[978ccaf1]96static inline bithenge_blob_t *sequential_as_blob(
[1923501]97 bithenge_sequential_blob_t *blob)
98{
99 return &blob->base;
100}
101
[b7fd2a0]102static errno_t sequential_size(bithenge_blob_t *base, aoff64_t *size)
[743ce51]103{
[978ccaf1]104 bithenge_sequential_blob_t *blob = blob_as_sequential(base);
[b7fd2a0]105 errno_t rc;
[743ce51]106 if (blob->ops->size) {
107 rc = blob->ops->size(blob, size);
108 if (rc == EOK)
109 return EOK;
110 }
111 rc = sequential_buffer(blob, blob->buffer_size);
112 if (rc != EOK)
113 return rc;
114 while (blob->data_size == blob->buffer_size) {
115 rc = sequential_buffer(blob, 2 * blob->buffer_size);
[a54bd98]116 if (rc != EOK)
117 return rc;
118 }
[743ce51]119 *size = blob->data_size;
[a54bd98]120 return EOK;
121}
122
[b7fd2a0]123static errno_t sequential_read(bithenge_blob_t *base, aoff64_t offset,
[743ce51]124 char *buffer, aoff64_t *size)
125{
[978ccaf1]126 bithenge_sequential_blob_t *blob = blob_as_sequential(base);
[a54bd98]127 aoff64_t end = offset + *size;
128 if (end > blob->data_size) {
[b7fd2a0]129 errno_t rc = sequential_buffer(blob, end);
[a54bd98]130 if (rc != EOK)
131 return rc;
132 }
133 if (offset > blob->data_size)
134 return EINVAL;
135 *size = min(*size, blob->data_size - end);
136 memcpy(buffer, blob->buffer + offset, *size);
137 return EOK;
138}
139
[978ccaf1]140static void sequential_destroy(bithenge_blob_t *base)
[743ce51]141{
[978ccaf1]142 bithenge_sequential_blob_t *blob = blob_as_sequential(base);
[a54bd98]143 free(blob->buffer);
[978ccaf1]144 blob->ops->destroy(blob);
[a54bd98]145}
146
147static const bithenge_random_access_blob_ops_t sequential_ops = {
148 .size = sequential_size,
149 .read = sequential_read,
150 .destroy = sequential_destroy,
151};
152
153/** Initialize a sequential blob.
154 * @memberof bithenge_sequential_blob_t
155 * @param[out] blob The blob to initialize.
156 * @param[in] ops Operations providing sequential access support. This pointer
157 * must be valid until the blob is destroyed.
158 * @return EOK on success or an error code from errno.h.
159 */
[b7fd2a0]160errno_t bithenge_init_sequential_blob(bithenge_sequential_blob_t *blob,
[743ce51]161 const bithenge_sequential_blob_ops_t *ops)
162{
163 assert(blob);
164 assert(ops);
165 assert(ops->destroy);
166 assert(ops->read);
167 // ops->size is optional
168
[b7fd2a0]169 errno_t rc = bithenge_init_random_access_blob(sequential_as_blob(blob),
[1923501]170 &sequential_ops);
[a54bd98]171 if (rc != EOK)
172 return rc;
173 blob->ops = ops;
174 blob->buffer = NULL; // realloc(NULL, ...) works like malloc
175 blob->buffer_size = 0;
176 blob->data_size = 0;
177 return EOK;
178}
179
[ce683ed3]180typedef struct {
181 bithenge_blob_t base;
182 const char *buffer;
183 size_t size;
184 bool needs_free;
185} memory_blob_t;
186
[978ccaf1]187static inline memory_blob_t *blob_as_memory(bithenge_blob_t *base)
[ce683ed3]188{
189 return (memory_blob_t *)base;
190}
191
[978ccaf1]192static inline bithenge_blob_t *memory_as_blob(memory_blob_t *blob)
[ce683ed3]193{
194 return &blob->base;
195}
196
[b7fd2a0]197static errno_t memory_size(bithenge_blob_t *base, aoff64_t *size)
[ce683ed3]198{
[978ccaf1]199 memory_blob_t *blob = blob_as_memory(base);
[1a3b953]200 if (bithenge_should_fail())
201 return EIO;
[ce683ed3]202 *size = blob->size;
203 return EOK;
204}
205
[b7fd2a0]206static errno_t memory_read(bithenge_blob_t *base, aoff64_t offset, char *buffer,
[ce683ed3]207 aoff64_t *size)
208{
[978ccaf1]209 memory_blob_t *blob = blob_as_memory(base);
[ce683ed3]210 if (offset > blob->size)
211 return ELIMIT;
212 *size = min(*size, blob->size - offset);
213 memcpy(buffer, blob->buffer + offset, *size);
214 return EOK;
215}
216
[978ccaf1]217static void memory_destroy(bithenge_blob_t *base)
[ce683ed3]218{
[978ccaf1]219 memory_blob_t *blob = blob_as_memory(base);
[ce683ed3]220 if (blob->needs_free)
[da0fef6]221 free((void *)blob->buffer);
[ce683ed3]222 free(blob);
223}
224
225static const bithenge_random_access_blob_ops_t memory_ops = {
226 .size = memory_size,
227 .read = memory_read,
228 .destroy = memory_destroy,
229};
230
[8375d0eb]231/** Create a blob node from a buffer. The buffer must exist as long as the blob
232 * does. The blob must be freed with @a bithenge_node_t::bithenge_node_destroy
[ce683ed3]233 * after it is used.
234 * @memberof bithenge_blob_t
[8375d0eb]235 * @param[out] out Stores the created blob node.
[ce683ed3]236 * @param[in] buffer The buffer, which must not be changed until the blob is
237 * destroyed.
238 * @param len The length of the data.
[da0fef6]239 * @param needs_free If true, the buffer will be freed with free() if this
240 * function fails or the blob is destroyed.
[7c3fb9b]241 * @return EOK on success or an error code from errno.h.
242 */
[b7fd2a0]243errno_t bithenge_new_blob_from_buffer(bithenge_node_t **out, const void *buffer,
[ce683ed3]244 size_t len, bool needs_free)
245{
[b7fd2a0]246 errno_t rc;
[ce683ed3]247 assert(buffer || !len);
248
249 memory_blob_t *blob = malloc(sizeof(*blob));
[1a3b953]250 if (!blob) {
251 rc = ENOMEM;
252 goto error;
253 }
[978ccaf1]254 rc = bithenge_init_random_access_blob(memory_as_blob(blob),
[ce683ed3]255 &memory_ops);
[1a3b953]256 if (rc != EOK)
257 goto error;
[ce683ed3]258 blob->buffer = buffer;
259 blob->size = len;
260 blob->needs_free = needs_free;
[978ccaf1]261 *out = bithenge_blob_as_node(memory_as_blob(blob));
[ce683ed3]262 return EOK;
[a35b458]263
[1a3b953]264error:
265 if (needs_free)
266 free((void *)buffer);
267 free(blob);
268 return rc;
269}
270
271/** Create a blob node from data. Unlike with @a
272 * bithenge_blob_t::bithenge_new_blob_from_buffer, the data is copied into a
273 * new buffer and the original data can be changed after this call. The blob
274 * must be freed with @a bithenge_node_t::bithenge_node_destroy after it is
275 * used.
276 * @memberof bithenge_blob_t
277 * @param[out] out Stores the created blob node.
278 * @param[in] data The data.
279 * @param len The length of the data.
[7c3fb9b]280 * @return EOK on success or an error code from errno.h.
281 */
[b7fd2a0]282errno_t bithenge_new_blob_from_data(bithenge_node_t **out, const void *data,
[1a3b953]283 size_t len)
284{
285 char *buffer = malloc(len);
286 if (!buffer)
287 return ENOMEM;
288 memcpy(buffer, data, len);
289
290 return bithenge_new_blob_from_buffer(out, buffer, len, true);
[ce683ed3]291}
292
[1a3b953]293
294
[04a7435f]295typedef struct {
296 bithenge_blob_t base;
297 bithenge_blob_t *source;
298 aoff64_t offset;
299 aoff64_t size;
300 bool size_matters;
301} subblob_t;
302
303static inline subblob_t *blob_as_subblob(bithenge_blob_t *base)
304{
305 return (subblob_t *)base;
306}
307
308static inline bithenge_blob_t *subblob_as_blob(subblob_t *blob)
309{
310 return &blob->base;
311}
312
[b7fd2a0]313static errno_t subblob_size(bithenge_blob_t *base, aoff64_t *size)
[04a7435f]314{
315 subblob_t *blob = blob_as_subblob(base);
316 if (blob->size_matters) {
317 *size = blob->size;
318 return EOK;
319 } else {
[b7fd2a0]320 errno_t rc = bithenge_blob_size(blob->source, size);
[04a7435f]321 *size -= blob->offset;
322 return rc;
323 }
324}
325
[b7fd2a0]326static errno_t subblob_read(bithenge_blob_t *base, aoff64_t offset,
[04a7435f]327 char *buffer, aoff64_t *size)
328{
329 subblob_t *blob = blob_as_subblob(base);
330 if (blob->size_matters) {
331 if (offset > blob->size)
332 return EINVAL;
333 *size = min(*size, blob->size - offset);
334 }
335 offset += blob->offset;
336 return bithenge_blob_read(blob->source, offset, buffer, size);
337}
338
[b7fd2a0]339static errno_t subblob_read_bits(bithenge_blob_t *base, aoff64_t offset,
[0ce0103]340 char *buffer, aoff64_t *size, bool little_endian)
341{
342 subblob_t *blob = blob_as_subblob(base);
343 if (blob->size_matters) {
344 if (offset > blob->size)
345 return EINVAL;
346 *size = min(*size, blob->size - offset);
347 }
348 offset += blob->offset;
349 return bithenge_blob_read_bits(blob->source, offset, buffer, size,
350 little_endian);
351}
352
[978ccaf1]353static void subblob_destroy(bithenge_blob_t *base)
[04a7435f]354{
355 subblob_t *blob = blob_as_subblob(base);
356 bithenge_blob_dec_ref(blob->source);
357 free(blob);
358}
359
360static const bithenge_random_access_blob_ops_t subblob_ops = {
361 .size = subblob_size,
362 .read = subblob_read,
[0ce0103]363 .read_bits = subblob_read_bits,
[04a7435f]364 .destroy = subblob_destroy,
365};
366
367static bool is_subblob(bithenge_blob_t *blob)
368{
369 return blob->base.blob_ops == &subblob_ops;
370}
371
[b7fd2a0]372static errno_t new_subblob(bithenge_node_t **out, bithenge_blob_t *source,
[04a7435f]373 aoff64_t offset, aoff64_t size, bool size_matters)
374{
375 assert(out);
376 assert(source);
[b7fd2a0]377 errno_t rc;
[04a7435f]378 subblob_t *blob = 0;
379
380 if (is_subblob(source)) {
381 /* We can do some optimizations this way */
382 if (!size_matters)
383 size = 0;
384 subblob_t *source_subblob = blob_as_subblob(source);
385 if (source_subblob->size_matters &&
386 offset + size > source_subblob->size) {
387 rc = EINVAL;
388 goto error;
389 }
390
391 if (source->base.refs == 1) {
392 source_subblob->offset += offset;
393 source_subblob->size -= offset;
394 if (size_matters) {
395 source_subblob->size_matters = true;
396 source_subblob->size = size;
397 }
398 *out = bithenge_blob_as_node(source);
399 return EOK;
400 }
401
402 if (!size_matters && source_subblob->size_matters) {
403 size_matters = true;
404 size = source_subblob->size - offset;
405 }
406 offset += source_subblob->offset;
407 source = source_subblob->source;
408 bithenge_blob_inc_ref(source);
409 bithenge_blob_dec_ref(subblob_as_blob(source_subblob));
410 }
411
412 blob = malloc(sizeof(*blob));
413 if (!blob) {
414 rc = ENOMEM;
415 goto error;
416 }
[978ccaf1]417 rc = bithenge_init_random_access_blob(subblob_as_blob(blob),
[04a7435f]418 &subblob_ops);
419 if (rc != EOK)
420 goto error;
421 blob->source = source;
422 blob->offset = offset;
423 blob->size = size;
424 blob->size_matters = size_matters;
425 *out = bithenge_blob_as_node(subblob_as_blob(blob));
426 return EOK;
427
428error:
429 bithenge_blob_dec_ref(source);
430 free(blob);
431 return rc;
432}
433
434/** Create a blob from data offset within another blob. This function takes
435 * ownership of a reference to @a blob.
436 * @param[out] out Stores the created blob node. On error, this is unchanged.
437 * @param[in] source The input blob.
438 * @param offset The offset within the input blob at which the new blob will start.
[7c3fb9b]439 * @return EOK on success or an error code from errno.h.
440 */
[b7fd2a0]441errno_t bithenge_new_offset_blob(bithenge_node_t **out, bithenge_blob_t *source,
[04a7435f]442 aoff64_t offset)
443{
444 return new_subblob(out, source, offset, 0, false);
445}
446
447/** Create a blob from part of another blob. This function takes ownership of a
448 * reference to @a blob.
449 * @param[out] out Stores the created blob node. On error, this is unchanged.
450 * @param[in] source The input blob.
451 * @param offset The offset within the input blob at which the new blob will start.
452 * @param size The size of the new blob.
[7c3fb9b]453 * @return EOK on success or an error code from errno.h.
454 */
[b7fd2a0]455errno_t bithenge_new_subblob(bithenge_node_t **out, bithenge_blob_t *source,
[04a7435f]456 aoff64_t offset, aoff64_t size)
457{
458 return new_subblob(out, source, offset, size, true);
459}
460
[d5070ef]461/** Check whether the contents of two blobs are equal.
462 * @memberof bithenge_blob_t
[a42d7d8]463 * @param[out] out Holds whether the blobs are equal.
[d5070ef]464 * @param a, b Blobs to compare.
[a42d7d8]465 * @return EOK on success, or an error code from errno.h.
[d5070ef]466 */
[b7fd2a0]467errno_t bithenge_blob_equal(bool *out, bithenge_blob_t *a, bithenge_blob_t *b)
[d5070ef]468{
469 assert(a);
470 assert(a->base.blob_ops);
471 assert(b);
472 assert(b->base.blob_ops);
[b7fd2a0]473 errno_t rc;
[d5070ef]474 char buffer_a[4096], buffer_b[4096];
475 aoff64_t offset = 0, size_a = sizeof(buffer_a), size_b = sizeof(buffer_b);
476 do {
477 rc = bithenge_blob_read(a, offset, buffer_a, &size_a);
478 if (rc != EOK)
[a42d7d8]479 return rc;
[d5070ef]480 rc = bithenge_blob_read(b, offset, buffer_b, &size_b);
481 if (rc != EOK)
[a42d7d8]482 return rc;
[44ecf89]483 if (size_a != size_b || memcmp(buffer_a, buffer_b, size_a) != 0) {
[a42d7d8]484 *out = false;
485 return EOK;
486 }
[d5070ef]487 offset += size_a;
488 } while (size_a == sizeof(buffer_a));
[a42d7d8]489 *out = true;
490 return EOK;
[d5070ef]491}
492
[a54bd98]493/** @}
494 */
Note: See TracBrowser for help on using the repository browser.