blob: 6c70129d8e2f0732c04aec8a2de9ea5c2c51972c [file] [log] [blame]
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#include "dice/cbor_writer.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
enum CborType {
CBOR_TYPE_UINT = 0,
CBOR_TYPE_NINT = 1,
CBOR_TYPE_BSTR = 2,
CBOR_TYPE_TSTR = 3,
CBOR_TYPE_ARRAY = 4,
CBOR_TYPE_MAP = 5,
CBOR_TYPE_TAG = 6,
CBOR_TYPE_SIMPLE = 7,
};
static bool CborWriteWouldOverflowCursor(size_t size, struct CborOut* out) {
return size > SIZE_MAX - out->cursor;
}
static bool CborWriteFitsInBuffer(size_t size, struct CborOut* out) {
return out->cursor <= out->buffer_size &&
size <= out->buffer_size - out->cursor;
}
static void CborWriteType(enum CborType type, uint64_t val,
struct CborOut* out) {
size_t size;
if (val <= 23) {
size = 1;
} else if (val <= 0xff) {
size = 2;
} else if (val <= 0xffff) {
size = 3;
} else if (val <= 0xffffffff) {
size = 5;
} else {
size = 9;
}
if (CborWriteWouldOverflowCursor(size, out)) {
out->cursor = SIZE_MAX;
return;
}
if (CborWriteFitsInBuffer(size, out)) {
if (size == 1) {
out->buffer[out->cursor] = (uint8_t)((type << 5) | val);
} else if (size == 2) {
out->buffer[out->cursor] = (uint8_t)((type << 5) | 24);
out->buffer[out->cursor + 1] = val & 0xff;
} else if (size == 3) {
out->buffer[out->cursor] = (uint8_t)((type << 5) | 25);
out->buffer[out->cursor + 1] = (val >> 8) & 0xff;
out->buffer[out->cursor + 2] = val & 0xff;
} else if (size == 5) {
out->buffer[out->cursor] = (uint8_t)((type << 5) | 26);
out->buffer[out->cursor + 1] = (val >> 24) & 0xff;
out->buffer[out->cursor + 2] = (val >> 16) & 0xff;
out->buffer[out->cursor + 3] = (val >> 8) & 0xff;
out->buffer[out->cursor + 4] = val & 0xff;
} else if (size == 9) {
out->buffer[out->cursor] = (uint8_t)((type << 5) | 27);
out->buffer[out->cursor + 1] = (val >> 56) & 0xff;
out->buffer[out->cursor + 2] = (val >> 48) & 0xff;
out->buffer[out->cursor + 3] = (val >> 40) & 0xff;
out->buffer[out->cursor + 4] = (val >> 32) & 0xff;
out->buffer[out->cursor + 5] = (val >> 24) & 0xff;
out->buffer[out->cursor + 6] = (val >> 16) & 0xff;
out->buffer[out->cursor + 7] = (val >> 8) & 0xff;
out->buffer[out->cursor + 8] = val & 0xff;
}
}
out->cursor += size;
}
static void* CborAllocStr(enum CborType type, size_t data_size,
struct CborOut* out) {
CborWriteType(type, data_size, out);
bool overflow = CborWriteWouldOverflowCursor(data_size, out);
bool fit = CborWriteFitsInBuffer(data_size, out);
void* ptr = (overflow || !fit) ? NULL : &out->buffer[out->cursor];
out->cursor = overflow ? SIZE_MAX : out->cursor + data_size;
return ptr;
}
static void CborWriteStr(enum CborType type, size_t data_size, const void* data,
struct CborOut* out) {
uint8_t* ptr = CborAllocStr(type, data_size, out);
if (ptr && data_size) {
memcpy(ptr, data, data_size);
}
}
void CborWriteInt(int64_t val, struct CborOut* out) {
if (val < 0) {
CborWriteType(CBOR_TYPE_NINT, (uint64_t)(-1 - val), out);
} else {
CborWriteType(CBOR_TYPE_UINT, (uint64_t)val, out);
}
}
void CborWriteUint(uint64_t val, struct CborOut* out) {
CborWriteType(CBOR_TYPE_UINT, val, out);
}
void CborWriteBstr(size_t data_size, const uint8_t* data, struct CborOut* out) {
CborWriteStr(CBOR_TYPE_BSTR, data_size, data, out);
}
uint8_t* CborAllocBstr(size_t data_size, struct CborOut* out) {
return CborAllocStr(CBOR_TYPE_BSTR, data_size, out);
}
void CborWriteTstr(const char* str, struct CborOut* out) {
CborWriteStr(CBOR_TYPE_TSTR, strlen(str), str, out);
}
char* CborAllocTstr(size_t size, struct CborOut* out) {
return CborAllocStr(CBOR_TYPE_TSTR, size, out);
}
void CborWriteArray(size_t num_elements, struct CborOut* out) {
CborWriteType(CBOR_TYPE_ARRAY, num_elements, out);
}
void CborWriteMap(size_t num_pairs, struct CborOut* out) {
CborWriteType(CBOR_TYPE_MAP, num_pairs, out);
}
void CborWriteTag(uint64_t tag, struct CborOut* out) {
CborWriteType(CBOR_TYPE_TAG, tag, out);
}
void CborWriteFalse(struct CborOut* out) {
CborWriteType(CBOR_TYPE_SIMPLE, /*val=*/20, out);
}
void CborWriteTrue(struct CborOut* out) {
CborWriteType(CBOR_TYPE_SIMPLE, /*val=*/21, out);
}
void CborWriteNull(struct CborOut* out) {
CborWriteType(CBOR_TYPE_SIMPLE, /*val=*/22, out);
}