252 lines
6.8 KiB
C
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 */
|