source: mainline/uspace/lib/bithenge/src/blob.c@ 1433ecda

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1433ecda was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

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