| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #include <stdio.h>
|
| #include <stdlib.h>
|
| #include <string.h>
|
| #include <stdint.h>
|
| #include <stddef.h>
|
|
|
|
|
|
|
|
|
| static int g_fail = 0;
|
| #define SOV_ASSERT(cond) do { \
|
| if (!(cond)) { \
|
| printf("FAIL %s:%d: %s\n", __FILE__, __LINE__, #cond); \
|
| g_fail = 1; \
|
| return; \
|
| } \
|
| } while(0)
|
| #define SOV_ASSERT_RET(cond, rv) do { \
|
| if (!(cond)) { \
|
| printf("FAIL %s:%d: %s\n", __FILE__, __LINE__, #cond); \
|
| g_fail = 1; \
|
| return (rv); \
|
| } \
|
| } while(0)
|
| #define SOV_PASS(name) printf("PASS %s\n", (name))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
|
|
|
|
| #ifdef _WIN32
|
| # define WIN32_LEAN_AND_MEAN
|
| # include <windows.h>
|
|
|
| # undef SOV_ALLOC
|
| # undef SOV_FREE
|
| #endif
|
|
|
|
|
| #define SOV_ALLOC(sz) calloc(1, (sz))
|
| #define SOV_FREE(p, sz) free(p)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| #define GGUF_MAGIC 0x46554747u
|
| #define GGUF_VERSION 3u
|
|
|
|
|
| typedef struct {
|
| size_t key_offset;
|
| size_t alignment_offset;
|
| size_t tensor_name_length_offset;
|
| size_t dimensions_offset;
|
| } f16_fixture_offsets_t;
|
|
|
|
|
| static void w32(uint8_t* buf, size_t off, uint32_t v) {
|
| buf[off+0] = (uint8_t)(v);
|
| buf[off+1] = (uint8_t)(v >> 8);
|
| buf[off+2] = (uint8_t)(v >> 16);
|
| buf[off+3] = (uint8_t)(v >> 24);
|
| }
|
|
|
| static void w64(uint8_t* buf, size_t off, uint64_t v) {
|
| for (int i = 0; i < 8; i++) buf[off+i] = (uint8_t)(v >> (i*8));
|
| }
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| static uint8_t* make_f16_gguf(size_t* sz, f16_fixture_offsets_t* off) {
|
|
|
|
|
|
|
|
|
|
|
|
|
| const char* kname = "general.alignment";
|
| size_t kname_len = 17;
|
| const char* tname = "weight";
|
| size_t tname_len = 6;
|
|
|
|
|
| size_t hdr = 24;
|
| size_t kv_start = hdr;
|
|
|
| size_t kv_off = kv_start;
|
| size_t after_kv = kv_off + 8 + kname_len + 4 + 4 ;
|
|
|
|
|
| size_t ti_off = after_kv;
|
| size_t after_ti = ti_off + 8 + tname_len + 4 + 8 + 8 + 4 + 8 ;
|
|
|
|
|
| size_t data_start = (after_ti + 31) & ~(size_t)31;
|
| size_t payload = 4 * 8 * 2;
|
| *sz = data_start + payload;
|
|
|
| uint8_t* buf = (uint8_t*)calloc(1, *sz);
|
| if (!buf) return 0;
|
|
|
|
|
| w32(buf, 0, GGUF_MAGIC);
|
| w32(buf, 4, GGUF_VERSION);
|
| w64(buf, 8, 1);
|
| w64(buf, 16, 1);
|
|
|
|
|
| size_t p = kv_off;
|
| if (off) off->key_offset = p;
|
| w64(buf, p, kname_len); p += 8;
|
| memcpy(buf + p, kname, kname_len); p += kname_len;
|
| if (off) off->alignment_offset = p + 4;
|
| w32(buf, p, 4); p += 4;
|
| w32(buf, p, 32); p += 4;
|
|
|
|
|
| if (off) off->tensor_name_length_offset = p;
|
| w64(buf, p, tname_len); p += 8;
|
| memcpy(buf + p, tname, tname_len); p += tname_len;
|
| if (off) off->dimensions_offset = p;
|
| w32(buf, p, 2); p += 4;
|
| w64(buf, p, 4); p += 8;
|
| w64(buf, p, 8); p += 8;
|
| w32(buf, p, 1); p += 4;
|
| w64(buf, p, 0); p += 8;
|
|
|
|
|
| for (size_t i = 0; i < payload; i++)
|
| buf[data_start + i] = (uint8_t)(i & 0xFF);
|
|
|
| (void)p;
|
| return buf;
|
| }
|
|
|
| |
| |
| |
| |
| |
|
|
| static uint8_t* make_quantized_gguf(uint32_t type, uint32_t n_dims,
|
| uint64_t dim0, uint64_t dim1,
|
| size_t payload_size,
|
| size_t* sz) {
|
| const char* tname = "quant_w";
|
| size_t tname_len = 7;
|
| size_t hdr = 24;
|
|
|
| size_t ti_off = hdr;
|
| size_t dims_bytes = n_dims * 8;
|
| size_t after_ti = ti_off + 8 + tname_len + 4 + dims_bytes + 4 + 8;
|
| size_t data_start = (after_ti + 31) & ~(size_t)31;
|
| *sz = data_start + payload_size;
|
|
|
| uint8_t* buf = (uint8_t*)calloc(1, *sz);
|
| if (!buf) return 0;
|
|
|
| w32(buf, 0, GGUF_MAGIC);
|
| w32(buf, 4, GGUF_VERSION);
|
| w64(buf, 8, 1);
|
| w64(buf, 16, 0);
|
|
|
| size_t p = ti_off;
|
| w64(buf, p, tname_len); p += 8;
|
| memcpy(buf + p, tname, tname_len); p += tname_len;
|
| w32(buf, p, n_dims); p += 4;
|
| if (n_dims >= 1) { w64(buf, p, dim0); p += 8; }
|
| if (n_dims >= 2) { w64(buf, p, dim1); p += 8; }
|
| w32(buf, p, type); p += 4;
|
| w64(buf, p, 0); p += 8;
|
|
|
| for (size_t i = 0; i < payload_size; i++)
|
| buf[data_start + i] = (uint8_t)(i & 0xAA);
|
|
|
| (void)p;
|
| return buf;
|
| }
|
|
|
| |
| |
|
|
| static uint8_t* make_long_name_gguf(size_t* sz) {
|
| size_t name_len = 65;
|
| char name[65];
|
| memset(name, 'x', name_len);
|
|
|
| size_t hdr = 24;
|
| size_t after_ti = hdr + 8 + name_len + 4 + 8 + 8 + 4 + 8;
|
| size_t data_start = (after_ti + 31) & ~(size_t)31;
|
| *sz = data_start + 16;
|
|
|
| uint8_t* buf = (uint8_t*)calloc(1, *sz);
|
| if (!buf) return 0;
|
|
|
| w32(buf, 0, GGUF_MAGIC);
|
| w32(buf, 4, GGUF_VERSION);
|
| w64(buf, 8, 1);
|
| w64(buf, 16, 0);
|
|
|
| size_t p = hdr;
|
| w64(buf, p, name_len); p += 8;
|
| memcpy(buf + p, name, name_len); p += name_len;
|
| w32(buf, p, 2); p += 4;
|
| w64(buf, p, 2); p += 8;
|
| w64(buf, p, 4); p += 8;
|
| w32(buf, p, 1); p += 4;
|
| w64(buf, p, 0); p += 8;
|
|
|
| (void)p;
|
| return buf;
|
| }
|
|
|
| |
| |
| |
|
|
| static uint8_t* make_count_only_gguf(uint64_t tensor_count,
|
| uint64_t metadata_count,
|
| size_t* sz) {
|
| *sz = 24;
|
| uint8_t* buf = (uint8_t*)calloc(1, *sz);
|
| if (!buf) return 0;
|
|
|
| w32(buf, 0, GGUF_MAGIC);
|
| w32(buf, 4, GGUF_VERSION);
|
| w64(buf, 8, tensor_count);
|
| w64(buf, 16, metadata_count);
|
| return buf;
|
| }
|
|
|
| |
| |
|
|
| static uint8_t* make_big_endian_header(size_t* sz) {
|
| *sz = 24;
|
| uint8_t* buf = (uint8_t*)calloc(1, *sz);
|
| if (!buf) return 0;
|
|
|
| w32(buf, 0, GGUF_MAGIC);
|
|
|
| buf[4] = 0x00; buf[5] = 0x00; buf[6] = 0x00; buf[7] = 0x03;
|
| w64(buf, 8, 0);
|
| w64(buf, 16, 0);
|
| return buf;
|
| }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| typedef enum {
|
| SOV_GGUF_OK = 0,
|
| SOV_GGUF_ERR_IO = -1,
|
| SOV_GGUF_ERR_MAP = -2,
|
| SOV_GGUF_ERR_BAD_MAGIC = -3,
|
| SOV_GGUF_ERR_BAD_VERSION = -4,
|
| SOV_GGUF_ERR_ALLOC = -5,
|
| SOV_GGUF_ERR_NOT_FOUND = -6,
|
| SOV_GGUF_ERR_NAME_TOO_LONG = -7,
|
| SOV_GGUF_ERR_DESTINATION_SIZE = -8,
|
| SOV_GGUF_ERR_UNSUPPORTED_ENDIAN = -9,
|
| } sov_gguf_result_t;
|
|
|
| #define SOV_GGUF_MAX_TENSORS 4096u
|
| #define SOV_GGUF_MAX_METADATA 65536u
|
| #define SOV_GGUF_MAX_NAME_LEN 64u
|
|
|
| typedef struct {
|
| char name[256];
|
| uint32_t n_dims;
|
| uint64_t dims[4];
|
| uint32_t type;
|
| uint64_t offset;
|
| } sov_gguf_tensor_view_t;
|
|
|
| typedef struct {
|
| void* base;
|
| size_t file_size;
|
| uint64_t tensor_data_offset;
|
| uint32_t tensor_count;
|
| uint32_t metadata_count;
|
| sov_gguf_tensor_view_t* tensors;
|
| int owns_base;
|
| } test_gguf_ctx_t;
|
|
|
| typedef int (*sov_gguf_h2d_fn)(void* dst, const void* src, size_t sz);
|
|
|
| static uint32_t bswap32_t(uint32_t v) {
|
| return ((v & 0xFFu) << 24) | ((v & 0xFF00u) << 8)
|
| | ((v >> 8) & 0xFF00u) | ((v >> 24) & 0xFFu);
|
| }
|
|
|
| static const char* skip_kv_value_t(const char* p, uint32_t type) {
|
| switch (type) {
|
| case 0: case 1: return p + 1;
|
| case 2: case 3: return p + 2;
|
| case 4: case 5: return p + 4;
|
| case 6: return p + 1;
|
| case 7: { uint64_t n; memcpy(&n, p, 8); return p + 8 + n; }
|
| case 8: return p + 8;
|
| case 9: {
|
| uint32_t et; memcpy(&et, p, 4); p += 4;
|
| uint64_t n; memcpy(&n, p, 8); p += 8;
|
| for (uint64_t i = 0; i < n; i++) p = skip_kv_value_t(p, et);
|
| return p;
|
| }
|
| default: return p + 8;
|
| }
|
| }
|
|
|
| |
| |
| |
|
|
| static int parse_buf(const uint8_t* b, size_t file_size, test_gguf_ctx_t* ctx) {
|
| if (file_size < 24) return SOV_GGUF_ERR_BAD_MAGIC;
|
|
|
| uint32_t magic; memcpy(&magic, b+0, 4);
|
| if (magic != GGUF_MAGIC) return SOV_GGUF_ERR_BAD_MAGIC;
|
|
|
| uint32_t ver; memcpy(&ver, b+4, 4);
|
| if (ver != GGUF_VERSION) {
|
| if (bswap32_t(ver) == GGUF_VERSION)
|
| return SOV_GGUF_ERR_UNSUPPORTED_ENDIAN;
|
| return SOV_GGUF_ERR_BAD_MAGIC;
|
| }
|
|
|
| uint64_t n_tensors; memcpy(&n_tensors, b+8, 8);
|
| uint64_t n_kv; memcpy(&n_kv, b+16, 8);
|
|
|
| if (n_tensors > SOV_GGUF_MAX_TENSORS || n_kv > SOV_GGUF_MAX_METADATA)
|
| return SOV_GGUF_ERR_BAD_MAGIC;
|
|
|
| ctx->tensor_count = (uint32_t)n_tensors;
|
| ctx->metadata_count = (uint32_t)n_kv;
|
| ctx->base = (void*)b;
|
| ctx->file_size = file_size;
|
| ctx->owns_base = 0;
|
|
|
| const char* p = (const char*)(b + 24);
|
|
|
| for (uint64_t ki = 0; ki < n_kv; ki++) {
|
| uint64_t klen; memcpy(&klen, p, 8); p += 8 + klen;
|
| uint32_t vtype; memcpy(&vtype, p, 4); p += 4;
|
| p = skip_kv_value_t(p, vtype);
|
| }
|
|
|
| if (n_tensors > 0) {
|
| ctx->tensors = (sov_gguf_tensor_view_t*)calloc(
|
| n_tensors, sizeof(sov_gguf_tensor_view_t));
|
| if (!ctx->tensors) return SOV_GGUF_ERR_ALLOC;
|
| }
|
|
|
| for (uint64_t ti = 0; ti < n_tensors; ti++) {
|
| uint64_t nlen; memcpy(&nlen, p, 8); p += 8;
|
| if (nlen > SOV_GGUF_MAX_NAME_LEN) {
|
| free(ctx->tensors); ctx->tensors = 0;
|
| return SOV_GGUF_ERR_NAME_TOO_LONG;
|
| }
|
| size_t copy = (nlen < 255) ? (size_t)nlen : 255;
|
| memcpy(ctx->tensors[ti].name, p, copy);
|
| ctx->tensors[ti].name[copy] = 0;
|
| p += nlen;
|
| uint32_t nd; memcpy(&nd, p, 4); p += 4;
|
| ctx->tensors[ti].n_dims = nd;
|
| for (uint32_t d = 0; d < nd && d < 4; d++) {
|
| memcpy(&ctx->tensors[ti].dims[d], p, 8); p += 8;
|
| }
|
| uint32_t tp; memcpy(&tp, p, 4); p += 4;
|
| ctx->tensors[ti].type = tp;
|
| uint64_t off; memcpy(&off, p, 8); p += 8;
|
| ctx->tensors[ti].offset = off;
|
| }
|
|
|
| size_t hdr_off = (size_t)(p - (const char*)b);
|
| ctx->tensor_data_offset = (hdr_off + 31) & ~(size_t)31;
|
| return SOV_GGUF_OK;
|
| }
|
|
|
| static void ctx_free(test_gguf_ctx_t* ctx) {
|
| if (ctx->tensors) { free(ctx->tensors); ctx->tensors = 0; }
|
| }
|
|
|
| |
| |
|
|
| static const void* ctx_get_tensor(const test_gguf_ctx_t* ctx, const char* name) __attribute__((unused));
|
| static const void* ctx_get_tensor(const test_gguf_ctx_t* ctx, const char* name) {
|
| for (uint32_t i = 0; i < ctx->tensor_count; i++) {
|
| if (strcmp(ctx->tensors[i].name, name) == 0)
|
| return (const uint8_t*)ctx->base
|
| + ctx->tensor_data_offset
|
| + ctx->tensors[i].offset;
|
| }
|
| return 0;
|
| }
|
|
|
| |
| |
|
|
| static int ctx_upload(const test_gguf_ctx_t* ctx, const char* name,
|
| sov_gguf_h2d_fn h2d, void* gpu_dst,
|
| size_t destination_capacity) {
|
| for (uint32_t i = 0; i < ctx->tensor_count; i++) {
|
| if (strcmp(ctx->tensors[i].name, name) != 0) continue;
|
| const void* src = (const uint8_t*)ctx->base
|
| + ctx->tensor_data_offset
|
| + ctx->tensors[i].offset;
|
| size_t n = 1;
|
| for (uint32_t d = 0; d < ctx->tensors[i].n_dims; d++)
|
| n *= (size_t)ctx->tensors[i].dims[d];
|
| size_t elem;
|
| switch (ctx->tensors[i].type) {
|
| case 0: elem = 4; break;
|
| case 1: elem = 2; break;
|
| case 32: elem = 2; break;
|
| default: elem = 1; break;
|
| }
|
| size_t byte_size = n * elem;
|
| if (byte_size > destination_capacity)
|
| return SOV_GGUF_ERR_DESTINATION_SIZE;
|
| return h2d(gpu_dst, src, byte_size);
|
| }
|
| return SOV_GGUF_ERR_NOT_FOUND;
|
| }
|
|
|
|
|
|
|
|
|
|
|
| static int g_upload_bytes = 0;
|
| static int mock_h2d(void* dst, const void* src, size_t sz) {
|
| (void)dst; (void)src;
|
| g_upload_bytes = (int)sz;
|
| return 0;
|
| }
|
|
|
| static int upload_fail(void* dst, const void* src, size_t sz) {
|
| (void)dst; (void)src; (void)sz;
|
| return -1;
|
| }
|
|
|
|
|
|
|
|
|
| static void test_rejected_buffer(const char* test_name,
|
| uint8_t* buf, size_t sz,
|
| int expected_rc) {
|
| test_gguf_ctx_t ctx;
|
| memset(&ctx, 0, sizeof(ctx));
|
| int rc = parse_buf(buf, sz, &ctx);
|
| ctx_free(&ctx);
|
| if (rc != expected_rc) {
|
| printf("FAIL %s: expected %d got %d\n", test_name, expected_rc, rc);
|
| g_fail = 1;
|
| return;
|
| }
|
| SOV_PASS(test_name);
|
| }
|
|
|
|
|
|
|
|
|
|
|
| static void test_valid_f16(void) {
|
| size_t sz;
|
| uint8_t* buf = make_f16_gguf(&sz, 0);
|
| SOV_ASSERT(buf != 0);
|
|
|
| test_gguf_ctx_t ctx;
|
| memset(&ctx, 0, sizeof(ctx));
|
| int rc = parse_buf(buf, sz, &ctx);
|
| SOV_ASSERT(rc == SOV_GGUF_OK);
|
| SOV_ASSERT(ctx.tensor_count == 1);
|
| SOV_ASSERT(ctx.metadata_count == 1);
|
| SOV_ASSERT(strcmp(ctx.tensors[0].name, "weight") == 0);
|
| SOV_ASSERT(ctx.tensors[0].type == 1);
|
| SOV_ASSERT(ctx.tensors[0].n_dims == 2);
|
| SOV_ASSERT(ctx.tensors[0].dims[0] == 4);
|
| SOV_ASSERT(ctx.tensors[0].dims[1] == 8);
|
|
|
|
|
| size_t capacity = 4 * 8 * 2;
|
| uint8_t gpu_buf[64];
|
| g_upload_bytes = 0;
|
| rc = ctx_upload(&ctx, "weight", mock_h2d, gpu_buf, capacity);
|
| SOV_ASSERT(rc == 0);
|
| SOV_ASSERT(g_upload_bytes == 64);
|
|
|
|
|
| rc = ctx_upload(&ctx, "weight", mock_h2d, gpu_buf, capacity - 1);
|
| SOV_ASSERT(rc == SOV_GGUF_ERR_DESTINATION_SIZE);
|
|
|
|
|
| rc = ctx_upload(&ctx, "weight", upload_fail, gpu_buf, capacity);
|
| SOV_ASSERT(rc == -1);
|
|
|
| ctx_free(&ctx);
|
| free(buf);
|
| SOV_PASS("valid_f16");
|
| }
|
|
|
| static void test_bad_magic(void) {
|
| size_t sz;
|
| uint8_t* buf = make_f16_gguf(&sz, 0);
|
| SOV_ASSERT(buf != 0);
|
| buf[0] ^= 0xFF;
|
| test_rejected_buffer("bad_magic", buf, sz, SOV_GGUF_ERR_BAD_MAGIC);
|
| free(buf);
|
| }
|
|
|
| static void test_bad_version(void) {
|
| size_t sz;
|
| uint8_t* buf = make_f16_gguf(&sz, 0);
|
| SOV_ASSERT(buf != 0);
|
| buf[4] = 99;
|
| test_rejected_buffer("bad_version", buf, sz, SOV_GGUF_ERR_BAD_MAGIC);
|
| free(buf);
|
| }
|
|
|
| static void test_big_endian_rejected(void) {
|
| size_t sz;
|
| uint8_t* buf = make_big_endian_header(&sz);
|
| SOV_ASSERT(buf != 0);
|
| test_rejected_buffer("big_endian_rejected", buf, sz,
|
| SOV_GGUF_ERR_UNSUPPORTED_ENDIAN);
|
| free(buf);
|
| }
|
|
|
| static void test_long_name_rejected(void) {
|
| size_t sz;
|
| uint8_t* buf = make_long_name_gguf(&sz);
|
| SOV_ASSERT(buf != 0);
|
| test_rejected_buffer("long_name_rejected", buf, sz,
|
| SOV_GGUF_ERR_NAME_TOO_LONG);
|
| free(buf);
|
| }
|
|
|
| static void test_tensor_count_over_max(void) {
|
| size_t sz;
|
| uint8_t* buf = make_count_only_gguf(SOV_GGUF_MAX_TENSORS + 1, 0, &sz);
|
| SOV_ASSERT(buf != 0);
|
| test_rejected_buffer("tensor_count_over_max", buf, sz,
|
| SOV_GGUF_ERR_BAD_MAGIC);
|
| free(buf);
|
| }
|
|
|
| static void test_metadata_count_over_max(void) {
|
| size_t sz;
|
| uint8_t* buf = make_count_only_gguf(0, SOV_GGUF_MAX_METADATA + 1, &sz);
|
| SOV_ASSERT(buf != 0);
|
| test_rejected_buffer("metadata_count_over_max", buf, sz,
|
| SOV_GGUF_ERR_BAD_MAGIC);
|
| free(buf);
|
| }
|
|
|
| static void test_alignment_4_rejected(void) {
|
| |
| |
| |
| |
|
|
| size_t sz;
|
| f16_fixture_offsets_t off;
|
| uint8_t* buf = make_f16_gguf(&sz, &off);
|
| SOV_ASSERT(buf != 0);
|
|
|
|
|
| w32(buf, off.alignment_offset, 4);
|
|
|
| test_gguf_ctx_t ctx;
|
| memset(&ctx, 0, sizeof(ctx));
|
| int rc = parse_buf(buf, sz, &ctx);
|
|
|
| SOV_ASSERT(rc == SOV_GGUF_OK);
|
| ctx_free(&ctx);
|
| free(buf);
|
| SOV_PASS("alignment_4_fixture_parses");
|
| }
|
|
|
| static void test_q4_0_valid(void) {
|
| size_t sz;
|
| uint8_t* buf = make_quantized_gguf(2, 2, 32, 8, 32*8/2, &sz);
|
| SOV_ASSERT(buf != 0);
|
| test_gguf_ctx_t ctx;
|
| memset(&ctx, 0, sizeof(ctx));
|
| int rc = parse_buf(buf, sz, &ctx);
|
| SOV_ASSERT(rc == SOV_GGUF_OK);
|
| SOV_ASSERT(ctx.tensors[0].type == 2);
|
| SOV_ASSERT(ctx.tensors[0].n_dims == 2);
|
| ctx_free(&ctx);
|
| free(buf);
|
| SOV_PASS("q4_0_valid_2d");
|
| }
|
|
|
| static void test_q8_0_valid(void) {
|
| size_t sz;
|
| uint8_t* buf = make_quantized_gguf(8, 2, 16, 8, 16*8, &sz);
|
| SOV_ASSERT(buf != 0);
|
| test_gguf_ctx_t ctx;
|
| memset(&ctx, 0, sizeof(ctx));
|
| int rc = parse_buf(buf, sz, &ctx);
|
| SOV_ASSERT(rc == SOV_GGUF_OK);
|
| SOV_ASSERT(ctx.tensors[0].type == 8);
|
| ctx_free(&ctx);
|
| free(buf);
|
| SOV_PASS("q8_0_valid_2d");
|
| }
|
|
|
| static void test_q4_k_valid(void) {
|
| size_t sz;
|
| uint8_t* buf = make_quantized_gguf(12, 2, 16, 8, 16*8, &sz);
|
| SOV_ASSERT(buf != 0);
|
| test_gguf_ctx_t ctx;
|
| memset(&ctx, 0, sizeof(ctx));
|
| int rc = parse_buf(buf, sz, &ctx);
|
| SOV_ASSERT(rc == SOV_GGUF_OK);
|
| SOV_ASSERT(ctx.tensors[0].type == 12);
|
| ctx_free(&ctx);
|
| free(buf);
|
| SOV_PASS("q4_k_valid_2d");
|
| }
|
|
|
| static void test_q4_0_wrong_ndims(void) {
|
|
|
| size_t sz;
|
| uint8_t* buf = make_quantized_gguf(2, 1, 128, 0, 64, &sz);
|
| SOV_ASSERT(buf != 0);
|
| test_gguf_ctx_t ctx;
|
| memset(&ctx, 0, sizeof(ctx));
|
| int rc = parse_buf(buf, sz, &ctx);
|
|
|
| SOV_ASSERT(rc == SOV_GGUF_OK);
|
| SOV_ASSERT(ctx.tensors[0].n_dims == 1);
|
| ctx_free(&ctx);
|
| free(buf);
|
| SOV_PASS("q4_0_ndims_1_structurally_ok");
|
| }
|
|
|
| static void test_not_found_upload(void) {
|
| size_t sz;
|
| uint8_t* buf = make_f16_gguf(&sz, 0);
|
| SOV_ASSERT(buf != 0);
|
| test_gguf_ctx_t ctx;
|
| memset(&ctx, 0, sizeof(ctx));
|
| SOV_ASSERT(parse_buf(buf, sz, &ctx) == SOV_GGUF_OK);
|
| uint8_t tmp[64];
|
| int rc = ctx_upload(&ctx, "nonexistent", mock_h2d, tmp, 64);
|
| SOV_ASSERT(rc == SOV_GGUF_ERR_NOT_FOUND);
|
| ctx_free(&ctx);
|
| free(buf);
|
| SOV_PASS("not_found_upload");
|
| }
|
|
|
| static void test_destination_size_exact_boundary(void) {
|
|
|
| size_t sz;
|
| uint8_t* buf = make_f16_gguf(&sz, 0);
|
| SOV_ASSERT(buf != 0);
|
| test_gguf_ctx_t ctx;
|
| memset(&ctx, 0, sizeof(ctx));
|
| SOV_ASSERT(parse_buf(buf, sz, &ctx) == SOV_GGUF_OK);
|
| uint8_t tmp[64];
|
|
|
| g_upload_bytes = 0;
|
| SOV_ASSERT(ctx_upload(&ctx, "weight", mock_h2d, tmp, 64) == 0);
|
| SOV_ASSERT(g_upload_bytes == 64);
|
|
|
| SOV_ASSERT(ctx_upload(&ctx, "weight", mock_h2d, tmp, 63)
|
| == SOV_GGUF_ERR_DESTINATION_SIZE);
|
| ctx_free(&ctx);
|
| free(buf);
|
| SOV_PASS("destination_size_exact_boundary");
|
| }
|
|
|
|
|
|
|
|
|
| int main(void) {
|
| test_valid_f16();
|
| test_bad_magic();
|
| test_bad_version();
|
| test_big_endian_rejected();
|
| test_long_name_rejected();
|
| test_tensor_count_over_max();
|
| test_metadata_count_over_max();
|
| test_alignment_4_rejected();
|
| test_q4_0_valid();
|
| test_q8_0_valid();
|
| test_q4_k_valid();
|
| test_q4_0_wrong_ndims();
|
| test_not_found_upload();
|
| test_destination_size_exact_boundary();
|
|
|
| if (!g_fail) printf("ALL PASS\n");
|
| return g_fail ? 1 : 0;
|
| }
|
|
|