// GGUF container assembly: header, metadata, tensor directory and data section.
//
// Layout, all little-endian:
//
// magic u32 "GGUF" (0x46554747 read as LE)
// version u32 2 or 3 (1 is refused)
// tensor_count u64
// metadata_kv_count u64
// kv[] key(string) + value_type(u32) + value
// tensor_info[] name(string) + n_dims(u32) + dims(u64 * n_dims) + ggml_type(u32) + offset(u64)
// padding to `general.alignment` (default 32)
// data tensor payloads, each at data_start + offset
//
// Parsing is a single pass with no allocation proportional to total tensor
// bytes: the caller's `Bytes` buffer is retained and tensor payloads are
// reached through `data_start` plus each entry's offset.
///|
/// Maximum tensor rank GGUF can carry (`GGML_MAX_DIMS`).
pub let gguf_max_dims : Int = 4
///|
/// Alignment used when `general.alignment` is absent (`GGUF_DEFAULT_ALIGNMENT`).
pub let gguf_default_alignment : UInt = 32U
///|
/// The magic bytes `GGUF`, read as a little-endian u32.
pub let gguf_magic : UInt = 0x46554747U
///|
/// Highest GGUF version this parser accepts.
pub let gguf_version_max : UInt = 3U
///|
/// One entry of the tensor directory.
///
/// `n_elems` is the product of `dims`. `nbytes` is the on-disk payload size, or
/// `None` for type ids whose block size this build does not know (removed types
/// and ids past the enum) — such a tensor is still listed so a directory dump
/// can show it, but it cannot be range-checked or dequantized.
pub(all) struct TensorInfo {
name : String
dims : Array[UInt64]
type_id : UInt
offset : UInt64
n_elems : Int64
nbytes : Int64?
} derive(Eq, Debug)
///|
/// Number of dimensions, for symmetry with the on-disk `n_dims`.
pub fn TensorInfo::n_dims(self : TensorInfo) -> Int {
self.dims.length()
}
///|
/// The ggml type of this tensor, resolved through the tensor package's table.
pub fn TensorInfo::ggml_type(self : TensorInfo) -> @tensor.GgmlType {
@tensor.GgmlType::from_id(self.type_id.reinterpret_as_int())
}
///|
/// Whether this build can dequantize the tensor's type.
pub fn TensorInfo::is_dequantizable(self : TensorInfo) -> Bool {
self.ggml_type().is_dequantizable()
}
///|
/// A parsed GGUF container. Holds a reference to the caller's buffer so tensor
/// payloads can be sliced on demand rather than copied at parse time.
pub(all) struct GgufFile {
version : UInt
alignment : UInt
tensors : Array[TensorInfo]
metadata : Array[(String, MetaValue)]
data_start : Int64
buf : Bytes
}
///|
pub fn GgufFile::tensor_count(self : GgufFile) -> Int {
self.tensors.length()
}
///|
pub fn GgufFile::metadata_count(self : GgufFile) -> Int {
self.metadata.length()
}
///|
/// Total buffer length, including the header, metadata and data sections.
pub fn GgufFile::len(self : GgufFile) -> Int64 {
self.buf.length().to_int64()
}
///|
/// Bytes available from `data_start` to the end of the buffer.
///
/// This is the upper bound a tensor's `offset + nbytes` may reach; it can
/// exceed the sum of all tensor sizes when the file has trailing padding or
/// embedded alignment gaps.
pub fn GgufFile::data_len(self : GgufFile) -> Int64 {
self.len() - self.data_start
}
///|
/// Look up a metadata value by key. Returns the first match, since GGUF keys
/// are unique in practice and a duplicate would be corruption.
pub fn GgufFile::get(self : GgufFile, key : String) -> MetaValue? {
for entry in self.metadata {
if entry.0 == key {
return Some(entry.1)
}
}
None
}
///|
/// Whether a key is present.
pub fn GgufFile::has_key(self : GgufFile, key : String) -> Bool {
self.get(key) is Some(_)
}
///|
/// Look up a string-valued metadata entry.
pub fn GgufFile::get_string(self : GgufFile, key : String) -> String? {
match self.get(key) {
Some(v) => v.as_string()
None => None
}
}
///|
/// Look up an integer-valued metadata entry, widening any integer width.
pub fn GgufFile::get_int(self : GgufFile, key : String) -> Int64? {
match self.get(key) {
Some(v) => v.as_int()
None => None
}
}
///|
/// Look up a float-valued metadata entry.
pub fn GgufFile::get_double(self : GgufFile, key : String) -> Double? {
match self.get(key) {
Some(v) => v.as_double()
None => None
}
}
///|
/// Find a tensor directory entry by name.
pub fn GgufFile::tensor(self : GgufFile, name : String) -> TensorInfo? {
for t in self.tensors {
if t.name == name {
return Some(t)
}
}
None
}
///|
/// Find a tensor directory entry by name, returning its index.
pub fn GgufFile::index_of_tensor(self : GgufFile, name : String) -> Int? {
for i, t in self.tensors {
if t.name == name {
return Some(i)
}
}
None
}
///|
/// The whole buffer this file was parsed from.
///
/// Exposed so a caller can hold one shared reference and address tensors by
/// offset, instead of copying each tensor's payload out and holding a second
/// copy of the weights.
pub fn GgufFile::buffer(self : GgufFile) -> Bytes {
self.buf
}
///|
/// Where a tensor's payload lives: an offset into the file buffer and its
/// length in bytes.
pub(all) struct TensorSpan {
/// Byte offset of the payload within the file buffer.
start : Int
/// Payload length in bytes.
byte_len : Int
} derive(Eq, Debug)
///|
/// Locate tensor `index`'s payload without copying it.
///
/// This is what a caller that keeps the file buffer alive should use; the
/// returned offsets address the same `Bytes` that `buffer()` exposes.
pub fn GgufFile::tensor_span(
self : GgufFile,
index : Int,
) -> Result[TensorSpan, GgufError] {
let t = self.tensors[index]
match t.nbytes {
None =>
Err(
TensorOutOfRange(
name=t.name,
offset=0L,
nbytes=-1L,
data_len=self.data_len(),
),
)
Some(nbytes) => {
let start = self.data_start + t.offset.reinterpret_as_int64()
Ok({ start: start.to_int(), byte_len: nbytes.to_int() })
}
}
}
///|
/// The raw payload bytes of tensor `index`, spanning `nbytes` from the data
/// section.
///
/// Returns an owned copy. Callers that keep the file buffer alive should prefer
/// `tensor_span`, which avoids duplicating the payload.
pub fn GgufFile::tensor_bytes(
self : GgufFile,
index : Int,
) -> Result[Bytes, GgufError] {
let t = self.tensors[index]
match t.nbytes {
None => Err(TensorOutOfRange(name=t.name, offset=0L, nbytes=-1L, data_len=self.data_len()))
Some(nbytes) => {
let start = self.data_start + t.offset.reinterpret_as_int64()
let view = self.buf.view(start=start.to_int(), end=(start + nbytes).to_int())
Ok(view.to_owned())
}
}
}
///|
/// Dequantize tensor `index` into a freshly allocated `Array[Float]`.
///
/// The buffer is sliced first so the tensor package sees a region it can
/// bounds-check on its own, and so a payload that sits past the buffer is
/// reported as a GGUF-level error rather than a generic buffer overrun.
pub fn GgufFile::dequant_tensor(
self : GgufFile,
index : Int,
) -> Result[Array[Float], GgufError] {
let t = self.tensors[index]
if !t.is_dequantizable() {
return Err(
TensorNotSupported(
name=t.name,
type_id=t.type_id,
type_name=t.ggml_type().name,
),
)
}
match self.tensor_bytes(index) {
Err(e) => Err(e)
Ok(payload) =>
match @tensor.dequant(payload, 0L, t.ggml_type(), t.n_elems) {
Ok(v) => Ok(v)
Err(e) => Err(DequantFailed(name=t.name, reason=e.message()))
}
}
}
///|
/// Product of the tensor dimensions as `Int64`, or `None` if the product does
/// not fit.
fn product_dims(dims : Array[UInt64]) -> Int64? {
let mut acc = 1L
for d in dims {
if d > 0x7FFFFFFFUL {
return None
}
let n = d.reinterpret_as_int64()
if n != 0L && acc > 0x7FFFFFFFFFFFFFFFL / n {
return None
}
acc = acc * n
}
Some(acc)
}
///|
/// Read `count` metadata key-value pairs.
///
/// `materialize~` forwards to array-valued entries: when true, arrays beyond
/// the usual summarize threshold are decoded in full. `parse` keeps the default
/// (summarize) so a plain `inspect` stays cheap; `parse_materializing_metadata`
/// opts in for callers that consume whole arrays, like the tokenizer.
fn read_metadata(
c : ByteCursor,
count : UInt64,
materialize~ : Bool = false,
) -> Result[Array[(String, MetaValue)], GgufError] {
let out : Array[(String, MetaValue)] = []
// Each pair needs at least a 8-byte key length, a 4-byte type and 1 value byte.
let min_bytes = count * 13UL
if min_bytes > c.remaining().reinterpret_as_uint64() {
return Err(CountOverflow(offset=c.pos, count=count, elem_size=13L))
}
let n = count.to_int()
for i = 0; i < n; i = i + 1 {
let key = match c.read_string() {
Ok(pair) => pair.0
Err(e) => return Err(e)
}
let type_id = match c.read_u32() {
Ok(t) => t
Err(e) => return Err(e)
}
let value = match read_meta_value(c, type_id, materialize~) {
Ok(v) => v
Err(e) => return Err(e)
}
out.push((key, value))
}
Ok(out)
}
///|
/// Read `count` tensor directory entries.
fn read_tensor_infos(
c : ByteCursor,
count : UInt64,
) -> Result[Array[TensorInfo], GgufError] {
let out : Array[TensorInfo] = []
// Smallest possible entry: 8-byte name length, 4-byte n_dims (0),
// 4-byte type, 8-byte offset.
let min_bytes = count * 24UL
if min_bytes > c.remaining().reinterpret_as_uint64() {
return Err(TensorCountImplausible(count=count, remaining=c.remaining()))
}
let n = count.to_int()
for i = 0; i < n; i = i + 1 {
let info_start = c.pos
let name = match c.read_string() {
Ok(pair) => pair.0
Err(e) => return Err(e)
}
let n_dims = match c.read_u32() {
Ok(d) => d
Err(e) => return Err(e)
}
if n_dims > gguf_max_dims.reinterpret_as_uint() {
return Err(TooManyDims(offset=info_start, n_dims=n_dims, max_dims=gguf_max_dims))
}
let dims : Array[UInt64] = []
for i = 0; i < n_dims.reinterpret_as_int(); i = i + 1 {
match c.read_u64() {
Ok(d) => dims.push(d)
Err(e) => return Err(e)
}
}
let type_id = match c.read_u32() {
Ok(t) => t
Err(e) => return Err(e)
}
let offset = match c.read_u64() {
Ok(o) => o
Err(e) => return Err(e)
}
let n_elems = match product_dims(dims) {
Some(v) => v
None => return Err(CountTooLarge(offset=info_start, count=offset))
}
// A tensor whose type this build cannot size stays in the directory with
// `nbytes = None`; it is still valid input, just not dequantizable.
let ty = @tensor.GgmlType::from_id(type_id.reinterpret_as_int())
let nbytes = if ty.is_known() {
match ty.nbytes(n_elems) {
Ok(v) => Some(v)
Err(_) => None
}
} else {
None
}
out.push({ name, dims, type_id, offset, n_elems, nbytes })
}
Ok(out)
}
///|
/// Round `pos` up to the next multiple of `alignment`.
fn align_up(pos : Int64, alignment : UInt) -> Int64 {
let a = alignment.reinterpret_as_int().to_int64()
if a <= 1L {
pos
} else {
(pos + a - 1L) / a * a
}
}
///|
/// Parse a GGUF container from an in-memory buffer.
///
/// Accepts GGUF v2 and v3; v1 and anything newer are refused. Every failure
/// mode in the format — bad magic, truncation, unknown metadata or tensor type
/// ids, counts that overflow, tensor payloads outside the buffer — produces a
/// `GgufError` carrying the file offset, and no input can cause a panic or an
/// out-of-bounds read.
///
/// Array-valued metadata beyond the summarize threshold is folded into a
/// bounded summary; use `parse_materializing_metadata` to keep the full arrays.
pub fn parse(buf : Bytes) -> Result[GgufFile, GgufError] {
parse_with(buf, materialize=false)
}
///|
/// Like `parse`, but materializes every array-valued metadata entry in full.
///
/// The tokenizer needs the complete `tokenizer.ggml.tokens` and
/// `tokenizer.ggml.merges` arrays (about 100k strings for SmolLM2), which the
/// default summary would truncate. Everything else — validation, tensor
/// directory, data section — behaves identically.
pub fn parse_materializing_metadata(
buf : Bytes,
) -> Result[GgufFile, GgufError] {
parse_with(buf, materialize=true)
}
///|
fn parse_with(buf : Bytes, materialize~ : Bool) -> Result[GgufFile, GgufError] {
let c = ByteCursor::new(buf)
let magic = match c.read_u32() {
Ok(m) => m
Err(_) => return Err(Truncated(offset=0L, needed=4L, remaining=c.len()))
}
if magic != gguf_magic {
return Err(BadMagic(found=magic))
}
let version = match c.read_u32() {
Ok(v) => v
Err(e) => return Err(e)
}
if version == 1U {
return Err(UnsupportedVersion(version=version, supported_max=gguf_version_max))
}
if version < 2U || version > gguf_version_max {
return Err(UnsupportedVersion(version=version, supported_max=gguf_version_max))
}
let tensor_count = match c.read_u64() {
Ok(n) => n
Err(e) => return Err(e)
}
let kv_count = match c.read_u64() {
Ok(n) => n
Err(e) => return Err(e)
}
let metadata = match read_metadata(c, kv_count, materialize~) {
Ok(m) => m
Err(e) => return Err(e)
}
// `general.alignment` must be read before the data offset can be computed,
// and it lives in the metadata that was just parsed.
let alignment = match find_alignment(metadata) {
Ok(a) => a
Err(e) => return Err(e)
}
let tensors = match read_tensor_infos(c, tensor_count) {
Ok(t) => t
Err(e) => return Err(e)
}
// llama.cpp only aligns the data offset when there is at least one tensor:
// a container with no tensors ends where its metadata ends, so padding it to
// the alignment boundary would point the data section past the file.
let data_start = if tensors.length() == 0 {
c.pos
} else {
align_up(c.pos, alignment)
}
if data_start > c.len() {
return Err(DataSectionOutOfRange(data_start=data_start, buffer_len=c.len()))
}
let file = { version, alignment, tensors, metadata, data_start, buf }
// Validate every tensor payload against the data section up front, so the
// CLI and the engine can index tensors without re-checking.
for t in file.tensors {
match t.nbytes {
None => ()
Some(nbytes) => {
let end = t.offset.reinterpret_as_int64() + nbytes
if end > file.data_len() {
return Err(
TensorOutOfRange(
name=t.name,
offset=t.offset.reinterpret_as_int64(),
nbytes=nbytes,
data_len=file.data_len(),
),
)
}
}
}
}
Ok(file)
}
///|
/// Resolve `general.alignment`: default 32, must be a positive power of two.
fn find_alignment(metadata : Array[(String, MetaValue)]) -> Result[UInt, GgufError] {
for entry in metadata {
if entry.0 == "general.alignment" {
match entry.1 {
U32(v) => {
let a = v.reinterpret_as_int()
if a <= 0 || (a & (a - 1)) != 0 {
return Err(BadAlignment(value=v))
}
return Ok(v)
}
other => return Err(AlignmentWrongType(type_id=other.type_id()))
}
}
}
Ok(gguf_default_alignment)
}