xref: /freebsd/contrib/libcbor/src/cbor/maps.c (revision f7c32ed617858bcd22f8d1b03199099d50125721)
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 "maps.h"
9 #include "internal/memory_utils.h"
10 
11 size_t cbor_map_size(const cbor_item_t *item) {
12   assert(cbor_isa_map(item));
13   return item->metadata.map_metadata.end_ptr;
14 }
15 
16 size_t cbor_map_allocated(const cbor_item_t *item) {
17   assert(cbor_isa_map(item));
18   return item->metadata.map_metadata.allocated;
19 }
20 
21 cbor_item_t *cbor_new_definite_map(size_t size) {
22   cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t));
23   _CBOR_NOTNULL(item);
24 
25   *item = (cbor_item_t){
26       .refcount = 1,
27       .type = CBOR_TYPE_MAP,
28       .metadata = {.map_metadata = {.allocated = size,
29                                     .type = _CBOR_METADATA_DEFINITE,
30                                     .end_ptr = 0}},
31       .data = _cbor_alloc_multiple(sizeof(struct cbor_pair), size)};
32   _CBOR_DEPENDENT_NOTNULL(item, item->data);
33 
34   return item;
35 }
36 
37 cbor_item_t *cbor_new_indefinite_map() {
38   cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t));
39   _CBOR_NOTNULL(item);
40 
41   *item = (cbor_item_t){
42       .refcount = 1,
43       .type = CBOR_TYPE_MAP,
44       .metadata = {.map_metadata = {.allocated = 0,
45                                     .type = _CBOR_METADATA_INDEFINITE,
46                                     .end_ptr = 0}},
47       .data = NULL};
48 
49   return item;
50 }
51 
52 bool _cbor_map_add_key(cbor_item_t *item, cbor_item_t *key) {
53   assert(cbor_isa_map(item));
54   struct _cbor_map_metadata *metadata =
55       (struct _cbor_map_metadata *)&item->metadata;
56   if (cbor_map_is_definite(item)) {
57     struct cbor_pair *data = cbor_map_handle(item);
58     if (metadata->end_ptr >= metadata->allocated) {
59       /* Don't realloc definite preallocated map */
60       return false;
61     }
62 
63     data[metadata->end_ptr].key = key;
64     data[metadata->end_ptr++].value = NULL;
65   } else {
66     if (metadata->end_ptr >= metadata->allocated) {
67       /* Exponential realloc */
68       // Check for overflows first
69       // TODO: Explicitly test this
70       if (!_cbor_safe_to_multiply(CBOR_BUFFER_GROWTH, metadata->allocated)) {
71         return false;
72       }
73 
74       size_t new_allocation = metadata->allocated == 0
75                                   ? 1
76                                   : CBOR_BUFFER_GROWTH * metadata->allocated;
77 
78       unsigned char *new_data = _cbor_realloc_multiple(
79           item->data, sizeof(struct cbor_pair), new_allocation);
80 
81       if (new_data == NULL) {
82         return false;
83       }
84 
85       item->data = new_data;
86       metadata->allocated = new_allocation;
87     }
88     struct cbor_pair *data = cbor_map_handle(item);
89     data[metadata->end_ptr].key = key;
90     data[metadata->end_ptr++].value = NULL;
91   }
92   cbor_incref(key);
93   return true;
94 }
95 
96 bool _cbor_map_add_value(cbor_item_t *item, cbor_item_t *value) {
97   assert(cbor_isa_map(item));
98   cbor_incref(value);
99   cbor_map_handle(item)[
100       /* Move one back since we are assuming _add_key (which increased the ptr)
101        * was the previous operation on this object */
102       item->metadata.map_metadata.end_ptr - 1]
103       .value = value;
104   return true;
105 }
106 
107 bool cbor_map_add(cbor_item_t *item, struct cbor_pair pair) {
108   assert(cbor_isa_map(item));
109   if (!_cbor_map_add_key(item, pair.key)) return false;
110   return _cbor_map_add_value(item, pair.value);
111 }
112 
113 bool cbor_map_is_definite(const cbor_item_t *item) {
114   assert(cbor_isa_map(item));
115   return item->metadata.map_metadata.type == _CBOR_METADATA_DEFINITE;
116 }
117 
118 bool cbor_map_is_indefinite(const cbor_item_t *item) {
119   return !cbor_map_is_definite(item);
120 }
121 
122 struct cbor_pair *cbor_map_handle(const cbor_item_t *item) {
123   assert(cbor_isa_map(item));
124   return (struct cbor_pair *)item->data;
125 }
126