use std::collections::HashMap;
pub const GGUF_MAGIC: u32 = 0x4655_4747;
pub const GGUF_VERSION_V2: u32 = 2;
pub const GGUF_VERSION_V3: u32 = 3;
pub const GGUF_TYPE_F32: u32 = 0;
pub const GGUF_TYPE_F16: u32 = 1;
pub const GGUF_TYPE_Q4_0: u32 = 2;
pub const GGUF_TYPE_Q4_1: u32 = 3;
pub const GGUF_TYPE_Q5_0: u32 = 6;
pub const GGUF_TYPE_Q5_1: u32 = 7;
pub const GGUF_TYPE_Q8_0: u32 = 8;
pub const GGUF_TYPE_Q2_K: u32 = 10;
pub const GGUF_TYPE_Q3_K: u32 = 11;
pub const GGUF_TYPE_Q4_K: u32 = 12;
pub const GGUF_TYPE_Q5_K: u32 = 13;
pub const GGUF_TYPE_Q6_K: u32 = 14;
pub const GGUF_TYPE_BF16: u32 = 30;
pub const APR_TYPE_Q4: u32 = 128;
pub const APR_TYPE_Q8: u32 = 129;
pub use trueno_quant::GgmlType;
pub use trueno_quant::GgmlType as GgmlQuantType;
pub const ADMITTED: [GgmlQuantType; 16] = [
GgmlQuantType::F32,
GgmlQuantType::F16,
GgmlQuantType::Q4_0,
GgmlQuantType::Q4_1,
GgmlQuantType::Q5_0,
GgmlQuantType::Q5_1,
GgmlQuantType::Q8_0,
GgmlQuantType::Q8_1,
GgmlQuantType::Q2K,
GgmlQuantType::Q3K,
GgmlQuantType::Q4K,
GgmlQuantType::Q5K,
GgmlQuantType::Q6K,
GgmlQuantType::IQ2XXS,
GgmlQuantType::IQ2XS,
GgmlQuantType::BF16,
];
#[must_use]
pub const fn is_admitted(t: GgmlQuantType) -> bool {
let mut i = 0;
while i < ADMITTED.len() {
if ADMITTED[i] as u32 == t as u32 {
return true;
}
i += 1;
}
false
}
#[must_use]
pub const fn admitted_from_id(id: u32) -> Option<GgmlQuantType> {
match GgmlQuantType::from_id(id) {
Some(t) if is_admitted(t) => Some(t),
_ => None,
}
}
#[must_use]
pub fn admitted_from_name(name: &str) -> Option<GgmlQuantType> {
GgmlQuantType::from_str_lossy(name).filter(|t| is_admitted(*t))
}
pub const TOKEN_BUFFER_INLINE_CAP: usize = 32;
pub const ATTENTION_BUFFER_INLINE_CAP: usize = 64;
pub const HIDDEN_BUFFER_INLINE_CAP: usize = 128;
pub const BUFFER_LW_SIZE: usize = 1024;
pub const BUFFER_HW_SIZE: usize = 8 * 1024;
pub const BUFFER_MAX_SIZE: usize = 32 * 1024;
pub type TokenBuffer = smallvec::SmallVec<[u32; TOKEN_BUFFER_INLINE_CAP]>;
pub type AttentionBuffer = smallvec::SmallVec<[f32; ATTENTION_BUFFER_INLINE_CAP]>;
pub type HiddenBuffer = smallvec::SmallVec<[f32; HIDDEN_BUFFER_INLINE_CAP]>;
pub const GGUF_ALIGNMENT: usize = 32;
#[derive(Debug, Clone, PartialEq)]
pub enum GGUFValue {
UInt8(u8),
Int8(i8),
UInt16(u16),
Int16(i16),
UInt32(u32),
Int32(i32),
Float32(f32),
Bool(bool),
String(String),
Array(Vec<GGUFValue>),
UInt64(u64),
Int64(i64),
Float64(f64),
}
#[derive(Debug, Clone, PartialEq)]
pub struct GGUFHeader {
pub magic: u32,
pub version: u32,
pub tensor_count: u64,
pub metadata_count: u64,
}
#[derive(Debug, Clone, PartialEq)]
pub struct TensorInfo {
pub name: String,
pub n_dims: u32,
pub dims: Vec<u64>,
pub qtype: u32,
pub offset: u64,
}
#[derive(Debug, Clone)]
pub struct GGUFModel {
pub header: GGUFHeader,
pub metadata: HashMap<String, GGUFValue>,
pub tensors: Vec<TensorInfo>,
pub tensor_data_start: usize,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_magic_constant() {
assert_eq!(GGUF_MAGIC, 0x4655_4747);
}
#[test]
fn test_quantization_constants() {
assert_eq!(GGUF_TYPE_F32, 0);
assert_eq!(GGUF_TYPE_F16, 1);
assert_eq!(GGUF_TYPE_Q4_0, 2);
assert_eq!(GGUF_TYPE_Q8_0, 8);
assert_eq!(GGUF_TYPE_Q4_K, 12);
assert_eq!(GGUF_TYPE_Q6_K, 14);
}
#[test]
fn test_buffer_constants() {
assert_eq!(TOKEN_BUFFER_INLINE_CAP, 32);
assert_eq!(ATTENTION_BUFFER_INLINE_CAP, 64);
assert_eq!(HIDDEN_BUFFER_INLINE_CAP, 128);
}
#[test]
fn test_buffer_watermarks() {
assert_eq!(BUFFER_LW_SIZE, 1024);
assert_eq!(BUFFER_HW_SIZE, 8 * 1024);
assert_eq!(BUFFER_MAX_SIZE, 32 * 1024);
}
#[test]
fn test_version_constant() {
assert_eq!(GGUF_VERSION_V3, 3);
}
#[test]
fn test_gguf_value_variants() {
let uint8 = GGUFValue::UInt8(255);
let string = GGUFValue::String("test".to_string());
let array = GGUFValue::Array(vec![GGUFValue::UInt32(1), GGUFValue::UInt32(2)]);
assert_eq!(uint8, GGUFValue::UInt8(255));
assert_eq!(string, GGUFValue::String("test".to_string()));
assert!(matches!(array, GGUFValue::Array(_)));
}
#[test]
fn test_gguf_header() {
let header = GGUFHeader {
magic: GGUF_MAGIC,
version: GGUF_VERSION_V3,
tensor_count: 100,
metadata_count: 50,
};
assert_eq!(header.magic, 0x4655_4747);
assert_eq!(header.version, 3);
assert_eq!(header.tensor_count, 100);
assert_eq!(header.metadata_count, 50);
}
#[test]
fn test_tensor_info() {
let info = TensorInfo {
name: "model.layers.0.attn.wq".to_string(),
n_dims: 2,
dims: vec![4096, 4096],
qtype: GGUF_TYPE_Q4_K,
offset: 1024,
};
assert_eq!(info.name, "model.layers.0.attn.wq");
assert_eq!(info.n_dims, 2);
assert_eq!(info.dims, vec![4096, 4096]);
assert_eq!(info.qtype, GGUF_TYPE_Q4_K);
assert_eq!(info.offset, 1024);
}
#[test]
fn test_gguf_model() {
let model = GGUFModel {
header: GGUFHeader {
magic: GGUF_MAGIC,
version: GGUF_VERSION_V3,
tensor_count: 1,
metadata_count: 0,
},
metadata: HashMap::new(),
tensors: vec![],
tensor_data_start: 128,
};
assert_eq!(model.header.magic, GGUF_MAGIC);
assert!(model.tensors.is_empty());
assert_eq!(model.tensor_data_start, 128);
}
#[test]
fn test_alignment_constant() {
assert_eq!(GGUF_ALIGNMENT, 32);
assert_eq!(GGUF_ALIGNMENT & (GGUF_ALIGNMENT - 1), 0);
}
}