sources/cqlrt.c (310 lines of code) (raw):
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "cqlrt.h"
#include <memory.h>
#include <stdbool.h>
int cql_outstanding_refs = 0;
void cql_retain(cql_type_ref _Nullable ref) {
if (ref) {
ref->ref_count++;
cql_outstanding_refs++;
}
}
void cql_release(cql_type_ref _Nullable ref) {
if (ref) {
if (--ref->ref_count == 0) {
if (ref->finalize) {
ref->finalize(ref);
}
free((void *)ref);
}
cql_outstanding_refs--;
cql_invariant(cql_outstanding_refs >= 0);
}
}
static void cql_blob_finalize(cql_type_ref _Nonnull ref) {
cql_blob_ref blob = (cql_blob_ref)ref;
cql_invariant(blob->ptr != NULL);
free((void *)blob->ptr);
blob->size = 0;
}
cql_blob_ref _Nonnull cql_blob_ref_new(const void *_Nonnull bytes, cql_uint32 size) {
cql_invariant(bytes != NULL);
cql_blob_ref result = malloc(sizeof(cql_blob));
result->base.type = CQL_C_TYPE_BLOB;
result->base.ref_count = 1;
result->base.finalize = &cql_blob_finalize;
result->ptr = malloc(size);
result->size = size;
memcpy((void *)result->ptr, bytes, size);
cql_outstanding_refs++;
return result;
}
cql_hash_code cql_blob_hash(cql_blob_ref _Nullable blob) {
cql_hash_code hash = 0;
if (blob) {
// djb2
hash = 5381;
const unsigned char *bytes = blob->ptr;
cql_uint32 size = blob->size;
while (size--) {
hash = ((hash << 5) + hash) + *bytes++; /* hash * 33 + c */
}
}
return hash;
}
cql_bool cql_blob_equal(cql_blob_ref _Nullable blob1, cql_blob_ref _Nullable blob2) {
if (blob1 == blob2) {
return cql_true;
}
if (!blob1 || !blob2) {
return cql_false;
}
const unsigned char *bytes1 = blob1->ptr;
cql_uint32 size1 = blob1->size;
const unsigned char *bytes2 = blob2->ptr;
cql_uint32 size2 = blob2->size;
return size1 == size2 && !memcmp(bytes1, bytes2, size1);
}
static void cql_string_finalize(cql_type_ref _Nonnull ref) {
cql_string_ref string = (cql_string_ref)ref;
cql_invariant(string->ptr != NULL);
free((void *)string->ptr);
string->ptr = NULL; // in case of use after free, fail fast
}
cql_string_ref _Nonnull cql_string_ref_new(const char *_Nonnull cstr) {
cql_invariant(cstr != NULL);
cql_string_ref result = malloc(sizeof(cql_string));
result->base.type = CQL_C_TYPE_STRING;
result->base.ref_count = 1;
result->base.finalize = &cql_string_finalize;
size_t cstrlen = strlen(cstr);
result->ptr = malloc(cstrlen + 1);
memcpy((void *)result->ptr, cstr, cstrlen + 1);
cql_outstanding_refs++;
return result;
}
static void cql_boxed_stmt_finalize(cql_type_ref _Nonnull ref) {
cql_boxed_stmt_ref boxed_stmt = (cql_boxed_stmt_ref)ref;
cql_finalize_stmt(&boxed_stmt->stmt);
}
cql_object_ref _Nonnull cql_box_stmt(sqlite3_stmt *_Nullable stmt) {
cql_boxed_stmt_ref result = malloc(sizeof(cql_boxed_stmt));
result->base.type = CQL_C_TYPE_BOXED_STMT;
result->base.ref_count = 1;
result->base.finalize = &cql_boxed_stmt_finalize;
result->stmt = stmt;
cql_outstanding_refs++;
return (cql_object_ref)result;
}
sqlite3_stmt *_Nullable cql_unbox_stmt(cql_object_ref _Nonnull ref) {
cql_boxed_stmt_ref box = (cql_boxed_stmt_ref)ref;
cql_contract(box->base.type == CQL_C_TYPE_BOXED_STMT);
return box->stmt;
}
cql_int32 cql_string_compare(cql_string_ref _Nonnull s1, cql_string_ref _Nonnull s2) {
cql_invariant(s1 != NULL);
cql_invariant(s2 != NULL);
return strcmp(s1->ptr, s2->ptr);
}
cql_hash_code cql_string_hash(cql_string_ref _Nullable str) {
cql_hash_code hash = 0;
if (str) {
// djb2
hash = 5381;
const char *chars = str->ptr;
int c;
while ((c = *chars++))
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
}
return hash;
}
cql_bool cql_string_equal(cql_string_ref _Nullable s1, cql_string_ref _Nullable s2) {
if (s1 == s2) {
return cql_true;
}
if (!s1 || !s2) {
return cql_false;
}
return cql_string_compare(s1, s2) == 0;
}
int cql_string_like(cql_string_ref _Nonnull s1, cql_string_ref _Nonnull s2) {
cql_invariant(s1 != NULL);
cql_invariant(s2 != NULL);
return cql_compat_sqlite3_strlike(s2->ptr, s1->ptr, '\0');
}
static void cql_result_set_finalize(cql_type_ref _Nonnull ref) {
cql_result_set_ref result_set = (cql_result_set_ref)ref;
if (result_set->meta.teardown) {
result_set->meta.teardown(result_set);
}
}
cql_result_set_ref _Nonnull cql_result_set_create(void *_Nonnull data, cql_int32 count, cql_result_set_meta meta) {
cql_result_set_ref result = malloc(sizeof(cql_result_set));
result->base.type = CQL_C_TYPE_RESULTS;
result->base.ref_count = 1;
result->base.finalize = &cql_result_set_finalize;
result->meta = meta;
result->count = count;
result->data = data;
cql_outstanding_refs++;
return result;
}
cql_hash_code cql_ref_hash(cql_type_ref typeref) {
if (typeref == NULL) {
return 0;
}
if (typeref->type == CQL_C_TYPE_STRING) {
return cql_string_hash((cql_string_ref)typeref);
}
// only these two types are ever invoked
cql_contract(typeref->type == CQL_C_TYPE_BLOB);
return cql_blob_hash((cql_blob_ref)typeref);
}
cql_bool cql_ref_equal(cql_type_ref typeref1, cql_type_ref typeref2) {
if (typeref1 == typeref2) {
return true;
}
// both are not null, so if either is null then false
if (typeref1 == NULL || typeref2 == NULL) {
return false;
}
// not used for arbitrary comparisons, types already checked
cql_contract(typeref1->type == typeref2->type);
if (typeref1->type == CQL_C_TYPE_STRING) {
return cql_string_equal((cql_string_ref)typeref1, (cql_string_ref)typeref2);
}
// only these two types are ever invoked
cql_contract(typeref1->type == CQL_C_TYPE_BLOB);
return cql_blob_equal((cql_blob_ref)typeref1, (cql_blob_ref)typeref2);
}
// naive implementation of encode for cql_bool. It flip the boolean value
cql_bool cql_encode_bool(
cql_object_ref _Nullable encoder,
cql_bool value,
cql_int32 context_type,
void *_Nullable context)
{
return !value;
}
// naive implementation of decode for cql_bool. It flip the boolean value
cql_bool cql_decode_bool(
cql_object_ref _Nullable encoder,
cql_bool value,
cql_int32 context_type,
void *_Nullable context)
{
return !value;
}
// naive implementation of encode for cql_int32.
cql_int32 cql_encode_int32(
cql_object_ref _Nullable encoder,
cql_int32 value,
cql_int32 context_type,
void *_Nullable context)
{
return (value >> 16) | (value << 16);
}
// naive implementation of decode for cql_int32.
cql_int32 cql_decode_int32(
cql_object_ref _Nullable encoder,
cql_int32 value,
cql_int32 context_type,
void *_Nullable context)
{
return (value << 16) | (value >> 16);
}
// naive implementation of encode for cql_int64.
cql_int64 cql_encode_int64(
cql_object_ref _Nullable encoder,
cql_int64 value,
cql_int32 context_type,
void *_Nullable context)
{
return (value >> 32) | (value << 32);
}
// naive implementation of decode for cql_int64.
cql_int64 cql_decode_int64(
cql_object_ref _Nullable encoder,
cql_int64 value,
cql_int32 context_type,
void *_Nullable context)
{
return (value << 32) | (value >> 32);
}
// naive implementation of encode for double.
cql_double cql_encode_double(
cql_object_ref _Nullable encoder,
cql_double value,
cql_int32 context_type,
void *_Nullable context)
{
return -value;
}
// naive implementation of decode for double.
cql_double cql_decode_double(
cql_object_ref _Nullable encoder,
cql_double value,
cql_int32 context_type,
void *_Nullable context)
{
return -value;
}
// naive implementation of encode for string. It appends a character
// and encode context to the string
cql_string_ref cql_encode_string_ref_new(
cql_object_ref _Nullable encoder,
cql_string_ref _Nonnull value,
cql_int32 context_type,
void *_Nullable context)
{
size_t cstrlen = strlen(value->ptr);
size_t context_len = 0;
if (context != NULL && CQL_CORE_DATA_TYPE_OF(context_type) == CQL_DATA_TYPE_STRING) {
cql_string_ref _Nullable encode_context = *(cql_string_ref *)context;
context_len = strlen(encode_context->ptr);
}
char *tmp = malloc(cstrlen + 2 + context_len);
memcpy(tmp, value->ptr, cstrlen);
tmp[cstrlen] = '#';
// naive test case for string type encode context
if (context != NULL && CQL_CORE_DATA_TYPE_OF(context_type) == CQL_DATA_TYPE_STRING) {
cql_string_ref _Nullable encode_context = *(cql_string_ref *)context;
memcpy(tmp + cstrlen + 1, encode_context->ptr, context_len);
cstrlen += context_len;
}
tmp[cstrlen + 1] = 0;
cql_string_ref rs = cql_string_ref_new(tmp);
free(tmp);
return rs;
}
// naive implementation of decode for string. It remove the last character
// and encode context in the string
cql_string_ref cql_decode_string_ref_new(
cql_object_ref _Nullable encoder,
cql_string_ref _Nonnull value,
cql_int32 context_type,
void *_Nullable context)
{
size_t cstrlen = strlen(value->ptr);
// naive test case for string type encode context
if (context != NULL && CQL_CORE_DATA_TYPE_OF(context_type) == CQL_DATA_TYPE_STRING) {
cql_string_ref _Nullable encode_context = *(cql_string_ref *)context;
cstrlen -= strlen(encode_context->ptr);
}
char *tmp = malloc(cstrlen);
memcpy(tmp, value->ptr, cstrlen - 1);
tmp[cstrlen - 1] = 0;
cql_string_ref rs = cql_string_ref_new(tmp);
free(tmp);
return rs;
}
// naive implementation of encode for blob. It appends a byte to the blob
cql_blob_ref cql_encode_blob_ref_new(
cql_object_ref _Nullable encoder,
cql_blob_ref _Nonnull value,
cql_int32 context_type,
void *_Nullable context)
{
cql_uint32 size = value->size + 1;
char *tmp = malloc(size);
memcpy(tmp, value->ptr, size - 1);
tmp[size - 1] = '#';
cql_blob_ref rs = cql_blob_ref_new(tmp, size);
free(tmp);
return rs;
}
// naive implementation of decode for blob. It removes the last byte
// in the blob.
cql_blob_ref cql_decode_blob_ref_new(
cql_object_ref _Nullable encoder,
cql_blob_ref _Nonnull value,
cql_int32 context_type,
void *_Nullable context)
{
cql_uint32 size = value->size - 1;
void *tmp = malloc(size);
memcpy(tmp, value->ptr, size);
cql_blob_ref rs = cql_blob_ref_new(tmp, size);
free(tmp);
return rs;
}
cql_object_ref cql_copy_encoder(sqlite3* db) {
return NULL; // no encoder object needed
}
#include "cqlrt_common.c"