xref: /freebsd/contrib/libcbor/src/cbor/maps.c (revision 56b17de1e8360fe131d425de20b5e75ff3ea897c)
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   CBOR_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   CBOR_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(void) {
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   CBOR_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       if (!_cbor_safe_to_multiply(CBOR_BUFFER_GROWTH, metadata->allocated)) {
70         return false;
71       }
72 
73       size_t new_allocation = metadata->allocated == 0
74                                   ? 1
75                                   : CBOR_BUFFER_GROWTH * metadata->allocated;
76 
77       unsigned char *new_data = _cbor_realloc_multiple(
78           item->data, sizeof(struct cbor_pair), new_allocation);
79 
80       if (new_data == NULL) {
81         return false;
82       }
83 
84       item->data = new_data;
85       metadata->allocated = new_allocation;
86     }
87     struct cbor_pair *data = cbor_map_handle(item);
88     data[metadata->end_ptr].key = key;
89     data[metadata->end_ptr++].value = NULL;
90   }
91   cbor_incref(key);
92   return true;
93 }
94 
95 bool _cbor_map_add_value(cbor_item_t *item, cbor_item_t *value) {
96   CBOR_ASSERT(cbor_isa_map(item));
97   cbor_incref(value);
98   cbor_map_handle(item)[
99       /* Move one back since we are assuming _add_key (which increased the ptr)
100        * was the previous operation on this object */
101       item->metadata.map_metadata.end_ptr - 1]
102       .value = value;
103   return true;
104 }
105 
106 // TODO: Add a more convenient API like add(item, key, val)
107 bool cbor_map_add(cbor_item_t *item, struct cbor_pair pair) {
108   CBOR_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   CBOR_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   CBOR_ASSERT(cbor_isa_map(item));
124   return (struct cbor_pair *)item->data;
125 }
126