source: mainline/uspace/app/bithenge/blob.c@ 978ccaf1

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

Bithenge: various cleanup and tweaks

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