Foundry/tt/convert/ft_gguf_map.h

252 lines
6.8 KiB
C

/* SPDX-License-Identifier: AGPL-3.0-or-later */
/* Copyright (C) 2026 David (Corey) Herrera */
/**
* @file ft_gguf_map.h
* @brief Read-only memory-mapped GGUF reader: header, metadata, tensor directory, splits.
*
* Written from a behaviour specification (docs/cleanroom/f_gguf_spec.md); no ggml or llama.cpp source was consulted.
*
* Every part file is mapped read-only and private; tensor `data` points straight
* into the mapping (no copy). Failures print one line to stderr and return a
* negative FT_GGUF_* code.
*
* @status None
* @issues Kept as-is: keys and names truncate to 127 bytes; split filename digits
* are not compared with the metadata; split tensor totals sum in 32 bits;
* a zero-element tensor of any type is accepted; data_offset alignment is
* not checked.
* @todo None
*/
#ifndef FT_GGUF_MAP_H
#define FT_GGUF_MAP_H
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define FT_GGUF_MAX_NAME 128
#define FT_GGUF_MAX_PATH 1024
#define FT_GGUF_MAX_PARTS 256u
/** GGML element type ids this reader names in its API. */
enum {
FT_GGML_TYPE_F32 = 0,
FT_GGML_TYPE_F16 = 1,
FT_GGML_TYPE_Q8_0 = 8,
FT_GGML_TYPE_Q2_K = 10,
FT_GGML_TYPE_Q3_K = 11,
FT_GGML_TYPE_Q4_K = 12,
FT_GGML_TYPE_Q5_K = 13,
FT_GGML_TYPE_Q6_K = 14,
FT_GGML_TYPE_BF16 = 30
};
/** Status codes returned by the reader. */
enum {
FT_GGUF_OK = 0,
FT_GGUF_ERROR = -1,
FT_GGUF_ENOMEM = -2,
FT_GGUF_EINVAL = -3,
FT_GGUF_EIO = -4,
FT_GGUF_ENOTFOUND = -5
};
/**
* One tensor directory entry. `dims[0]` is the fastest axis; dims past `n_dims`
* are 1. `data_offset` is relative to the owning part's tensor-data section,
* `byte_count` is the on-disk size, `data` points into the mapping.
*/
typedef struct {
char name[FT_GGUF_MAX_NAME];
uint32_t n_dims;
uint64_t dims[4];
uint32_t ggml_type;
uint64_t data_offset;
uint64_t byte_count;
const void *data;
} ft_gguf_tensor;
/** An open GGUF file or split set. */
typedef struct ft_gguf_map ft_gguf_map;
/**
* Map and parse a GGUF file. A file declaring split.count opens its whole set
* (found by the `-NNNNN-of-NNNNN.gguf` name pattern).
*
* @param path GGUF path, shorter than FT_GGUF_MAX_PATH bytes
* @param out receives the handle on success (NULL on failure); the caller frees
* it with ft_gguf_map_close()
* @return FT_GGUF_OK, or FT_GGUF_EINVAL / EIO / ERROR / ENOMEM
*
* Thread-safe: yes (no shared state between handles)
* Reentrant: yes
* Signal-safe: no
*/
int ft_gguf_map_open(const char *path, ft_gguf_map **out);
/**
* Unmap every part and free the handle. NULL-safe.
*
* @param map handle from ft_gguf_map_open(), or NULL
*
* Thread-safe: no (not synchronised with readers of the same handle)
* Reentrant: no
* Signal-safe: no
*/
void ft_gguf_map_close(ft_gguf_map *map);
/**
* Number of tensors across all parts.
*
* @param map handle, or NULL
* @return tensor count; 0 for NULL
*
* Thread-safe: yes
* Reentrant: yes
* Signal-safe: yes
*/
uint32_t ft_gguf_map_tensor_count(const ft_gguf_map *map);
/**
* Tensor by directory index (part order, then file order).
*
* @param map handle, or NULL
* @param index directory index
* @return the entry, or NULL for a NULL handle or an index out of range
*
* Thread-safe: yes
* Reentrant: yes
* Signal-safe: yes
*/
const ft_gguf_tensor *ft_gguf_map_tensor(const ft_gguf_map *map, uint32_t index);
/**
* First tensor whose name equals `name`.
*
* @param map handle, or NULL
* @param name tensor name, or NULL
* @return the entry, or NULL when not found or on a NULL argument
*
* Thread-safe: yes
* Reentrant: yes
* Signal-safe: yes
*/
const ft_gguf_tensor *ft_gguf_map_find(const ft_gguf_map *map, const char *name);
/**
* GGUF header version (of part 1 for a split set).
*
* @param map handle, or NULL
* @return 2 or 3; 0 for NULL
*
* Thread-safe: yes
* Reentrant: yes
* Signal-safe: yes
*/
uint32_t ft_gguf_map_version(const ft_gguf_map *map);
/**
* Number of part files behind the handle (1 for an unsplit file).
*
* @param map handle, or NULL
* @return part count; 0 for NULL
*
* Thread-safe: yes
* Reentrant: yes
* Signal-safe: yes
*/
uint32_t ft_gguf_map_part_count(const ft_gguf_map *map);
/**
* Path of one part file, as given or as formed from the split name pattern.
*
* @param map handle, or NULL
* @param part_index 0-based part index
* @return the path (owned by the handle), or NULL when out of range
*
* Thread-safe: yes
* Reentrant: yes
* Signal-safe: yes
*/
const char *ft_gguf_map_part_path(const ft_gguf_map *map, uint32_t part_index);
/**
* Which part file holds a tensor.
*
* @param map handle, or NULL
* @param tensor an entry returned by this handle
* @return 0-based part index; UINT32_MAX for NULL or a foreign pointer
*
* Thread-safe: yes
* Reentrant: yes
* Signal-safe: yes
*/
uint32_t ft_gguf_map_tensor_part(const ft_gguf_map *map, const ft_gguf_tensor *tensor);
/**
* Absolute byte offset of a tensor's data within its part file.
*
* @param map handle, or NULL
* @param tensor an entry returned by this handle
* @return file offset; 0 for NULL or a foreign pointer
*
* Thread-safe: yes
* Reentrant: yes
* Signal-safe: yes
*/
uint64_t ft_gguf_map_tensor_file_offset(const ft_gguf_map *map, const ft_gguf_tensor *tensor);
/**
* Integer (or bool) metadata value; signed types come back sign-extended.
*
* @param map handle
* @param key metadata key
* @param out receives the value, written only on FT_GGUF_OK
* @return FT_GGUF_OK; FT_GGUF_ENOTFOUND; FT_GGUF_EINVAL for a NULL argument or
* a non-integer value
*
* Thread-safe: yes
* Reentrant: yes
* Signal-safe: yes
*/
int ft_gguf_map_metadata_u64(const ft_gguf_map *map, const char *key, uint64_t *out);
/**
* String metadata value, copied and NUL-terminated (truncated to fit).
*
* @param map handle
* @param key metadata key
* @param out caller buffer
* @param out_bytes size of `out`, at least 1
* @return FT_GGUF_OK; FT_GGUF_ENOTFOUND; FT_GGUF_EINVAL for a bad
* argument or a non-string value
*
* Thread-safe: yes
* Reentrant: yes
* Signal-safe: yes
*/
int ft_gguf_map_metadata_string(const ft_gguf_map *map, const char *key, char *out, size_t out_bytes);
/**
* Advise the kernel that a tensor's whole pages are no longer needed
* (MADV_DONTNEED). Pages shared with a neighbouring tensor are left alone.
*
* @param map handle, or NULL (no-op)
* @param tensor an entry returned by this handle, or NULL (no-op)
*
* Thread-safe: yes
* Reentrant: yes
* Signal-safe: no
*/
void ft_gguf_map_release(const ft_gguf_map *map, const ft_gguf_tensor *tensor);
#ifdef __cplusplus
}
#endif
#endif /* FT_GGUF_MAP_H */