xref: /freebsd/contrib/libcbor/src/cbor/arrays.c (revision 8ddb146abcdf061be9f2c0db7e391697dafad85c)
1 /*
2  * Copyright (c) 2014-2020 Pavel Kalvoda <me@pavelkalvoda.com>
3  *
4  * libcbor is free software; you can redistribute it and/or modify
5  * it under the terms of the MIT license. See LICENSE for details.
6  */
7 
8 #include "arrays.h"
9 #include <string.h>
10 #include "internal/memory_utils.h"
11 
12 size_t cbor_array_size(const cbor_item_t *item) {
13   assert(cbor_isa_array(item));
14   return item->metadata.array_metadata.end_ptr;
15 }
16 
17 size_t cbor_array_allocated(const cbor_item_t *item) {
18   assert(cbor_isa_array(item));
19   return item->metadata.array_metadata.allocated;
20 }
21 
22 cbor_item_t *cbor_array_get(const cbor_item_t *item, size_t index) {
23   return cbor_incref(((cbor_item_t **)item->data)[index]);
24 }
25 
26 bool cbor_array_set(cbor_item_t *item, size_t index, cbor_item_t *value) {
27   if (index == item->metadata.array_metadata.end_ptr) {
28     return cbor_array_push(item, value);
29   } else if (index < item->metadata.array_metadata.end_ptr) {
30     return cbor_array_replace(item, index, value);
31   } else {
32     return false;
33   }
34   // TODO: This is unreachable and the index checking logic above seems
35   // suspicious -- out of bounds index is a caller error. Figure out & fix.
36   return true;
37 }
38 
39 bool cbor_array_replace(cbor_item_t *item, size_t index, cbor_item_t *value) {
40   if (index >= item->metadata.array_metadata.end_ptr) return false;
41   /* We cannot use cbor_array_get as that would increase the refcount */
42   cbor_intermediate_decref(((cbor_item_t **)item->data)[index]);
43   ((cbor_item_t **)item->data)[index] = cbor_incref(value);
44   return true;
45 }
46 
47 bool cbor_array_push(cbor_item_t *array, cbor_item_t *pushee) {
48   assert(cbor_isa_array(array));
49   struct _cbor_array_metadata *metadata =
50       (struct _cbor_array_metadata *)&array->metadata;
51   cbor_item_t **data = (cbor_item_t **)array->data;
52   if (cbor_array_is_definite(array)) {
53     /* Do not reallocate definite arrays */
54     if (metadata->end_ptr >= metadata->allocated) {
55       return false;
56     }
57     data[metadata->end_ptr++] = pushee;
58   } else {
59     /* Exponential realloc */
60     if (metadata->end_ptr >= metadata->allocated) {
61       // Check for overflows first
62       // TODO: Explicitly test this
63       if (!_cbor_safe_to_multiply(CBOR_BUFFER_GROWTH, metadata->allocated)) {
64         return false;
65       }
66 
67       size_t new_allocation = metadata->allocated == 0
68                                   ? 1
69                                   : CBOR_BUFFER_GROWTH * metadata->allocated;
70 
71       unsigned char *new_data = _cbor_realloc_multiple(
72           array->data, sizeof(cbor_item_t *), new_allocation);
73       if (new_data == NULL) {
74         return false;
75       }
76 
77       array->data = new_data;
78       metadata->allocated = new_allocation;
79     }
80     ((cbor_item_t **)array->data)[metadata->end_ptr++] = pushee;
81   }
82   cbor_incref(pushee);
83   return true;
84 }
85 
86 bool cbor_array_is_definite(const cbor_item_t *item) {
87   assert(cbor_isa_array(item));
88   return item->metadata.array_metadata.type == _CBOR_METADATA_DEFINITE;
89 }
90 
91 bool cbor_array_is_indefinite(const cbor_item_t *item) {
92   assert(cbor_isa_array(item));
93   return item->metadata.array_metadata.type == _CBOR_METADATA_INDEFINITE;
94 }
95 
96 cbor_item_t **cbor_array_handle(const cbor_item_t *item) {
97   assert(cbor_isa_array(item));
98   return (cbor_item_t **)item->data;
99 }
100 
101 cbor_item_t *cbor_new_definite_array(size_t size) {
102   cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t));
103   _CBOR_NOTNULL(item);
104   cbor_item_t **data = _cbor_alloc_multiple(sizeof(cbor_item_t *), size);
105   _CBOR_DEPENDENT_NOTNULL(item, data);
106 
107   for (size_t i = 0; i < size; i++) {
108     data[i] = NULL;
109   }
110 
111   *item = (cbor_item_t){
112       .refcount = 1,
113       .type = CBOR_TYPE_ARRAY,
114       .metadata = {.array_metadata = {.type = _CBOR_METADATA_DEFINITE,
115                                       .allocated = size,
116                                       .end_ptr = 0}},
117       .data = (unsigned char *)data};
118 
119   return item;
120 }
121 
122 cbor_item_t *cbor_new_indefinite_array() {
123   cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t));
124   _CBOR_NOTNULL(item);
125 
126   *item = (cbor_item_t){
127       .refcount = 1,
128       .type = CBOR_TYPE_ARRAY,
129       .metadata = {.array_metadata = {.type = _CBOR_METADATA_INDEFINITE,
130                                       .allocated = 0,
131                                       .end_ptr = 0}},
132       .data = NULL /* Can be safely realloc-ed */
133   };
134   return item;
135 }
136