@@ -21,6 +21,9 @@ enum handcrafted_file_type {
2121 HANDCRAFTED_HEADER_BAD_VERSION_FUTURE = 30 ,
2222 HANDCRAFTED_HEADER_BAD_N_TENSORS = 40 ,
2323 HANDCRAFTED_HEADER_BAD_N_KV = 50 ,
24+ HANDCRAFTED_HEADER_ENDIANNESS_MISMATCH = 60 ,
25+ HANDCRAFTED_HEADER_N_TENSORS_MAX = 70 ,
26+ HANDCRAFTED_HEADER_N_KV_MAX = 80 ,
2427 HANDCRAFTED_HEADER_EMPTY = 800 ,
2528
2629 HANDCRAFTED_KV_BAD_KEY_SIZE = 10 + offset_has_kv,
@@ -57,6 +60,9 @@ static std::string handcrafted_file_type_name(const enum handcrafted_file_type h
5760 case HANDCRAFTED_HEADER_BAD_VERSION_FUTURE: return " HEADER_BAD_VERSION_FUTURE" ;
5861 case HANDCRAFTED_HEADER_BAD_N_KV: return " HEADER_BAD_N_KV" ;
5962 case HANDCRAFTED_HEADER_BAD_N_TENSORS: return " HEADER_BAD_N_TENSORS" ;
63+ case HANDCRAFTED_HEADER_ENDIANNESS_MISMATCH: return " HEADER_ENDIANNESS_MISMATCH" ;
64+ case HANDCRAFTED_HEADER_N_TENSORS_MAX: return " HEADER_N_TENSORS_MAX" ;
65+ case HANDCRAFTED_HEADER_N_KV_MAX: return " HEADER_N_KV_MAX" ;
6066 case HANDCRAFTED_HEADER_EMPTY: return " HEADER_EMPTY" ;
6167
6268 case HANDCRAFTED_KV_BAD_KEY_SIZE: return " KV_BAD_KEY_SIZE" ;
@@ -182,6 +188,15 @@ static FILE * get_handcrafted_file(const unsigned int seed, const enum handcraft
182188 } else if (hft == HANDCRAFTED_HEADER_BAD_VERSION_FUTURE) {
183189 const uint32_t version = GGUF_VERSION + 1 ;
184190 helper_write (file, version);
191+ } else if (hft == HANDCRAFTED_HEADER_ENDIANNESS_MISMATCH) {
192+ const uint32_t version = GGUF_VERSION;
193+ const uint8_t version_bytes[4 ] = {
194+ uint8_t (version >> 24 ),
195+ uint8_t (version >> 16 ),
196+ uint8_t (version >> 8 ),
197+ uint8_t (version)
198+ };
199+ helper_write (file, version_bytes, 4 );
185200 } else {
186201 const uint32_t version = GGUF_VERSION;
187202 helper_write (file, version);
@@ -195,6 +210,9 @@ static FILE * get_handcrafted_file(const unsigned int seed, const enum handcraft
195210 if (hft == HANDCRAFTED_HEADER_BAD_N_TENSORS) {
196211 const uint64_t n_tensors = -1 ;
197212 helper_write (file, n_tensors);
213+ } else if (hft == HANDCRAFTED_HEADER_N_TENSORS_MAX) {
214+ const uint64_t n_tensors = SIZE_MAX;
215+ helper_write (file, n_tensors);
198216 } else {
199217 const uint64_t n_tensors = tensor_configs.size ();
200218 helper_write (file, n_tensors);
@@ -213,6 +231,8 @@ static FILE * get_handcrafted_file(const unsigned int seed, const enum handcraft
213231 n_kv += 1 ;
214232 } else if (hft == HANDCRAFTED_HEADER_BAD_N_KV) {
215233 n_kv = -1 ;
234+ } else if (hft == HANDCRAFTED_HEADER_N_KV_MAX) {
235+ n_kv = SIZE_MAX;
216236 }
217237 helper_write (file, n_kv);
218238 }
@@ -670,6 +690,9 @@ static std::pair<int, int> test_handcrafted_file(const unsigned int seed) {
670690 HANDCRAFTED_HEADER_BAD_VERSION_FUTURE,
671691 HANDCRAFTED_HEADER_BAD_N_KV,
672692 HANDCRAFTED_HEADER_BAD_N_TENSORS,
693+ HANDCRAFTED_HEADER_ENDIANNESS_MISMATCH,
694+ HANDCRAFTED_HEADER_N_TENSORS_MAX,
695+ HANDCRAFTED_HEADER_N_KV_MAX,
673696 HANDCRAFTED_HEADER_EMPTY,
674697
675698 HANDCRAFTED_KV_BAD_KEY_SIZE,
@@ -1292,6 +1315,114 @@ static std::pair<int, int> test_gguf_set_kv(ggml_backend_dev_t dev, const unsign
12921315 return std::make_pair (npass, ntest);
12931316}
12941317
1318+ static std::pair<int , int > test_version_compatibility (const unsigned int seed) {
1319+ printf (" %s: testing GGUF version compatibility\n " , __func__);
1320+
1321+ int npass = 0 ;
1322+ int ntest = 0 ;
1323+
1324+ FILE * file_v3 = get_handcrafted_file (seed, HANDCRAFTED_DATA_SUCCESS);
1325+
1326+ #ifdef _WIN32
1327+ if (!file_v3) {
1328+ printf (" failed to create tmpfile(), needs elevated privileges on Windows" );
1329+ printf (" skipping tests" );
1330+ return std::make_pair (0 , 0 );
1331+ }
1332+ #else
1333+ GGML_ASSERT (file_v3);
1334+ #endif
1335+
1336+ struct ggml_context * ctx = nullptr ;
1337+ struct gguf_init_params params = {
1338+ /* no_alloc =*/ false ,
1339+ /* ctx =*/ &ctx,
1340+ };
1341+ struct gguf_context * gguf_ctx = gguf_init_from_file_impl (file_v3, params);
1342+
1343+ printf (" %s: read_version_3: " , __func__);
1344+ if (gguf_ctx && gguf_get_version (gguf_ctx) == GGUF_VERSION) {
1345+ printf (" \033 [1;32mOK\033 [0m\n " );
1346+ npass++;
1347+ } else {
1348+ printf (" \033 [1;31mFAIL\033 [0m\n " );
1349+ }
1350+ ntest++;
1351+
1352+ fclose (file_v3);
1353+ if (gguf_ctx) {
1354+ ggml_free (ctx);
1355+ gguf_free (gguf_ctx);
1356+ }
1357+
1358+ printf (" \n " );
1359+ return std::make_pair (npass, ntest);
1360+ }
1361+
1362+ static std::pair<int , int > test_large_file_handling (ggml_backend_dev_t dev, const unsigned int seed) {
1363+ (void )seed;
1364+ ggml_backend_t backend = ggml_backend_dev_init (dev, nullptr );
1365+ printf (" %s: device=%s, backend=%s\n " , __func__, ggml_backend_dev_description (dev), ggml_backend_name (backend));
1366+
1367+ int npass = 0 ;
1368+ int ntest = 0 ;
1369+
1370+ struct gguf_context * gguf_ctx = gguf_init_empty ();
1371+ for (int i = 0 ; i < 1000 ; ++i) {
1372+ const std::string key = " large_test_key_" + std::to_string (i);
1373+ gguf_set_val_u32 (gguf_ctx, key.c_str (), i);
1374+ }
1375+
1376+ printf (" %s: large_kv_count: " , __func__);
1377+ if (gguf_get_n_kv (gguf_ctx) == 1000 ) {
1378+ printf (" \033 [1;32mOK\033 [0m\n " );
1379+ npass++;
1380+ } else {
1381+ printf (" \033 [1;31mFAIL\033 [0m\n " );
1382+ }
1383+ ntest++;
1384+
1385+ FILE * file = tmpfile ();
1386+ #ifdef _WIN32
1387+ if (!file) {
1388+ printf (" failed to create tmpfile(), needs elevated privileges on Windows" );
1389+ printf (" skipping remaining tests" );
1390+ gguf_free (gguf_ctx);
1391+ ggml_backend_free (backend);
1392+ return std::make_pair (npass, ntest);
1393+ }
1394+ #else
1395+ GGML_ASSERT (file);
1396+ #endif
1397+
1398+ std::vector<int8_t > buf;
1399+ gguf_write_to_buf (gguf_ctx, buf, false );
1400+ GGML_ASSERT (fwrite (buf.data (), 1 , buf.size (), file) == buf.size ());
1401+ rewind (file);
1402+
1403+ struct gguf_init_params params = {/* no_alloc =*/ false , /* ctx =*/ nullptr };
1404+ struct gguf_context * gguf_ctx_read = gguf_init_from_file_impl (file, params);
1405+
1406+ printf (" %s: large_kv_roundtrip: " , __func__);
1407+ if (gguf_ctx_read && gguf_get_n_kv (gguf_ctx_read) == 1000 ) {
1408+ printf (" \033 [1;32mOK\033 [0m\n " );
1409+ npass++;
1410+ } else {
1411+ printf (" \033 [1;31mFAIL\033 [0m\n " );
1412+ }
1413+ ntest++;
1414+
1415+ fclose (file);
1416+ gguf_free (gguf_ctx);
1417+ if (gguf_ctx_read) {
1418+ gguf_free (gguf_ctx_read);
1419+ }
1420+ ggml_backend_free (backend);
1421+
1422+ printf (" \n " );
1423+ return std::make_pair (npass, ntest);
1424+ }
1425+
12951426static void print_usage () {
12961427 printf (" usage: test-gguf [seed]\n " );
12971428 printf (" if no seed is unspecified then a random seed is used\n " );
@@ -1318,6 +1449,12 @@ int main(int argc, char ** argv) {
13181449 ntest += result.second ;
13191450 }
13201451
1452+ {
1453+ std::pair<int , int > result = test_version_compatibility (seed);
1454+ npass += result.first ;
1455+ ntest += result.second ;
1456+ }
1457+
13211458 for (size_t i = 0 ; i < ggml_backend_dev_count (); ++i) {
13221459 ggml_backend_dev_t dev = ggml_backend_dev_get (i);
13231460
@@ -1332,6 +1469,12 @@ int main(int argc, char ** argv) {
13321469 npass += result.first ;
13331470 ntest += result.second ;
13341471 }
1472+
1473+ {
1474+ std::pair<int , int > result = test_large_file_handling (dev, seed);
1475+ npass += result.first ;
1476+ ntest += result.second ;
1477+ }
13351478 }
13361479
13371480 printf (" %d/%d tests passed\n " , npass, ntest);
0 commit comments