cql_code cql_serialize_to_blob()

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;
}