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
12size_t cbor_array_size(const cbor_item_t *item) {
13  CBOR_ASSERT(cbor_isa_array(item));
14  return item->metadata.array_metadata.end_ptr;
15}
16
17size_t cbor_array_allocated(const cbor_item_t *item) {
18  CBOR_ASSERT(cbor_isa_array(item));
19  return item->metadata.array_metadata.allocated;
20}
21
22cbor_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
26bool 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}
35
36bool cbor_array_replace(cbor_item_t *item, size_t index, cbor_item_t *value) {
37  if (index >= item->metadata.array_metadata.end_ptr) return false;
38  /* We cannot use cbor_array_get as that would increase the refcount */
39  cbor_intermediate_decref(((cbor_item_t **)item->data)[index]);
40  ((cbor_item_t **)item->data)[index] = cbor_incref(value);
41  return true;
42}
43
44bool cbor_array_push(cbor_item_t *array, cbor_item_t *pushee) {
45  CBOR_ASSERT(cbor_isa_array(array));
46  struct _cbor_array_metadata *metadata =
47      (struct _cbor_array_metadata *)&array->metadata;
48  cbor_item_t **data = (cbor_item_t **)array->data;
49  if (cbor_array_is_definite(array)) {
50    /* Do not reallocate definite arrays */
51    if (metadata->end_ptr >= metadata->allocated) {
52      return false;
53    }
54    data[metadata->end_ptr++] = pushee;
55  } else {
56    /* Exponential realloc */
57    if (metadata->end_ptr >= metadata->allocated) {
58      // Check for overflows first
59      if (!_cbor_safe_to_multiply(CBOR_BUFFER_GROWTH, metadata->allocated)) {
60        return false;
61      }
62
63      size_t new_allocation = metadata->allocated == 0
64                                  ? 1
65                                  : CBOR_BUFFER_GROWTH * metadata->allocated;
66
67      unsigned char *new_data = _cbor_realloc_multiple(
68          array->data, sizeof(cbor_item_t *), new_allocation);
69      if (new_data == NULL) {
70        return false;
71      }
72
73      array->data = new_data;
74      metadata->allocated = new_allocation;
75    }
76    ((cbor_item_t **)array->data)[metadata->end_ptr++] = pushee;
77  }
78  cbor_incref(pushee);
79  return true;
80}
81
82bool cbor_array_is_definite(const cbor_item_t *item) {
83  CBOR_ASSERT(cbor_isa_array(item));
84  return item->metadata.array_metadata.type == _CBOR_METADATA_DEFINITE;
85}
86
87bool cbor_array_is_indefinite(const cbor_item_t *item) {
88  CBOR_ASSERT(cbor_isa_array(item));
89  return item->metadata.array_metadata.type == _CBOR_METADATA_INDEFINITE;
90}
91
92cbor_item_t **cbor_array_handle(const cbor_item_t *item) {
93  CBOR_ASSERT(cbor_isa_array(item));
94  return (cbor_item_t **)item->data;
95}
96
97cbor_item_t *cbor_new_definite_array(size_t size) {
98  cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t));
99  _CBOR_NOTNULL(item);
100  cbor_item_t **data = _cbor_alloc_multiple(sizeof(cbor_item_t *), size);
101  _CBOR_DEPENDENT_NOTNULL(item, data);
102
103  for (size_t i = 0; i < size; i++) {
104    data[i] = NULL;
105  }
106
107  *item = (cbor_item_t){
108      .refcount = 1,
109      .type = CBOR_TYPE_ARRAY,
110      .metadata = {.array_metadata = {.type = _CBOR_METADATA_DEFINITE,
111                                      .allocated = size,
112                                      .end_ptr = 0}},
113      .data = (unsigned char *)data};
114
115  return item;
116}
117
118cbor_item_t *cbor_new_indefinite_array(void) {
119  cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t));
120  _CBOR_NOTNULL(item);
121
122  *item = (cbor_item_t){
123      .refcount = 1,
124      .type = CBOR_TYPE_ARRAY,
125      .metadata = {.array_metadata = {.type = _CBOR_METADATA_INDEFINITE,
126                                      .allocated = 0,
127                                      .end_ptr = 0}},
128      .data = NULL /* Can be safely realloc-ed */
129  };
130  return item;
131}
132