in sources/cqlrt_common.c [2797:2976]
cql_code cql_serialize_to_blob(
cql_blob_ref _Nullable *_Nonnull blob,
void *_Nonnull cursor_raw,
cql_bool has_row,
uint16_t *_Nonnull offsets,
uint8_t *_Nonnull types)
{
if (!has_row) {
return SQLITE_ERROR;
}
uint16_t count = offsets[0]; // the first index is the count of fields
cql_bytebuf b;
cql_bytebuf_open(&b);
uint8_t code = 0;
uint16_t nullable_count = 0;
uint16_t bool_count = 0;
uint8_t *cursor = cursor_raw; // we will be using char offsets
for (uint16_t i = 0; i < count; i++) {
uint8_t type = types[i];
cql_bool nullable = !(type & CQL_DATA_TYPE_NOT_NULL);
int8_t core_data_type = CQL_CORE_DATA_TYPE_OF(type);
code = 0;
if (nullable) {
nullable_count++;
code = 'a' - 'A'; // lower case for nullable
}
// this makes upper or lower case depending on nullable
switch (core_data_type) {
case CQL_DATA_TYPE_INT32: code += 'I'; break;
case CQL_DATA_TYPE_INT64: code += 'L'; break;
case CQL_DATA_TYPE_DOUBLE: code += 'D'; break;
case CQL_DATA_TYPE_BOOL: code += 'F'; bool_count++; break;
case CQL_DATA_TYPE_STRING: code += 'S'; break;
case CQL_DATA_TYPE_BLOB: code += 'B'; break;
}
// verifies that we set code
cql_invariant(code != 0 && code != 'a' - 'A');
cql_append_value(b, code);
}
// null terminate the type info
code = 0;
cql_append_value(b, code);
uint16_t bitvector_bytes_needed = (nullable_count + bool_count + 7) / 8;
uint8_t *bits = cql_bytebuf_alloc(&b, bitvector_bytes_needed);
memset(bits, 0, bitvector_bytes_needed);
uint16_t nullable_index = 0;
uint16_t bool_index = 0;
for (uint16_t i = 0; i < count; i++) {
uint16_t offset = offsets[i+1];
uint8_t type = types[i];
int8_t core_data_type = CQL_CORE_DATA_TYPE_OF(type);
if (type & CQL_DATA_TYPE_NOT_NULL) {
switch (core_data_type) {
case CQL_DATA_TYPE_INT32: {
cql_int32 int32_data = *(cql_int32 *)(cursor + offset);
cql_write_varint_32(&b, int32_data);
break;
}
case CQL_DATA_TYPE_INT64: {
cql_int64 int64_data = *(cql_int64 *)(cursor + offset);
cql_write_varint_64(&b, int64_data);
break;
}
case CQL_DATA_TYPE_DOUBLE: {
// IEEE 754 big endian seems to be everywhere we need it to be
// it's good enough for SQLite so it's good enough for us.
// We're punting on their ARM7 mixed endian support, we don't care about ARM7
cql_double double_data = *(cql_double *)(cursor + offset);
cql_append_value(b, double_data);
break;
}
case CQL_DATA_TYPE_BOOL: {
cql_bool bool_data = *(cql_bool *)(cursor + offset);
if (bool_data) {
cql_setbit(bits, nullable_count + bool_index);
}
bool_index++;
break;
}
case CQL_DATA_TYPE_STRING: {
cql_string_ref str_ref = *(cql_string_ref *)(cursor + offset);
cql_alloc_cstr(temp, str_ref);
cql_bytebuf_append(&b, temp, (uint32_t)(strlen(temp) + 1));
cql_free_cstr(temp, str_ref);
break;
}
case CQL_DATA_TYPE_BLOB: {
cql_blob_ref blob_ref = *(cql_blob_ref *)(cursor + offset);
const void *bytes = cql_get_blob_bytes(blob_ref);
cql_uint32 size = cql_get_blob_size(blob_ref);
cql_append_value(b, size);
cql_bytebuf_append(&b, bytes, size);
break;
}
}
}
else {
switch (core_data_type) {
case CQL_DATA_TYPE_INT32: {
cql_nullable_int32 int32_data = *(cql_nullable_int32 *)(cursor + offset);
if (!int32_data.is_null) {
cql_setbit(bits, nullable_index);
cql_write_varint_32(&b, int32_data.value);
}
break;
}
case CQL_DATA_TYPE_INT64: {
cql_nullable_int64 int64_data = *(cql_nullable_int64 *)(cursor + offset);
if (!int64_data.is_null) {
cql_setbit(bits, nullable_index);
cql_write_varint_64(&b, int64_data.value);
}
break;
}
case CQL_DATA_TYPE_DOUBLE: {
// IEEE 754 big endian seems to be everywhere we need it to be
// it's good enough for SQLite so it's good enough for us.
// We're punting on their ARM7 mixed endian support, we don't care about ARM7
cql_nullable_double double_data = *(cql_nullable_double *)(cursor + offset);
cql_append_nullable_value(b, double_data);
break;
}
case CQL_DATA_TYPE_BOOL: {
cql_nullable_bool bool_data = *(cql_nullable_bool *)(cursor + offset);
if (!bool_data.is_null) {
cql_setbit(bits, nullable_index);
if (bool_data.value) {
cql_setbit(bits, nullable_count + bool_index);
}
}
bool_index++;
break;
}
case CQL_DATA_TYPE_STRING: {
cql_string_ref str_ref = *(cql_string_ref *)(cursor + offset);
if (str_ref) {
cql_setbit(bits, nullable_index);
cql_alloc_cstr(temp, str_ref);
cql_bytebuf_append(&b, temp, (uint32_t)(strlen(temp) + 1));
cql_free_cstr(temp, str_ref);
}
break;
}
case CQL_DATA_TYPE_BLOB: {
cql_blob_ref blob_ref = *(cql_blob_ref *)(cursor + offset);
if (blob_ref) {
cql_setbit(bits, nullable_index);
const void *bytes = cql_get_blob_bytes(blob_ref);
uint32_t size = cql_get_blob_size(blob_ref);
cql_append_value(b, size);
cql_bytebuf_append(&b, bytes, size);
}
break;
}
}
nullable_index++;
}
}
cql_invariant(nullable_index == nullable_count);
cql_blob_ref new_blob = cql_blob_ref_new((const uint8_t *)b.ptr, b.used);
cql_blob_release(*blob);
*blob = new_blob;
cql_bytebuf_close(&b);
return SQLITE_OK;
}