This repository has been archived on 2025-03-27. You can view files and clone it, but cannot push or open issues or pull requests.

1472 lines
45 KiB
C++

#include "lib.h"
#include "rawlist.cc"
TypeTree DB_TYPE;
NameTree DB_NAME;
SparseList<Type::SchemaBinding> DB_SCHEMA;
Pool<Reference> DB_REFERENCE;
extern "C" void test() { }
extern "C" size_t type_outer(size_t type_id, size_t key)
{
return DB_TYPE.outer(type_id, key);
}
extern "C" size_t type_inner(size_t type_id)
{
return DB_TYPE.inner(type_id);
}
extern "C" size_t type_key(size_t type_id)
{
return DB_TYPE.key(type_id);
}
extern "C" size_t type_innerkey(size_t type_id)
{
return DB_TYPE.key(DB_TYPE.inner(type_id));
}
extern "C" size_t type_size(size_t type_id)
{
if(DB_TYPE.has(type_id)) {
size_t type = DB_TYPE.key(type_id);
switch(type) {
case Type::Tag::Null: return 0;
case Type::Tag::Varying: return sizeof(Reference);
case Type::Tag::Boolean: return sizeof(Type::Boolean);
case Type::Tag::Natural: return sizeof(Type::Natural);
case Type::Tag::Integer: return sizeof(Type::Integer);
case Type::Tag::Significant: return sizeof(Type::Significant);
case Type::Tag::Decimal: return sizeof(Type::Decimal);
case Type::Tag::Block: return type_innerkey(type_id);
case Type::Tag::Sequence: return sizeof(Type::Sequence);
case Type::Tag::Array: {
size_t length = DB_TYPE.inner(type_id);
size_t inner = DB_TYPE.inner(length);
if(inner == Type::Tag::Null) {
return sizeof(Reference);
}
else {
return static_cast<size_t>(DB_TYPE.key(length)) * type_size(inner);
}
}
case Type::Tag::List:
case Type::Tag::Sparse:
case Type::Tag::Record:
{
size_t innerkey = type_innerkey(type_id);
if(innerkey == Type::Tag::Null) {
return sizeof(Reference);
}
else {
switch(type) {
case Type::Tag::List: return sizeof(Type::List);
case Type::Tag::Sparse: return sizeof(Type::Sparse);
case Type::Tag::Record: {
auto binding = DB_SCHEMA.get(type_innerkey(type_id));
if(binding != nullptr) {
return binding->size;
}
}
}
}
}
case Type::Tag::Schema: return sizeof(Type::Schema);
default: return 0;
}
}
return 0;
}
size_t type_alignment(size_t type_id)
{
if(DB_TYPE.has(type_id)) {
size_t type = DB_TYPE.key(type_id);
switch(type) {
case Type::Tag::Null: return 0;
case Type::Tag::Boolean: return sizeof(Type::Boolean);
case Type::Tag::Natural: return sizeof(Type::Natural);
case Type::Tag::Integer: return sizeof(Type::Integer);
case Type::Tag::Significant: return sizeof(Type::Significant);
case Type::Tag::Decimal: return sizeof(Type::Decimal);
case Type::Tag::Block: return sizeof(uint8_t);
case Type::Tag::Array: return type_alignment(type_inner(type_id));
case Type::Tag::Varying:
case Type::Tag::Sequence:
case Type::Tag::List:
case Type::Tag::Sparse:
case Type::Tag::Schema:
return sizeof(size_t);
case Type::Tag::Record: {
auto binding = DB_SCHEMA.get(type_innerkey(type_id));
return (binding != nullptr)? binding->alignment : 0;
}
}
}
return 0;
}
extern "C" size_t kind_hasinner(size_t kind)
{
switch(kind) {
case Type::Tag::Array:
return 2;
case Type::Tag::Decimal:
case Type::Tag::Block:
case Type::Tag::List:
case Type::Tag::Sparse:
case Type::Tag::Record:
return 1;
}
return 0;
}
extern "C" size_t type_hasinner(size_t type_id)
{
return kind_hasinner(type_key(type_id));
}
extern "C" size_t name_indexof(const uint8_t* bytes, size_t length)
{
std::string str(reinterpret_cast<const char*>(bytes), length);
if(str.size() > 0) {
return DB_NAME.indexof(str);
}
return 0;
}
extern "C" Str name_keyof(size_t index)
{
Str result {0};
std::string str = DB_NAME.keyof(index);
if(str.length() > 0) {
result.bytes = new uint8_t[str.length()];
}
for(size_t i = 0; i < str.length(); ++i) {
result.bytes[i] = str[i];
}
result.length = str.length();
return result;
}
std::string name_keyof_internal(size_t index)
{
return DB_NAME.keyof(index);
}
extern "C" void name_release(Str data)
{
if(data.bytes != nullptr) {
delete[] data.bytes;
}
}
uint8_t* allocate(size_t type_id, size_t count)
{
uint8_t* mem = nullptr;
size_t size = type_size(type_id) * count;
if(size > 0) {
mem = reinterpret_cast<uint8_t*>(malloc(size));
if(mem != nullptr) {
memset(mem, 0, size);
}
return mem;
}
return nullptr;
}
extern "C" Reference acquire(size_t type_id)
{
Reference addr {0};
addr.address = allocate(type_id, 1);
if(addr.address != nullptr) {
addr.type = type_id;
}
return addr;
}
void drop(Reference addr)
{
if(addr.address != nullptr) {
switch(type_key(addr.type)) {
case Type::Tag::Varying: {
varying_clear(addr);
} break;
case Type::Tag::Sequence: {
sequence_clear(addr);
} break;
case Type::Tag::List: {
list_clear(addr);
} break;
case Type::Tag::Sparse: {
sparse_clear(addr);
} break;
case Type::Tag::Record: {
size_t schema_id = type_innerkey(addr.type);
auto binding = DB_SCHEMA.get(schema_id);
if(binding != nullptr) {
for(size_t i = 0; i < binding->data.size(); ++i) {
auto cell = record_cell(addr, i);
drop(cell);
}
}
} break;
case Type::Tag::Schema: {
auto& object = (*reinterpret_cast<Type::Schema*>(addr.address));
rawlist_clear(object.data);
rawlist_clear(object.map);
} break;
}
memset(addr.address, 0, type_size(addr.type));
}
}
extern "C" void release(Reference addr)
{
if(addr.address != nullptr) {
drop(addr);
free(addr.address);
}
}
extern "C" bool copy(Reference dst, Reference src)
{
if(src.address != dst.address) {
Reference source = src;
Reference destination = dst;
// dereference varying data
if(type_key(src.type) == Type::Tag::Varying) {
source = *reinterpret_cast<Reference*>(src.address);
}
// prepare destination for varying data
if(type_key(dst.type) == Type::Tag::Varying) {
auto& dest_ref = *reinterpret_cast<Reference*>(dst.address);
// determine if memory can be reused, otherwise free and reallocate
if(source.type != dest_ref.type) {
if(dest_ref.address != nullptr) {
free(dest_ref.address);
dest_ref.type = Type::Tag::Null;
dest_ref.address = nullptr;
}
dest_ref = acquire(source.type);
}
}
// copy data into destination
if(source.type == destination.type) {
drop(destination);
switch(type_key(destination.type)) {
case Type::Tag::Null: { } break;
case Type::Tag::Sequence: {
auto& src_seq = *reinterpret_cast<Type::Sequence*>(source.address);
auto& dst_seq = *reinterpret_cast<Type::Sequence*>(destination.address);
rawlist_clear(dst_seq.data);
rawlist_reserve(dst_seq.data, sizeof(uint8_t), src_seq.data.length);
memcpy(dst_seq.data.data, src_seq.data.data, sizeof(uint8_t) * src_seq.data.length);
dst_seq.data.length = src_seq.data.length;
} break;
case Type::Tag::Array: {
for(size_t i = 0; i < array_length(source); ++i) {
copy(array_cell(destination, i), array_cell(source, i));
}
} break;
case Type::Tag::List: {
auto& src_list = *reinterpret_cast<Type::List*>(source.address);
auto& dst_list = *reinterpret_cast<Type::List*>(destination.address);
rawlist_reserve(dst_list.data, type_size(type_inner(source.type)), src_list.data.capacity);
dst_list.data.length = src_list.data.length;
for(size_t i = 0; i < src_list.data.length; ++i) {
copy(list_at(destination, i), list_at(source, i));
}
} break;
case Type::Tag::Sparse: {
auto& src_list = *reinterpret_cast<Type::Sparse*>(source.address);
auto& dst_list = *reinterpret_cast<Type::Sparse*>(destination.address);
rawlist_reserve(dst_list.header, sizeof(Type::SparseHeader), src_list.header.capacity);
dst_list.header.length = src_list.header.length;
for(size_t i = 0; i < src_list.header.length; ++i) {
Type::SparseHeader& src_header = *reinterpret_cast<Type::SparseHeader*>(rawlist_cell(src_list.header, sizeof(Type::SparseHeader), i));
Type::SparseHeader& dst_header = *reinterpret_cast<Type::SparseHeader*>(rawlist_cell(dst_list.header, sizeof(Type::SparseHeader), i));
dst_header = src_header;
}
rawlist_reserve(dst_list.data, type_size(type_inner(source.type)), src_list.data.capacity);
dst_list.data.length = src_list.data.length;
for(size_t i = 0; i < src_list.data.length; ++i) {
copy(list_at(destination, i), list_at(source, i));
}
} break;
case Type::Tag::Record: {
size_t schema_id = type_innerkey(source.type);
auto binding = DB_SCHEMA.get(schema_id);
if(binding != nullptr) {
for(size_t i = 0; i < binding->data.size(); ++i) {
auto src_cell = record_cell(source, i);
auto dst_cell = record_cell(destination, i);
copy(dst_cell, src_cell);
}
}
} break;
case Type::Tag::Schema: {
auto& src_schema = *reinterpret_cast<Type::Schema*>(source.address);
auto& dst_schema = *reinterpret_cast<Type::Schema*>(destination.address);
rawlist_clear(dst_schema.data);
rawlist_clear(dst_schema.map);
rawlist_reserve(dst_schema.data, sizeof(size_t), src_schema.data.length);
rawlist_reserve(dst_schema.map, sizeof(size_t), src_schema.map.length);
for(size_t i = 0; i < src_schema.data.length; ++i) {
auto src_cell = reinterpret_cast<size_t*>(rawlist_cell(src_schema.data, sizeof(size_t), i));
auto dst_cell = reinterpret_cast<size_t*>(rawlist_cell(dst_schema.data, sizeof(size_t), i));
if(src_cell != nullptr && dst_cell != nullptr) {
*dst_cell = *src_cell;
}
}
for(size_t i = 0; i < src_schema.map.length; ++i) {
auto src_cell = reinterpret_cast<size_t*>(rawlist_cell(src_schema.map, sizeof(size_t), i));
auto dst_cell = reinterpret_cast<size_t*>(rawlist_cell(dst_schema.map, sizeof(size_t), i));
if(src_cell != nullptr && dst_cell != nullptr) {
*dst_cell = *src_cell;
}
}
} break;
default: {
memcpy(destination.address, source.address, type_size(source.type));
} break;
}
return true;
}
}
return false;
}
extern "C" bool transfer(Reference dst, Reference src)
{
if(src.address != dst.address && src.type != 0 && dst.type != 0) {
Reference source = src;
Reference destination = dst;
// dereference varying data
if(type_key(src.type) == Type::Tag::Varying) {
source = *reinterpret_cast<Reference*>(src.address);
}
// prepare destination for varying data
if(type_key(dst.type) == Type::Tag::Varying) {
auto& dest_ref = *reinterpret_cast<Reference*>(dst.address);
// determine if memory can be reused, otherwise free and reallocate
if(source.type != dest_ref.type) {
if(dest_ref.address != nullptr) {
free(dest_ref.address);
dest_ref.type = Type::Tag::Null;
dest_ref.address = nullptr;
}
dest_ref = acquire(source.type);
}
}
// copy data into destination
if(source.type == destination.type) {
drop(destination);
memcpy(destination.address, source.address, type_size(source.type));
memset(source.address, 0, type_size(source.type));
return true;
}
}
return false;
}
Reference resolve_addr(Reference addr)
{
Reference result = addr;
if(result.type == Type::Tag::Null) {
if(result.address != nullptr) {
result = *reinterpret_cast<Reference*>(result.address);
}
else {
result = Reference {0};
}
}
return result;
}
// Varying //
extern "C" Reference varying_get(Reference addr)
{
Reference result {0};
result = *reinterpret_cast<Reference*>(addr.address);
return result;
}
extern "C" void varying_set(Reference addr, Reference source)
{
Reference& var = *reinterpret_cast<Reference*>(addr.address);
if(var.address != nullptr) {
drop(var);
}
if(var.type != source.type || var.address == nullptr) {
if(var.address != nullptr) {
free(var.address);
}
var.type = source.type;
var.address = allocate(source.type, 1);
}
copy(var, source);
}
extern "C" void varying_clear(Reference addr)
{
Reference& var = *reinterpret_cast<Reference*>(addr.address);
if(var.address != nullptr) {
drop(var);
free(var.address);
}
var.type = 0;
var.address = nullptr;
}
// Boolean //
extern "C" void bool_set(Reference addr, Type::Boolean value)
{
*(reinterpret_cast<Type::Boolean*>(addr.address)) = value;
}
extern "C" Type::Boolean bool_get(Reference addr)
{
return *(reinterpret_cast<Type::Boolean*>(addr.address));
}
// Natural //
extern "C" void natural_set(Reference addr, Type::Natural value)
{
*(reinterpret_cast<Type::Natural*>(addr.address)) = value;
}
extern "C" Type::Natural natural_get(Reference addr)
{
return *(reinterpret_cast<Type::Natural*>(addr.address));
}
// Integer //
extern "C" void integer_set(Reference addr, Type::Integer value)
{
*(reinterpret_cast<Type::Integer*>(addr.address)) = value;
}
extern "C" Type::Integer integer_get(Reference addr)
{
return *(reinterpret_cast<Type::Integer*>(addr.address));
}
// Significant //
extern "C" void significant_set(Reference addr, Type::Significant value)
{
*(reinterpret_cast<Type::Significant*>(addr.address)) = value;
}
extern "C" Type::Significant significant_get(Reference addr)
{
return *(reinterpret_cast<Type::Significant*>(addr.address));
}
// Decimal //
extern "C" void decimal_set(Reference addr, Type::Decimal value)
{
*(reinterpret_cast<Type::Decimal*>(addr.address)) = value;
}
extern "C" Type::Decimal decimal_get(Reference addr)
{
return *(reinterpret_cast<Type::Decimal*>(addr.address));
}
// Block //
extern "C" size_t block_length(Reference addr)
{
return type_innerkey(addr.type);
}
extern "C" uint8_t block_get(Reference addr, size_t index)
{
size_t length = type_innerkey(addr.type);
if(index < length) {
return reinterpret_cast<uint8_t*>(addr.address)[index];
}
return 0;
}
extern "C" uint64_t block_get_natural(Reference addr)
{
size_t length = type_innerkey(addr.type);
uint8_t* data = reinterpret_cast<uint8_t*>(addr.address);
uint64_t result = 0;
for(size_t i = 0; i < length; ++i) {
result <<= 8;
result |= data[i];
}
return result;
}
extern "C" int64_t block_get_integer(Reference addr)
{
size_t length = type_innerkey(addr.type);
uint8_t* data = reinterpret_cast<uint8_t*>(addr.address);
int64_t result = 0;
for(size_t i = 0; i < length; ++i) {
result <<= 8;
result |= data[i];
}
return result;
}
extern "C" uint64_t block_get_field(Reference addr, size_t index, size_t length)
{
size_t data_length = type_innerkey(addr.type);
size_t byte_start = index >> 3;
size_t bit_offset = index & 0x07;
size_t byte_length = 1 + ((bit_offset + length) >> 3);
size_t byte_end = byte_start + byte_length;
size_t mask_offset = ((8 * byte_length) - (length + bit_offset));
uint64_t mask = ((1 << length) - 1) << mask_offset;
uint64_t bits = 0;
uint8_t* data = reinterpret_cast<uint8_t*>(addr.address);
size_t i = byte_start;
for(; i < data_length && i < byte_end; ++i) {
bits <<= 8;
bits |= data[i];
}
for(; i < byte_end; ++i) {
bits <<= 8;
}
return (bits & mask) >> mask_offset;
}
extern "C" void block_set(Reference addr, size_t index, uint8_t value)
{
size_t length = type_innerkey(addr.type);
if(index < length) {
reinterpret_cast<uint8_t*>(addr.address)[index] = value;
}
}
extern "C" void block_set_natural(Reference addr, uint64_t data)
{
size_t length = type_innerkey(addr.type);
uint8_t* dest = reinterpret_cast<uint8_t*>(addr.address);
for(size_t i = length; i > 0; --i) {
dest[i - 1] = data & 0xFF;
data >>= 8;
}
}
extern "C" void block_set_integer(Reference addr, int64_t data)
{
size_t length = type_innerkey(addr.type);
uint8_t* dest = reinterpret_cast<uint8_t*>(addr.address);
for(size_t i = length; i > 0; --i) {
dest[i - 1] = data & 0xFF;
data >>= 8;
}
}
extern "C" void block_set_field(Reference addr, size_t index, size_t length, uint64_t value)
{
size_t data_length = type_innerkey(addr.type);
size_t byte_start = index >> 3;
size_t bit_offset = index & 0x07;
size_t byte_length = 1 + ((bit_offset + length) >> 3);
size_t byte_end = byte_start + byte_length;
size_t mask_offset = ((8 * byte_length) - (length + bit_offset));
uint64_t mask = ((1 << length) - 1) << mask_offset;
uint64_t bits = 0;
uint8_t* data = reinterpret_cast<uint8_t*>(addr.address);
size_t byte_index = byte_start;
for(; byte_index < data_length && byte_index < byte_end; ++byte_index) {
bits <<= 8;
bits |= data[byte_index];
}
for(; byte_index < byte_end; ++byte_index) {
bits <<= 8;
}
bits &= ~mask;
bits |= mask & (value << mask_offset);
for(; byte_index > data_length; --byte_index) {
bits >>= 8;
}
for(; byte_index > byte_start; --byte_index) {
data[byte_index - 1] = bits & 0xFF;
bits >>= 8;
}
}
// Sequence //
extern "C" size_t sequence_capacity(Reference addr)
{
Type::Sequence& seq = *reinterpret_cast<Type::Sequence*>(addr.address);
return seq.data.capacity;
}
extern "C" size_t sequence_length(Reference addr)
{
Type::Sequence& seq = *reinterpret_cast<Type::Sequence*>(addr.address);
return seq.data.length;
}
extern "C" uint8_t sequence_get(Reference addr, size_t index)
{
Type::Sequence& seq = *reinterpret_cast<Type::Sequence*>(addr.address);
if(index < seq.data.length) {
return *reinterpret_cast<uint8_t*>(rawlist_cell(seq.data, 1, index));
}
return 0;
}
extern "C" void sequence_clear(Reference addr)
{
Type::Sequence& seq = *reinterpret_cast<Type::Sequence*>(addr.address);
rawlist_clear(seq.data);
}
extern "C" void sequence_set(Reference addr, size_t index, uint8_t value)
{
Type::Sequence& seq = *reinterpret_cast<Type::Sequence*>(addr.address);
auto cell = reinterpret_cast<uint8_t*>(rawlist_cell(seq.data, 1, index));
if(cell != nullptr) {
*cell = value;
}
}
extern "C" void sequence_insert(Reference addr, size_t index, uint8_t value)
{
Type::Sequence& seq = *reinterpret_cast<Type::Sequence*>(addr.address);
auto cell = reinterpret_cast<uint8_t*>(rawlist_insert(seq.data, 1, index));
if(cell != nullptr) {
*cell = value;
}
}
extern "C" void sequence_reserve(Reference addr, size_t capacity)
{
Type::Sequence& seq = *reinterpret_cast<Type::Sequence*>(addr.address);
rawlist_reserve(seq.data, 1, capacity);
}
// Array //
extern "C" size_t array_length(Reference addr)
{
return type_innerkey(addr.type);
}
Reference array_cell(Reference addr, size_t index)
{
Reference result {0};
size_t length_n = type_inner(addr.type);
size_t length = type_key(length_n);
size_t type = type_inner(length_n);
size_t offset = type_size(type);
// validate for overflow
if(addr.address != nullptr && offset > 0 && index < length) {
result.type = type;
result.address = addr.address + (offset * index);
}
return result;
}
extern "C" Reference array_at(Reference addr, size_t index)
{
Reference result {0};
Reference cell = array_cell(addr, index);
if(cell.address != nullptr) {
if(type_key(cell.type) == Type::Tag::Varying) {
result = varying_get(cell);
}
else {
result = cell;
}
}
return result;
}
extern "C" void array_update(Reference addr, size_t index, Reference source)
{
Reference cell = array_cell(addr, index);
if(type_key(cell.type) == Type::Tag::Varying) {
varying_set(cell, source);
}
else {
copy(cell, source);
}
}
// List //
extern "C" size_t list_capacity(Reference addr)
{
return (*reinterpret_cast<Type::List*>(addr.address)).data.capacity;
}
extern "C" size_t list_length(Reference addr)
{
return (*reinterpret_cast<Type::List*>(addr.address)).data.length;
}
Reference list_cell(Reference addr, size_t index)
{
Reference result {0};
Type::List& list = *reinterpret_cast<Type::List*>(addr.address);
size_t inner = type_inner(addr.type);
size_t offset = type_size(inner);
// validate for overflow
if(list.data.data != nullptr && offset > 0 && index < list.data.capacity) {
result.type = inner;
result.address = rawlist_cell(list.data, offset, index);
}
return result;
}
extern "C" Reference list_at(Reference addr, size_t index)
{
Reference result {0};
Reference cell = list_cell(addr, index);
if(cell.address != nullptr) {
if(type_key(cell.type) == Type::Tag::Varying) {
result = *reinterpret_cast<Reference*>(cell.address);
} else {
result = cell;
}
}
return result;
}
extern "C" void list_clear(Reference addr)
{
Type::List& list = *reinterpret_cast<Type::List*>(addr.address);
for(size_t i = 0; i < list.data.length; ++i) {
drop(list_cell(addr, i));
}
rawlist_clear(list.data);
}
extern "C" void list_insert(Reference addr, size_t index, Reference source)
{
auto& list = (*reinterpret_cast<Type::List*>(addr.address));
size_t inner = type_inner(addr.type);
size_t offset = type_size(inner);
if(index > list.data.length) { index = list.data.length; }
Reference cell {0};
cell.type = inner;
cell.address = rawlist_insert(list.data, offset, index);
if(type_key(inner) == Type::Tag::Varying) {
varying_set(cell, source);
}
else {
copy(cell, source);
}
}
extern "C" void list_prepend(Reference addr, Reference source)
{
list_insert(addr, 0, source);
}
extern "C" void list_append(Reference addr, Reference source)
{
auto& list = *reinterpret_cast<Type::List*>(addr.address);
list_insert(addr, list.data.length, source);
}
extern "C" void list_update(Reference addr, size_t index, Reference source)
{
auto& list = *reinterpret_cast<Type::List*>(addr.address);
if(index < list.data.length) {
size_t inner = type_inner(addr.type);
if(type_key(inner) == Type::Tag::Varying) {
varying_set(list_cell(addr, index), source);
}
else {
copy(list_cell(addr, index), source);
}
}
}
extern "C" void list_remove(Reference addr, size_t index)
{
auto& list = *reinterpret_cast<Type::List*>(addr.address);
size_t inner = type_inner(addr.type);
size_t offset = type_size(inner);
if(index < list.data.length) {
drop(list_at(addr, index));
rawlist_remove(list.data, offset, index);
}
}
extern "C" void list_reserve(Reference addr, size_t capacity)
{
auto& list = *reinterpret_cast<Type::List*>(addr.address);
size_t inner = type_inner(addr.type);
size_t offset = type_size(inner);
rawlist_reserve(list.data, offset, capacity);
}
// Sparse //
extern "C" size_t sparse_header_length(Reference addr)
{
auto& list = *reinterpret_cast<Type::Sparse*>(addr.address);
return list.header.length;
}
extern "C" Type::SparseHeader sparse_header_data(Reference addr, size_t index)
{
auto& list = *reinterpret_cast<Type::Sparse*>(addr.address);
Type::SparseHeader header {0};
if(index < list.header.length) {
header = *reinterpret_cast<Type::SparseHeader*>(rawlist_cell(list.header, sizeof(Type::SparseHeader), index));
}
return header;
}
extern "C" size_t sparse_length(Reference addr)
{
auto& list = *reinterpret_cast<Type::Sparse*>(addr.address);
return list.data.length;
}
extern "C" void sparse_clear(Reference addr)
{
auto& list = *reinterpret_cast<Type::Sparse*>(addr.address);
size_t inner = type_inner(addr.type);
Reference ref {0};
ref.type = inner;
for(size_t i = 0; i < list.data.length; ++i) {
ref.address = rawlist_cell(list.data, type_size(inner), i);
drop(ref);
}
rawlist_clear(list.data);
rawlist_clear(list.header);
}
extern "C" size_t sparse_first(Reference addr)
{
auto& list = *reinterpret_cast<Type::Sparse*>(addr.address);
if(list.header.length > 0) {
auto cell = reinterpret_cast<Type::SparseHeader*>(rawlist_cell(list.header, sizeof(Type::SparseHeader), 0));
if(cell != nullptr) {
return cell->start;
}
}
return 0;
}
extern "C" size_t sparse_next(Reference addr)
{
auto& list = *reinterpret_cast<Type::Sparse*>(addr.address);
if(list.header.length > 0) {
auto cell = reinterpret_cast<Type::SparseHeader*>(rawlist_cell(list.header, sizeof(Type::SparseHeader), 0));
if(cell != nullptr) {
return cell->start + cell->length;
}
}
return 0;
}
extern "C" size_t sparse_last(Reference addr)
{
auto& list = *reinterpret_cast<Type::Sparse*>(addr.address);
if(list.header.length > 0) {
auto cell = reinterpret_cast<Type::SparseHeader*>(rawlist_cell(list.header, sizeof(Type::SparseHeader), list.header.length - 1));
if(cell != nullptr) {
return cell->start + cell->length - 1;
}
}
return 0;
}
extern "C" Reference sparse_at(Reference addr, size_t index)
{
Reference result {0};
auto& list = *reinterpret_cast<Type::Sparse*>(addr.address);
size_t inner = type_inner(addr.type);
if(index < list.data.length) {
auto cell = rawlist_cell(list.data, type_size(inner), index);
if(cell != nullptr) {
result.type = inner;
result.address = cell;
}
if(type_key(inner) == Type::Tag::Varying) {
result = varying_get(result);
}
}
return result;
}
extern "C" Reference sparse_get(Reference addr, size_t index)
{
Reference result {0};
auto& list = *reinterpret_cast<Type::Sparse*>(addr.address);
size_t inner = type_inner(addr.type);
size_t bound_lower = 0;
size_t bound_upper = list.header.length;
size_t header_index = 0;
Type::SparseHeader* header = nullptr;
while(bound_lower != bound_upper) {
header_index = bound_lower + ((bound_upper - bound_lower) / 2);
header = reinterpret_cast<Type::SparseHeader*>(rawlist_cell(list.header, sizeof(Type::SparseHeader), header_index));
if(index >= header->start && index < (header->start + header->length)) {
break;
}
else if(index < header->start) {
bound_upper = header_index;
}
else {
bound_lower = header_index + 1;
}
header = nullptr;
}
if(header != nullptr) {
result.type = inner;
size_t data_index = header->index + (index - header->start);
result.address = rawlist_cell(list.data, type_size(inner), data_index);
if(type_key(result.type) == Type::Tag::Varying) {
result = varying_get(result);
}
}
return result;
}
extern "C" void sparse_set(Reference addr, size_t index, Reference source)
{
auto& list = *reinterpret_cast<Type::Sparse*>(addr.address);
size_t inner = type_inner(addr.type);
size_t header_index = 0;
Type::SparseHeader* header = nullptr;
for(; header_index < list.header.length; ++header_index) {
header = reinterpret_cast<Type::SparseHeader*>(rawlist_cell(list.header, sizeof(Type::SparseHeader), header_index));
if(index <= (header->start + header->length)) {
break;
}
}
Type::SparseHeader header_new;
Reference new_data {0};
new_data.type = inner;
if(header_index == list.header.length) {
// append header to end
header_new.start = index;
header_new.length = 1;
header_new.index = list.data.length;
new_data.address = rawlist_insert(list.data, type_size(inner), list.data.length);
copy(new_data, source);
auto& h_cell = *reinterpret_cast<Type::SparseHeader*>(rawlist_insert(list.header, sizeof(Type::SparseHeader), list.header.length));
h_cell = header_new;
} else {
if(index < header->start) {
// insert header before current
header_new.start = index;
header_new.length = 1;
header_new.index = header->index;
new_data.address = rawlist_insert(list.data, type_size(inner), header->index);
copy(new_data, source);
header = reinterpret_cast<Type::SparseHeader*>(rawlist_insert(list.header, sizeof(Type::SparseHeader), header_index));
*header = header_new;
for(size_t i = header_index + 1; i < list.header.length; ++i) {
auto hcell = reinterpret_cast<Type::SparseHeader*>(rawlist_cell(list.header, sizeof(Type::SparseHeader), i));
hcell->index++;
}
} else {
size_t offset = index - header->start;
if(offset < header->length) {
// update existing value
new_data.address = rawlist_cell(list.data, type_size(inner), header->index + offset);
drop(new_data);
copy(new_data, source);
} else {
// append value to current
new_data.address = rawlist_insert(list.data, type_size(inner), header->index + offset);
copy(new_data, source);
header->length++;
for(size_t i = header_index + 1; i < list.header.length; ++i) {
auto hcell = reinterpret_cast<Type::SparseHeader*>(rawlist_cell(list.header, sizeof(Type::SparseHeader), i));
hcell->index++;
}
}
}
// join headers if ranges intersect
if(header_index + 1 < list.header.length) {
auto next_header = reinterpret_cast<Type::SparseHeader*>(rawlist_cell(list.header, sizeof(Type::SparseHeader), header_index + 1));
if(next_header->start == header->start + header->length) {
header->length += next_header->length;
rawlist_remove(list.header, sizeof(Type::SparseHeader), header_index + 1);
}
}
}
}
extern "C" void sparse_unset(Reference addr, size_t index)
{
auto& list = *reinterpret_cast<Type::Sparse*>(addr.address);
size_t inner = type_inner(addr.type);
size_t header_index = 0;
Type::SparseHeader* header = nullptr;
for(; header_index < list.header.length; ++header_index) {
header = reinterpret_cast<Type::SparseHeader*>(rawlist_cell(list.header, sizeof(Type::SparseHeader), header_index));
if(index <= (header->start + header->length)) {
break;
}
}
Type::SparseHeader header_new;
Reference cell {0};
cell.type = inner;
if(header_index < list.header.length && index >= header->start) {
size_t offset = index - header->start;
size_t data_index = header->index + offset;
if(offset == 0) {
// shift start of range
header->start++;
} else if(offset < header->length - 1) {
// split header at index
header_new.start = index + 1;
header_new.length = header->length - offset - 1;
header_new.index = header->index + offset + 1;
header->length = offset + 1;
auto header_insert = reinterpret_cast<Type::SparseHeader*>(rawlist_insert(list.header, sizeof(Type::SparseHeader), header_index + 1));
*header_insert = header_new;
}
cell.address = rawlist_cell(list.data, type_size(inner), data_index);
drop(cell);
rawlist_remove(list.data, type_size(inner), data_index);
header->length--;
for(size_t i = header_index + 1; i < list.header.length; ++i) {
auto hcell = reinterpret_cast<Type::SparseHeader*>(rawlist_cell(list.header, sizeof(Type::SparseHeader), i));
hcell->index--;
}
if(header->length == 0) {
rawlist_remove(list.header, sizeof(Type::SparseHeader), header_index);
}
}
}
extern "C" size_t sparse_indexof(Reference addr, size_t index)
{
auto& list = *reinterpret_cast<Type::Sparse*>(addr.address);
for(size_t i = 0; i < list.header.length; ++i) {
auto hcell = reinterpret_cast<Type::SparseHeader*>(rawlist_cell(list.header, sizeof(Type::SparseHeader), i));
if(index < hcell->length) {
return hcell->start + index;
} else {
index -= hcell->length;
}
}
return 0;
}
// Record //
extern "C" size_t record_length(Reference addr)
{
auto binding = DB_SCHEMA.get(type_innerkey(addr.type));
if(binding != nullptr) {
return binding->data.size();
}
return 0;
}
extern "C" size_t record_type(Reference addr, size_t index)
{
auto binding = DB_SCHEMA.get(type_innerkey(addr.type));
if(binding != nullptr && index < binding->data.size()) {
return binding->data[index].type;
}
return 0;
}
Reference record_cell(Reference addr, size_t index)
{
Reference result {0};
auto binding = DB_SCHEMA.get(type_innerkey(addr.type));
if(binding != nullptr) {
if(index < binding->data.size()) {
result.type = binding->data[index].type;
result.address = addr.address + binding->data[index].offset;
}
}
return result;
}
extern "C" Reference record_at(Reference addr, size_t index)
{
Reference result = record_cell(addr, index);
if(type_key(result.type) == Type::Tag::Varying) {
result = varying_get(result);
}
return result;
}
extern "C" void record_update(Reference addr, size_t index, Reference source)
{
Reference destination {0};
size_t schid = type_innerkey(addr.type);
auto binding = DB_SCHEMA.get(schid);
if(binding != nullptr) {
if(index < binding->data.size()) {
destination.type = binding->data[index].type;
destination.address = addr.address + binding->data[index].offset;
copy(destination, source);
}
}
}
extern "C" size_t record_keyof(Reference addr, size_t index)
{
Reference destination {0};
auto binding = DB_SCHEMA.get(type_innerkey(addr.type));
if(binding != nullptr) {
if(index < binding->data.size()) {
return binding->data[index].key;
}
}
return 0;
}
extern "C" size_t record_indexof(Reference addr, size_t key)
{
Reference destination {0};
auto binding = DB_SCHEMA.get(type_innerkey(addr.type));
if(binding != nullptr) {
auto result = binding->map.get(key);
if(result != nullptr) {
return *result;
}
return binding->data.size();
}
return 0;
}
// Schema //
extern "C" size_t schema_length(Reference addr)
{
auto& object = (*reinterpret_cast<Type::Schema*>(addr.address));
return object.data.length;
}
extern "C" size_t schema_insert(Reference addr, size_t index, size_t type_id)
{
auto& object = (*reinterpret_cast<Type::Schema*>(addr.address));
if(index >= object.data.length) {
index = object.data.length;
}
void* cell = rawlist_insert(object.data, sizeof(size_t), index);
*reinterpret_cast<size_t*>(cell) = type_id;
return index;
}
extern "C" void schema_update(Reference addr, size_t index, size_t type_id)
{
auto& object = (*reinterpret_cast<Type::Schema*>(addr.address));
if(index < object.data.length) {
auto cell = reinterpret_cast<size_t*>(rawlist_cell(object.data, sizeof(size_t), index));
if(cell != nullptr) {
*cell = type_id;
}
}
}
extern "C" size_t schema_get(Reference addr, size_t index)
{
auto& object = (*reinterpret_cast<Type::Schema*>(addr.address));
if(index < object.data.length) {
auto cell = reinterpret_cast<size_t*>(rawlist_cell(object.data, sizeof(size_t), index));
if(cell != nullptr) {
return *cell;
}
}
return 0;
}
extern "C" void schema_remove(Reference addr, size_t index)
{
auto& object = (*reinterpret_cast<Type::Schema*>(addr.address));
if(index < object.data.length) {
rawlist_remove(object.data, sizeof(size_t), index);
// remove mapping
for(size_t i = 0; i < object.map.length; i++) {
auto cell = reinterpret_cast<Type::Schema::Mapping*>(rawlist_cell(object.data, sizeof(Type::Schema::Mapping), i));
if(cell != nullptr) {
if(cell->index == index) {
rawlist_remove(object.map, sizeof(Type::Schema::Mapping), i);
return;
}
}
}
}
}
extern "C" void schema_clear(Reference addr)
{
auto& object = (*reinterpret_cast<Type::Schema*>(addr.address));
rawlist_clear(object.data);
rawlist_clear(object.map);
}
extern "C" void schema_map(Reference addr, size_t key, size_t index)
{
auto& object = (*reinterpret_cast<Type::Schema*>(addr.address));
if(index < object.data.length) {
size_t find_index = 0;
for(; find_index < object.map.length; find_index++) {
auto cell = reinterpret_cast<Type::Schema::Mapping*>(rawlist_cell(object.map, sizeof(Type::Schema::Mapping), find_index));
if(cell != nullptr) {
if(cell->key == key) { break; }
}
}
// if key is not found, add new mapping
if(find_index == object.map.length) {
auto cell = reinterpret_cast<Type::Schema::Mapping*>(rawlist_insert(object.map, sizeof(Type::Schema::Mapping), object.map.length));
cell->key = key;
cell->index = index;
}
// otherwise, update existing key
else {
auto cell = reinterpret_cast<Type::Schema::Mapping*>(rawlist_cell(object.map, sizeof(Type::Schema::Mapping), find_index));
cell->index = index;
}
}
}
extern "C" void schema_unmap(Reference addr, size_t key)
{
auto& object = (*reinterpret_cast<Type::Schema*>(addr.address));
for(size_t i; i < object.map.length; ++i) {
auto cell = reinterpret_cast<Type::Schema::Mapping*>(rawlist_cell(object.map, sizeof(Type::Schema::Mapping), i));
if(cell != nullptr) {
if(cell->key == key) {
rawlist_remove(object.map, sizeof(Type::Schema::Mapping), i);
return;
}
}
}
}
extern "C" size_t schema_indexof(Reference addr, size_t key)
{
auto& object = (*reinterpret_cast<Type::Schema*>(addr.address));
for(size_t i = 0; i < object.map.length; i++) {
auto cell = reinterpret_cast<Type::Schema::Mapping*>(rawlist_cell(object.map, sizeof(Type::Schema::Mapping), i));
if(cell != nullptr) {
if(cell->key == key) {
return cell->index;
}
}
}
return object.data.length;
}
extern "C" size_t schema_keyof(Reference addr, size_t index)
{
auto& object = (*reinterpret_cast<Type::Schema*>(addr.address));
for(size_t i = 0; i < object.map.length; i++) {
auto cell = reinterpret_cast<Type::Schema::Mapping*>(rawlist_cell(object.map, sizeof(Type::Schema::Mapping), i));
if(cell != nullptr) {
if(cell->index == index) {
return cell->key;
}
}
}
return object.data.length;
}
extern "C" size_t schema_bind(Reference addr, size_t id)
{
if(id > 0) {
Type::SchemaBinding binding {0};
auto& object = (*reinterpret_cast<Type::Schema*>(addr.address));
// prepare binding
binding.binding = id;
for(size_t i = 0; i < object.data.length; ++i) {
Type::SchemaBinding::Row row {0};
size_t type_id = *reinterpret_cast<size_t*>(rawlist_cell(object.data, sizeof(size_t), i));
size_t size = type_size(type_id);
// fail if size is not valid
if(size == 0) { return 0; }
size_t alignment = type_alignment(type_id);
binding.alignment = std::max(alignment, binding.alignment);
size_t position = ((binding.size + (alignment - 1)) & ~(alignment - 1));
binding.size = size + position;
row.type = type_id;
row.offset = position;
binding.data.push_back(row);
}
binding.size = ((binding.size + (binding.alignment - 1)) & ~(binding.alignment - 1));
binding.references = 0;
for(size_t i = 0; i < object.map.length; ++i) {
auto cell = reinterpret_cast<Type::Schema::Mapping*>(rawlist_cell(object.map, sizeof(Type::Schema::Mapping), i));
binding.map.set(cell->key, cell->index);
binding.data[cell->index].key = cell->key;
}
/* printf("[Binding]\n");
printf(" Id: %zu\n", binding.binding);
printf(" Size: %zu\n", binding.size);
printf(" Align: %zu\n", binding.alignment);
printf(" Data:\n");
for(size_t i = 0; i < binding.data.size(); ++i) {
printf(" - %zu:%#x +%zu\n",
binding.data[i].type,
type_key(binding.data[i].type),
binding.data[i].offset
);
}
printf(" Map:\n");
for(size_t key : binding.map.indices()) {
printf(" - %zu -> %zu\n",
key,
*binding.map.get(key)
);
} */
// add binding to pool
DB_SCHEMA.set(id, binding);
return id;
}
return 0;
}
extern "C" bool schema_has(size_t id)
{
return DB_SCHEMA.has(id);
}