1/*
2 * Copyright (c) 2014-2019 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 "bytestrings.h"
9#include <string.h>
10#include "internal/memory_utils.h"
11
12size_t cbor_bytestring_length(const cbor_item_t *item) {
13  assert(cbor_isa_bytestring(item));
14  return item->metadata.bytestring_metadata.length;
15}
16
17unsigned char *cbor_bytestring_handle(const cbor_item_t *item) {
18  assert(cbor_isa_bytestring(item));
19  return item->data;
20}
21
22bool cbor_bytestring_is_definite(const cbor_item_t *item) {
23  assert(cbor_isa_bytestring(item));
24  return item->metadata.bytestring_metadata.type == _CBOR_METADATA_DEFINITE;
25}
26
27bool cbor_bytestring_is_indefinite(const cbor_item_t *item) {
28  return !cbor_bytestring_is_definite(item);
29}
30
31cbor_item_t *cbor_new_definite_bytestring() {
32  cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t));
33  _CBOR_NOTNULL(item);
34  *item = (cbor_item_t){
35      .refcount = 1,
36      .type = CBOR_TYPE_BYTESTRING,
37      .metadata = {.bytestring_metadata = {_CBOR_METADATA_DEFINITE, 0}}};
38  return item;
39}
40
41cbor_item_t *cbor_new_indefinite_bytestring() {
42  cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t));
43  _CBOR_NOTNULL(item);
44  *item = (cbor_item_t){
45      .refcount = 1,
46      .type = CBOR_TYPE_BYTESTRING,
47      .metadata = {.bytestring_metadata = {.type = _CBOR_METADATA_INDEFINITE,
48                                           .length = 0}},
49      .data = _CBOR_MALLOC(sizeof(struct cbor_indefinite_string_data))};
50  _CBOR_DEPENDENT_NOTNULL(item, item->data);
51  *((struct cbor_indefinite_string_data *)item->data) =
52      (struct cbor_indefinite_string_data){
53          .chunk_count = 0,
54          .chunk_capacity = 0,
55          .chunks = NULL,
56      };
57  return item;
58}
59
60cbor_item_t *cbor_build_bytestring(cbor_data handle, size_t length) {
61  cbor_item_t *item = cbor_new_definite_bytestring();
62  _CBOR_NOTNULL(item);
63  void *content = _CBOR_MALLOC(length);
64  _CBOR_DEPENDENT_NOTNULL(item, content);
65  memcpy(content, handle, length);
66  cbor_bytestring_set_handle(item, content, length);
67  return item;
68}
69
70void cbor_bytestring_set_handle(cbor_item_t *item,
71                                cbor_mutable_data CBOR_RESTRICT_POINTER data,
72                                size_t length) {
73  assert(cbor_isa_bytestring(item));
74  assert(cbor_bytestring_is_definite(item));
75  item->data = data;
76  item->metadata.bytestring_metadata.length = length;
77}
78
79cbor_item_t **cbor_bytestring_chunks_handle(const cbor_item_t *item) {
80  assert(cbor_isa_bytestring(item));
81  assert(cbor_bytestring_is_indefinite(item));
82  return ((struct cbor_indefinite_string_data *)item->data)->chunks;
83}
84
85size_t cbor_bytestring_chunk_count(const cbor_item_t *item) {
86  assert(cbor_isa_bytestring(item));
87  assert(cbor_bytestring_is_indefinite(item));
88  return ((struct cbor_indefinite_string_data *)item->data)->chunk_count;
89}
90
91bool cbor_bytestring_add_chunk(cbor_item_t *item, cbor_item_t *chunk) {
92  assert(cbor_isa_bytestring(item));
93  assert(cbor_bytestring_is_indefinite(item));
94  struct cbor_indefinite_string_data *data =
95      (struct cbor_indefinite_string_data *)item->data;
96  if (data->chunk_count == data->chunk_capacity) {
97    // TODO: Add a test for this
98    if (!_cbor_safe_to_multiply(CBOR_BUFFER_GROWTH, data->chunk_capacity)) {
99      return false;
100    }
101
102    size_t new_chunk_capacity =
103        data->chunk_capacity == 0 ? 1
104                                  : CBOR_BUFFER_GROWTH * (data->chunk_capacity);
105
106    cbor_item_t **new_chunks_data = _cbor_realloc_multiple(
107        data->chunks, sizeof(cbor_item_t *), new_chunk_capacity);
108
109    if (new_chunks_data == NULL) {
110      return false;
111    }
112    data->chunk_capacity = new_chunk_capacity;
113    data->chunks = new_chunks_data;
114  }
115  data->chunks[data->chunk_count++] = cbor_incref(chunk);
116  return true;
117}
118