char-fork/llama.cpp

git clone https://git.t4t.associates/char-fork/llama.cpp

Georgi Gerganovbuild : fix xcframework + cmake clean-up (#27304)27e345b57

char/aliceai-t5-moe
23.6 KiB700 linesraw
1#include "ggml.h"
2#include "gguf.h"
3
4#include <algorithm>
5#include <clocale>
6#include <cstddef>
7#include <cstdio>
8#include <cstdlib>   /* abort() */
9#include <cstring>
10#include <stdexcept>
11#include <string>
12
13#include <sstream>
14#include <fstream>
15
16#ifdef __cplusplus
17extern "C" {
18#endif
19
20#include "hash/xxhash/xxhash.h"
21#include "hash/sha256/sha256.h"
22
23#ifdef __cplusplus
24}
25#endif
26
27// sha1 is compiled as C++ and lives in a namespace, see scripts/sync_vendor.py
28#include "hash/sha1/sha1.h"
29using namespace vendor_hash;
30
31
32// uuid.uuid5(uuid.NAMESPACE_URL, 'en.wikipedia.org/wiki/Llama.cpp')
33#define UUID_NAMESPACE_LLAMA_CPP "ef001206-dadc-5f6d-a15f-3359e577d4e5"
34#define UUID_NAMESPACE_LLAMA_CPP_HEX 0xef, 0x00, 0x12, 0x06, 0xda, 0xdc, 0x5f, 0x6d, 0xa1, 0x5f, 0x33, 0x59, 0xe5, 0x77, 0xd4, 0xe5
35
36
37#define HASH_TYPE_SHA256_STR "sha256"
38#define HASH_TYPE_SHA1_STR   "sha1"
39#define HASH_TYPE_XXH64_STR  "xxh64"
40#define HASH_TYPE_UUID_STR   "uuid"
41
42
43typedef enum {
44    HASH_EXIT_SUCCESS = 0, // All hash has been generated or validated
45    HASH_EXIT_FAILURE = 1, // Generic Failure
46    HASH_EXIT_MISMATCH = 2, // Hash mismatched during validation
47    HASH_EXIT_MANIFEST_MISSING_ENTRY = 3, // Hash attempted validation but missing entry in manifest
48    HASH_EXIT_MANIFEST_UNKNOWN_HASH = 4, // Manifest is present, but we do not know any hash format within it
49    HASH_EXIT_MANIFEST_FILE_ERROR = 5 // Manifest is either missing or not a known format
50} hash_exit_code_t;
51
52
53typedef enum {
54    HASH_MANIFEST_NOT_FOUND,
55    HASH_MANIFEST_MISMATCH,
56    HASH_MANIFEST_OK,
57} hash_manifest_result_t;
58
59
60struct hash_params {
61    std::string input;
62    bool xxh64 = false;
63    bool sha1 = false;
64    bool sha256 = false;
65    bool uuid = false;
66
67    bool no_layer = false;
68
69    bool manifest_is_usable = false;
70    std::string manifest_file;
71};
72
73struct manifest_check_params {
74    bool xxh64 = false;
75    bool sha1 = false;
76    bool sha256 = false;
77    bool uuid = false;
78};
79
80static char const * hash_manifest_result_to_str(hash_manifest_result_t value) {
81    switch (value) {
82        case HASH_MANIFEST_NOT_FOUND: return "Not Found";
83        case HASH_MANIFEST_MISMATCH: return "Mismatch";
84        case HASH_MANIFEST_OK: return "Ok";
85    }
86    return "?";
87}
88
89static char const * hash_exit_code_to_str(hash_exit_code_t value) {
90    switch (value) {
91        case HASH_EXIT_SUCCESS: return "Success";
92        case HASH_EXIT_FAILURE: return "Failure";
93        case HASH_EXIT_MISMATCH: return "Mismatch";
94        case HASH_EXIT_MANIFEST_MISSING_ENTRY: return "Manifest Missing Entry";
95        case HASH_EXIT_MANIFEST_UNKNOWN_HASH: return "Manifest Unknown Hash";
96        case HASH_EXIT_MANIFEST_FILE_ERROR: return "Manifest File Error";
97    }
98    return "?";
99}
100
101static void hash_print_usage(const char * executable) {
102    const hash_params default_params;
103    printf("\n");
104    printf("usage: %s [options] GGUF_IN\n", executable);
105    printf("\n");
106    printf("Hash a GGUF file");
107    printf("\n");
108    printf("options:\n");
109    printf("  -h, --help              show this help message and exit\n");
110    printf("      --xxh64             use xxh64 hash\n");
111    printf("      --sha1              use sha1 hash\n");
112    printf("      --sha256            use sha256 hash\n");
113    printf("      --all               use all hash\n");
114    printf("      --no-layer          exclude per layer hash\n");
115    printf("      --uuid              generate UUIDv5 ID\n");
116    printf("  -c, --check <manifest>  verify against a manifest\n");
117    printf("\n");
118}
119
120static void hash_params_parse_ex(int argc, const char ** argv, hash_params & params) {
121    std::string arg;
122    bool invalid_param = false;
123    const std::string arg_prefix = "--";
124
125    int arg_idx = 1;
126    for (; arg_idx < argc && strncmp(argv[arg_idx], "--", 2) == 0; arg_idx++) {
127        arg = argv[arg_idx];
128        if (arg.compare(0, arg_prefix.size(), arg_prefix) == 0) {
129            std::replace(arg.begin(), arg.end(), '_', '-');
130        }
131
132        bool arg_found = false;
133        if (arg == "-h" || arg == "--help") {
134            hash_print_usage(argv[0]);
135            exit(0);
136        }
137
138        if (arg == "--xxh64") {
139            arg_found = true;
140            params.xxh64 = true;
141        }
142
143        if (arg == "--sha1") {
144            arg_found = true;
145            params.sha1 = true;
146        }
147
148        if (arg == "--uuid") {
149            arg_found = true;
150            params.uuid = true;
151        }
152
153        if (arg == "--sha256") {
154            arg_found = true;
155            params.sha256 = true;
156        }
157
158        if (arg == "--all") {
159            arg_found = true;
160            params.sha256 = true;
161            params.sha1 = true;
162            params.xxh64 = true;
163        }
164
165        if (arg == "--no-layer") {
166            arg_found = true;
167            params.no_layer = true;
168        }
169
170        if (arg == "-c" || arg == "--check") {
171            if (++arg_idx >= argc) {
172                invalid_param = true;
173                break;
174            }
175            arg_found = true;
176            params.manifest_file = argv[arg_idx];
177        }
178
179        if (!arg_found) {
180            throw std::invalid_argument("error: unknown argument: " + arg);
181        }
182    }
183
184    if (invalid_param) {
185        throw std::invalid_argument("error: invalid parameter for argument:" + arg);
186    }
187
188    if (argc - arg_idx < 1) {
189        throw std::invalid_argument("error: bad arguments");
190    }
191
192    params.input = argv[arg_idx++];
193}
194
195static bool hash_params_parse(int argc, const char ** argv, hash_params & params) {
196    bool result = true;
197    try {
198        hash_params_parse_ex(argc, argv, params);
199    }
200    catch (const std::invalid_argument & ex) {
201        fprintf(stderr, "%s\n", ex.what());
202        hash_print_usage(argv[0]);
203        exit(EXIT_FAILURE);
204    }
205    return result;
206}
207
208static bool manifest_type(const std::string & manifest_file, manifest_check_params & manifest_check) {
209    if (manifest_file.empty()) {
210        return false;
211    }
212
213    std::ifstream file(manifest_file);
214    if (!file.is_open()) {
215        return false;
216    }
217
218    std::string manifest_entry_line;
219    while (getline(file, manifest_entry_line)) {
220        // hash_type_str hash_str tensor_name
221        // e.g. 'xxh64     f66e9cd66a4396a0  test.gguf:tensor_0'
222        std::istringstream line_stream(manifest_entry_line);
223        std::string file_hash_type;
224        if (line_stream >> file_hash_type) {
225            if (file_hash_type == HASH_TYPE_SHA256_STR) {
226                manifest_check.sha256 = true;
227            } else if (file_hash_type == HASH_TYPE_SHA1_STR) {
228                manifest_check.sha1 = true;
229            } else if (file_hash_type == HASH_TYPE_XXH64_STR) {
230                manifest_check.xxh64 = true;
231            } else if (file_hash_type == HASH_TYPE_UUID_STR) {
232                manifest_check.uuid = true;
233            }
234        }
235    }
236
237    return true;
238}
239
240static hash_manifest_result_t manifest_verify(const std::string& manifest_file, const std::string& hash_type_str, const std::string& hash_str, const std::string& tensor_name) {
241    if (manifest_file.empty()) {
242        return HASH_MANIFEST_NOT_FOUND;
243    }
244
245    std::ifstream file(manifest_file);
246    if (!file.is_open()) {
247        return HASH_MANIFEST_NOT_FOUND;
248    }
249
250    std::string manifest_entry_line;
251    while (getline(file, manifest_entry_line)) {
252        std::istringstream line_stream(manifest_entry_line);
253        std::string file_hash_type;
254        std::string file_hash;
255        std::string file_tensor_name;
256        if (line_stream >> file_hash_type >> file_hash >> file_tensor_name) {
257            // Line parsed. Check hash validity
258
259            if (file_hash_type != hash_type_str) {
260                continue;
261            }
262
263            if (file_tensor_name != tensor_name) {
264                continue;
265            }
266
267            return (file_hash == hash_str) ? HASH_MANIFEST_OK : HASH_MANIFEST_MISMATCH;
268        }
269    }
270
271    return HASH_MANIFEST_NOT_FOUND;
272}
273
274static void generate_uuidv5(const unsigned char sha1_digest[20], unsigned char uuid[16]) {
275    // Ref: https://www.rfc-editor.org/rfc/rfc9562.html#section-5.5
276    // Assumes that digest was processed correctly with the expected namespace
277    for (int i = 0; i < 16; i++) {
278        uuid[i] = sha1_digest[i];
279    }
280
281    // Set bits corresponding to UUID ver 5
282    uuid[ 6] &= ~(0xF << 4);
283    uuid[ 6] |= (5 << 4);
284
285    // Set bits corresponding to UUID variant 0b10XX
286    uuid[ 8] &= ~(0xc << 4);
287    uuid[ 8] |= (0x8 << 4);
288}
289
290static hash_exit_code_t gguf_hash(const hash_params & hash_params) {
291    const std::string & fname = hash_params.input;
292    struct ggml_context * ctx_data = NULL;
293
294    struct gguf_init_params params = {
295        /*.no_alloc = */ false,
296        /*.ctx      = */ &ctx_data,
297    };
298
299    // xxh64 init
300    XXH64_state_t* xxh64_model_hash_state = NULL;
301    if (hash_params.xxh64) {
302        xxh64_model_hash_state = XXH64_createState();
303        if (xxh64_model_hash_state==NULL) {
304            abort();
305        }
306
307        XXH64_hash_t const seed = 0;
308        if (XXH64_reset(xxh64_model_hash_state, seed) == XXH_ERROR) {
309            abort();
310        }
311    }
312
313    // sha1 init
314    SHA1_CTX sha1_model_hash_ctx;
315    if (hash_params.sha1) {
316        SHA1Init(&sha1_model_hash_ctx);
317    }
318
319    // sha256 init
320    sha256_t sha256_model_hash_ctx;
321    if (hash_params.sha256) {
322        sha256_init(&sha256_model_hash_ctx);
323    }
324
325    // sha1 for uuid init
326    SHA1_CTX sha1_for_uuid_ctx;
327    if (hash_params.uuid) {
328        unsigned char const uuidv5_namespace[] = {UUID_NAMESPACE_LLAMA_CPP_HEX};
329        SHA1Init(&sha1_for_uuid_ctx);
330        SHA1Update( &sha1_for_uuid_ctx, (unsigned char const *)uuidv5_namespace, sizeof(uuidv5_namespace));
331    }
332
333    struct gguf_context * ctx = gguf_init_from_file(fname.c_str(), params);
334    const int n_tensors = gguf_get_n_tensors(ctx);
335    bool tensor_layer_in_manifest = false;
336    bool model_in_manifest = false;
337    bool tensor_layer_has_mismatch = false;
338    bool model_has_mismatch = false;
339    for (int i = 0; i < n_tensors; ++i) {
340        const char * name = gguf_get_tensor_name(ctx, i);
341        struct ggml_tensor * cur = ggml_get_tensor(ctx_data, name);
342        auto n_bytes = ggml_nbytes(cur);
343        auto *raw_data = cur->data;
344        const std::string tensor_layer_name = fname + ":" + name;
345
346        if (hash_params.xxh64) {
347
348            if (!hash_params.no_layer) {
349                // Per Layer Hash
350                XXH64_hash_t hash = XXH64(raw_data, n_bytes, 0);
351
352                char hex_result[17];
353                for (int  offset = 0; offset < 8; offset++) {
354                    unsigned int shift_bits_by = (8 * (8 - offset - 1));
355                    snprintf( ( hex_result + (2*offset)), sizeof(hex_result) - (2*offset), "%02x", (unsigned char) (hash >> shift_bits_by)&0xff);
356                }
357
358                if (hash_params.manifest_is_usable) {
359                    hash_manifest_result_t verify_result = manifest_verify(hash_params.manifest_file, HASH_TYPE_XXH64_STR, hex_result, tensor_layer_name);
360
361                    switch (verify_result) {
362                        case HASH_MANIFEST_NOT_FOUND:
363                            break;
364                        case HASH_MANIFEST_MISMATCH:
365                            tensor_layer_in_manifest = true;
366                            tensor_layer_has_mismatch = true;
367                            break;
368                        case HASH_MANIFEST_OK:
369                            tensor_layer_in_manifest = true;
370                            break;
371                    }
372
373                    printf("%-8s  %-s  %s  -  %s\n", HASH_TYPE_XXH64_STR, hex_result, tensor_layer_name.c_str(), hash_manifest_result_to_str(verify_result));
374                } else {
375                    printf("%-8s  %-s  %s\n", HASH_TYPE_XXH64_STR, hex_result, tensor_layer_name.c_str());
376                }
377            }
378
379            // Overall Model Hash
380            if (XXH64_update(xxh64_model_hash_state, raw_data, n_bytes) == XXH_ERROR) abort();
381        }
382
383        if (hash_params.sha1) {
384
385            if (!hash_params.no_layer) {
386                // Per Layer Hash
387                char result[21]; // sha1 outputs 20 bytes
388                SHA1( result, (const char *)raw_data, n_bytes);
389
390                char hex_result[41] = {0};
391                for (int  offset = 0; offset < 20; offset++) {
392                    snprintf( ( hex_result + (2*offset)), sizeof(hex_result) - (2*offset), "%02x", result[offset]&0xff);
393                }
394
395                if (hash_params.manifest_is_usable) {
396                    hash_manifest_result_t verify_result = manifest_verify(hash_params.manifest_file, HASH_TYPE_SHA1_STR, hex_result, tensor_layer_name);
397
398                    switch (verify_result) {
399                        case HASH_MANIFEST_NOT_FOUND:
400                            break;
401                        case HASH_MANIFEST_MISMATCH:
402                            tensor_layer_in_manifest = true;
403                            tensor_layer_has_mismatch = true;
404                            break;
405                        case HASH_MANIFEST_OK:
406                            tensor_layer_in_manifest = true;
407                            break;
408                    }
409
410                    printf("%-8s  %-s  %s  -  %s\n", HASH_TYPE_SHA1_STR, hex_result, tensor_layer_name.c_str(), hash_manifest_result_to_str(verify_result));
411                } else {
412                    printf("%-8s  %-s  %s\n", HASH_TYPE_SHA1_STR, hex_result, tensor_layer_name.c_str());
413                }
414            }
415
416            // Overall Model Hash
417            SHA1Update( &sha1_model_hash_ctx, (unsigned char const *)raw_data, n_bytes);
418        }
419
420        if (hash_params.sha256) {
421
422            if (!hash_params.no_layer) {
423                // Per Layer Hash
424                unsigned char result[SHA256_DIGEST_SIZE]; // sha256 outputs 32 bytes
425                sha256_hash((unsigned char*) result, (const unsigned char *)raw_data, n_bytes);
426
427                char hex_result[SHA256_DIGEST_SIZE * 2 + 1] = {0};
428                for (int  offset = 0; offset < SHA256_DIGEST_SIZE; offset++) {
429                    snprintf( ( hex_result + (2*offset)), sizeof(hex_result) - (2*offset), "%02x", result[offset]&0xff);
430                }
431
432                if (hash_params.manifest_is_usable) {
433                    hash_manifest_result_t verify_result = manifest_verify(hash_params.manifest_file, HASH_TYPE_SHA256_STR, hex_result, tensor_layer_name);
434
435                    switch (verify_result) {
436                        case HASH_MANIFEST_NOT_FOUND:
437                            break;
438                        case HASH_MANIFEST_MISMATCH:
439                            tensor_layer_in_manifest = true;
440                            tensor_layer_has_mismatch = true;
441                            break;
442                        case HASH_MANIFEST_OK:
443                            tensor_layer_in_manifest = true;
444                            break;
445                    }
446
447                    printf("%-8s  %-s  %s  -  %s\n", HASH_TYPE_SHA256_STR, hex_result, tensor_layer_name.c_str(), hash_manifest_result_to_str(verify_result));
448                } else {
449                    printf("%-8s  %-s  %s\n", HASH_TYPE_SHA256_STR, hex_result, tensor_layer_name.c_str());
450                }
451            }
452
453            // Overall Model Hash
454            sha256_update( &sha256_model_hash_ctx, (unsigned char const *)raw_data, n_bytes);
455        }
456
457        if (hash_params.uuid) {
458            SHA1Update( &sha1_for_uuid_ctx, (unsigned char const *)raw_data, n_bytes);
459        }
460    }
461
462    if (hash_params.xxh64) {
463        XXH64_hash_t const hash = XXH64_digest(xxh64_model_hash_state);
464
465        char hex_result[17];
466        for (int  offset = 0; offset < 8; offset++) {
467            unsigned int shift_bits_by = (8 * (8 - offset - 1));
468            snprintf( ( hex_result + (2*offset)), sizeof(hex_result) - (2*offset), "%02x", (unsigned char) (hash >> shift_bits_by)&0xff);
469        }
470
471        if (hash_params.manifest_is_usable) {
472            hash_manifest_result_t verify_result = manifest_verify(hash_params.manifest_file, HASH_TYPE_XXH64_STR, hex_result, fname);
473
474            switch (verify_result) {
475                case HASH_MANIFEST_NOT_FOUND:
476                    break;
477                case HASH_MANIFEST_MISMATCH:
478                    model_in_manifest = true;
479                    model_has_mismatch = true;
480                    break;
481                case HASH_MANIFEST_OK:
482                    model_in_manifest = true;
483                    break;
484            }
485
486            printf("%-8s  %-s  %s  -  %s\n", HASH_TYPE_XXH64_STR, hex_result, fname.c_str(), hash_manifest_result_to_str(verify_result));
487        } else {
488            printf("%-8s  %-s  %s\n", HASH_TYPE_XXH64_STR, hex_result, fname.c_str());
489        }
490    }
491
492    if (hash_params.sha1) {
493        unsigned char result[21];
494        SHA1Final(result, &sha1_model_hash_ctx);
495
496        char hex_result[41];
497        for (int  offset = 0; offset < 20; offset++) {
498            snprintf( ( hex_result + (2*offset)), sizeof(hex_result) - (2*offset), "%02x", result[offset]&0xff);
499        }
500
501        if (hash_params.manifest_is_usable) {
502            hash_manifest_result_t verify_result = manifest_verify(hash_params.manifest_file, HASH_TYPE_SHA1_STR, hex_result, fname);
503
504            switch (verify_result) {
505                case HASH_MANIFEST_NOT_FOUND:
506                    break;
507                case HASH_MANIFEST_MISMATCH:
508                    model_in_manifest = true;
509                    model_has_mismatch = true;
510                    break;
511                case HASH_MANIFEST_OK:
512                    model_in_manifest = true;
513                    break;
514            }
515
516            printf("%-8s  %-s  %s  -  %s\n", HASH_TYPE_SHA1_STR, hex_result, fname.c_str(), hash_manifest_result_to_str(verify_result));
517        } else {
518            printf("%-8s  %-s  %s\n", HASH_TYPE_SHA1_STR, hex_result, fname.c_str());
519        }
520    }
521
522    if (hash_params.sha256) {
523        unsigned char result[SHA256_DIGEST_SIZE]; // sha256 outputs 32 bytes
524        sha256_final( &sha256_model_hash_ctx,  result);
525
526        char hex_result[SHA256_DIGEST_SIZE * 2 + 1] = {0};
527        for (int  offset = 0; offset < SHA256_DIGEST_SIZE; offset++) {
528            snprintf( ( hex_result + (2*offset)), sizeof(hex_result) - (2*offset), "%02x", result[offset]&0xff);
529        }
530
531        if (hash_params.manifest_is_usable) {
532            hash_manifest_result_t verify_result = manifest_verify(hash_params.manifest_file, HASH_TYPE_SHA256_STR, hex_result, fname);
533
534            switch (verify_result) {
535                case HASH_MANIFEST_NOT_FOUND:
536                    break;
537                case HASH_MANIFEST_MISMATCH:
538                    model_in_manifest = true;
539                    model_has_mismatch = true;
540                    break;
541                case HASH_MANIFEST_OK:
542                    model_in_manifest = true;
543                    break;
544            }
545
546            printf("%-8s  %-s  %s  -  %s\n", HASH_TYPE_SHA256_STR, hex_result, fname.c_str(), hash_manifest_result_to_str(verify_result));
547        } else {
548            printf("%-8s  %-s  %s\n", HASH_TYPE_SHA256_STR, hex_result, fname.c_str());
549        }
550    }
551
552    if (hash_params.uuid) {
553        unsigned char result[21];
554        SHA1Final(result, &sha1_for_uuid_ctx);
555
556        unsigned char uuid[16];
557        generate_uuidv5(result, uuid);
558
559        char string_buffer[37] = {0};
560        snprintf(string_buffer, sizeof(string_buffer), "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
561            uuid[0], uuid[1], uuid[2], uuid[3],
562            uuid[4], uuid[5], uuid[6], uuid[7],
563            uuid[8], uuid[9], uuid[10], uuid[11],
564            uuid[12], uuid[13], uuid[14], uuid[15]);
565
566        if (hash_params.manifest_is_usable) {
567            hash_manifest_result_t verify_result = manifest_verify(hash_params.manifest_file, HASH_TYPE_SHA256_STR, string_buffer, fname);
568
569            switch (verify_result) {
570                case HASH_MANIFEST_NOT_FOUND:
571                    break;
572                case HASH_MANIFEST_MISMATCH:
573                    model_in_manifest = true;
574                    model_has_mismatch = true;
575                    break;
576                case HASH_MANIFEST_OK:
577                    model_in_manifest = true;
578                    break;
579            }
580
581            printf("%-8s  %-s  %s  -  %s\n", HASH_TYPE_UUID_STR, string_buffer, fname.c_str(), hash_manifest_result_to_str(verify_result));
582        } else {
583            printf("%-8s  %-s  %s\n", HASH_TYPE_UUID_STR, string_buffer, fname.c_str());
584        }
585    }
586
587
588    ggml_free(ctx_data);
589    gguf_free(ctx);
590
591
592    if (hash_params.manifest_is_usable) {
593        // In hash verification mode
594
595        if (!model_in_manifest) {
596            // model missing in manifest?
597
598            // Check tensor layer...
599            if (!tensor_layer_in_manifest) {
600                // Still missing? Maybe we are reading the wrong manifest.
601                return HASH_EXIT_MANIFEST_MISSING_ENTRY;
602            }
603
604            if (tensor_layer_has_mismatch) {
605                // Per tensor check found error
606                return HASH_EXIT_FAILURE;
607            }
608
609            // All per tensor layer checks passed? Sounds good enough.
610            return HASH_EXIT_SUCCESS;
611        }
612
613        // Overall model check passed, but let's check per layer just in case
614        // If missing, we don't care too much as the overall model checked
615        if (tensor_layer_in_manifest && tensor_layer_has_mismatch) {
616            return HASH_EXIT_FAILURE;
617        }
618
619        if (model_has_mismatch) {
620            // model has failed hash somewhere in the model
621            return HASH_EXIT_FAILURE;
622        }
623
624        // All checks appears to be fine
625        return HASH_EXIT_SUCCESS;
626    }
627
628    // In hash generation mode
629    return HASH_EXIT_SUCCESS;
630}
631
632int main(int argc, const char ** argv) {
633    std::setlocale(LC_NUMERIC, "C");
634
635    hash_params params;
636    manifest_check_params manifest_check;
637    hash_params_parse(argc, argv, params);
638
639    if (!params.manifest_file.empty()) {
640        if (!manifest_type(params.manifest_file, manifest_check)) {
641            printf("ERROR cannot open manifest %s", params.manifest_file.c_str());
642            return HASH_EXIT_MANIFEST_FILE_ERROR;
643        }
644
645        if (!manifest_check.sha256 && !manifest_check.sha1 && !manifest_check.xxh64 && !manifest_check.uuid) {
646            printf("ERROR manifest does not have any known hash format in %s", params.manifest_file.c_str());
647            return HASH_EXIT_MANIFEST_UNKNOWN_HASH;
648        }
649
650        printf("manifest  %s", params.manifest_file.c_str());
651
652        if (manifest_check.sha256) {
653            printf("  sha256");
654        }
655
656        if (manifest_check.sha1) {
657            printf("  sha1");
658        }
659
660        if (manifest_check.xxh64) {
661            printf("  xxh64");
662        }
663
664        if (manifest_check.uuid) {
665            printf("  uuid");
666        }
667
668        printf("\n");
669
670        // Autoselect the highest security hash if manifest is provided but
671        // the user has not specifically defined the hash they care about
672        if (!params.xxh64 && !params.sha1 && !params.uuid && !params.sha256) {
673            // User has not selected a specific value, pick most secure hash
674            if (manifest_check.sha256) {
675                params.sha256 = true;
676            } else if (manifest_check.sha1) {
677                params.sha1 = true;
678            } else if (manifest_check.xxh64) {
679                params.xxh64 = true;
680            } else if (manifest_check.uuid) {
681                params.uuid = true;
682            }
683        }
684
685        params.manifest_is_usable = true;
686    }
687
688    // By default if no swich argument provided, assume xxh64
689    if (!params.xxh64 && !params.sha1 && !params.uuid && !params.sha256) {
690        params.xxh64 = true;
691    }
692
693    hash_exit_code_t exit_code = gguf_hash(params);
694
695    if (params.manifest_is_usable) {
696        printf("\nVerification results for %s - %s\n", params.manifest_file.c_str(), hash_exit_code_to_str(exit_code));
697    }
698
699    return exit_code;
700}