use std::fs;
use std::io::Read;
use std::path::Path;
pub const MAX_QUANTIZE_INDEX_LEN: u64 = 16 * 1024 * 1024;
#[derive(Debug)]
pub enum Q4ManifestError {
Unreadable(String),
TooLarge(String),
InvalidJson(String),
InvalidShape(String),
}
impl std::fmt::Display for Q4ManifestError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Q4ManifestError::Unreadable(s)
| Q4ManifestError::TooLarge(s)
| Q4ManifestError::InvalidJson(s)
| Q4ManifestError::InvalidShape(s) => write!(f, "{s}"),
}
}
}
impl std::error::Error for Q4ManifestError {}
impl From<Q4ManifestError> for String {
fn from(e: Q4ManifestError) -> String {
e.to_string()
}
}
#[derive(Debug, Clone, serde::Deserialize)]
pub struct Q4ManifestEntry {
pub name: String,
pub file: String,
#[serde(default)]
pub quantized: Option<bool>,
#[serde(default)]
pub shape: Option<Vec<usize>>,
#[serde(default)]
pub numel: Option<usize>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ManifestFlavor {
QuantizeQ4,
QuaRot,
}
#[derive(Debug, Clone)]
pub struct Q4Manifest {
pub flavor: ManifestFlavor,
pub quarot_seed: Option<u64>,
pub tensors: Vec<Q4ManifestEntry>,
}
pub fn read_manifest_bytes_bounded(path: &Path) -> Result<Option<Vec<u8>>, Q4ManifestError> {
match fs::symlink_metadata(path) {
Ok(_) => {}
Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(None),
Err(e) => {
return Err(Q4ManifestError::Unreadable(format!(
"{}: failed to stat quantize_index.json: {e}",
path.display()
)));
}
}
let metadata = fs::metadata(path).map_err(|e| {
Q4ManifestError::Unreadable(format!(
"{}: quantize_index.json entry exists but is unreadable \
(broken symlink or permission error): {e}",
path.display()
))
})?;
if metadata.len() > MAX_QUANTIZE_INDEX_LEN {
return Err(Q4ManifestError::TooLarge(format!(
"{}: quantize_index.json too large: {} bytes exceeds cap of {MAX_QUANTIZE_INDEX_LEN} bytes",
path.display(),
metadata.len()
)));
}
let file = fs::File::open(path).map_err(|e| {
Q4ManifestError::Unreadable(format!(
"{}: failed to open quantize_index.json: {e}",
path.display()
))
})?;
let mut buf = Vec::new();
file.take(MAX_QUANTIZE_INDEX_LEN.saturating_add(1))
.read_to_end(&mut buf)
.map_err(|e| {
Q4ManifestError::Unreadable(format!(
"{}: failed to read quantize_index.json: {e}",
path.display()
))
})?;
if buf.len() as u64 > MAX_QUANTIZE_INDEX_LEN {
return Err(Q4ManifestError::TooLarge(format!(
"{}: quantize_index.json too large: read exceeds cap of {MAX_QUANTIZE_INDEX_LEN} bytes",
path.display()
)));
}
Ok(Some(buf))
}
pub fn parse_manifest(bytes: &[u8], path: &Path) -> Result<Q4Manifest, Q4ManifestError> {
let value: serde_json::Value = serde_json::from_slice(bytes).map_err(|e| {
Q4ManifestError::InvalidJson(format!("{} is not valid JSON: {e}", path.display()))
})?;
match value {
serde_json::Value::Array(_) => serde_json::from_value::<Vec<Q4ManifestEntry>>(value)
.map(|tensors| Q4Manifest {
flavor: ManifestFlavor::QuantizeQ4,
quarot_seed: None,
tensors,
})
.map_err(|e| {
Q4ManifestError::InvalidShape(format!(
"{}: invalid bare-array (quantize_q4) manifest: {e}",
path.display()
))
}),
serde_json::Value::Object(_) => {
#[derive(serde::Deserialize)]
struct WrappedManifest {
#[serde(default)]
quarot_seed: Option<u64>,
tensors: Vec<Q4ManifestEntry>,
}
serde_json::from_value::<WrappedManifest>(value)
.map(|w| Q4Manifest {
flavor: ManifestFlavor::QuaRot,
quarot_seed: w.quarot_seed,
tensors: w.tensors,
})
.map_err(|e| {
Q4ManifestError::InvalidShape(format!(
"{}: invalid object-form (quantize_quarot) manifest: {e}",
path.display()
))
})
}
_ => Err(Q4ManifestError::InvalidShape(format!(
"{}: expected quantize_index.json to be either a bare array of tensor \
entries (quantize_q4) or an object with a \"tensors\" array \
(quantize_quarot)",
path.display()
))),
}
}
pub fn load_manifest(dir: &Path) -> Result<Option<Q4Manifest>, Q4ManifestError> {
let path = dir.join("quantize_index.json");
let Some(bytes) = read_manifest_bytes_bounded(&path)? else {
return Ok(None);
};
parse_manifest(&bytes, &path).map(Some)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn load_manifest_absent_file_is_none() {
let tmp = tempfile::tempdir().unwrap();
assert!(load_manifest(tmp.path()).unwrap().is_none());
}
#[test]
fn load_manifest_bare_array_normalizes_to_quantize_q4_flavor() {
let tmp = tempfile::tempdir().unwrap();
fs::write(
tmp.path().join("quantize_index.json"),
r#"[{"name":"foo","file":"foo.q4","quantized":true,"shape":[2,2],"numel":4}]"#,
)
.unwrap();
let manifest = load_manifest(tmp.path())
.unwrap()
.expect("manifest must load");
assert_eq!(manifest.flavor, ManifestFlavor::QuantizeQ4);
assert_eq!(manifest.quarot_seed, None);
assert_eq!(manifest.tensors.len(), 1);
assert_eq!(manifest.tensors[0].name, "foo");
}
#[test]
fn load_manifest_object_form_normalizes_to_quarot_flavor_with_seed() {
let tmp = tempfile::tempdir().unwrap();
fs::write(
tmp.path().join("quantize_index.json"),
r#"{"quarot_seed":42,"tensors":[{"name":"foo","file":"foo.q4","quantized":true,"shape":[2],"numel":2}]}"#,
)
.unwrap();
let manifest = load_manifest(tmp.path())
.unwrap()
.expect("manifest must load");
assert_eq!(manifest.flavor, ManifestFlavor::QuaRot);
assert_eq!(manifest.quarot_seed, Some(42));
assert_eq!(manifest.tensors.len(), 1);
}
#[test]
fn load_manifest_object_form_without_seed_key_is_none() {
let tmp = tempfile::tempdir().unwrap();
fs::write(tmp.path().join("quantize_index.json"), r#"{"tensors":[]}"#).unwrap();
let manifest = load_manifest(tmp.path())
.unwrap()
.expect("manifest must load");
assert_eq!(manifest.quarot_seed, None);
assert!(manifest.tensors.is_empty());
}
#[test]
fn load_manifest_tolerates_entries_without_quantized_shape_numel() {
let bare = tempfile::tempdir().unwrap();
fs::write(
bare.path().join("quantize_index.json"),
r#"[{"name":"foo","file":"foo.q4"}]"#,
)
.unwrap();
let bare_manifest = load_manifest(bare.path())
.unwrap()
.expect("minimal bare-array entry must load for doctor's inventory");
assert_eq!(bare_manifest.tensors[0].quantized, None);
let object = tempfile::tempdir().unwrap();
fs::write(
object.path().join("quantize_index.json"),
r#"{"quarot_seed":42,"tensors":[{"name":"foo","file":"foo.q4"}]}"#,
)
.unwrap();
let object_manifest = load_manifest(object.path())
.unwrap()
.expect("minimal object-form entry must load for doctor's inventory");
assert_eq!(object_manifest.tensors[0].shape, None);
assert_eq!(object_manifest.tensors[0].numel, None);
}
#[test]
fn parse_manifest_malformed_bare_array_error_names_the_missing_field() {
let err = parse_manifest(br#"[{"name": "x"}]"#, Path::new("quantize_index.json"))
.expect_err("bare-array entry missing `file` must fail");
assert!(
matches!(err, Q4ManifestError::InvalidShape(_)),
"wrong error variant: {err:?}"
);
let msg = err.to_string();
assert!(
msg.contains("file"),
"error must name the missing `file` field; got: {msg}"
);
assert!(
!msg.contains("did not match any variant"),
"error must not be the generic untagged-enum fallthrough; got: {msg}"
);
}
#[test]
fn parse_manifest_malformed_object_tensors_error_names_tensors() {
let err = parse_manifest(
br#"{"quarot_seed": 42, "tensors": "not-an-array"}"#,
Path::new("quantize_index.json"),
)
.expect_err("object-form manifest with non-array tensors must fail");
assert!(
matches!(err, Q4ManifestError::InvalidShape(_)),
"wrong error variant: {err:?}"
);
let msg = err.to_string();
assert!(
msg.contains("tensors") || msg.contains("sequence"),
"error must point at the bad `tensors` value; got: {msg}"
);
assert!(
!msg.contains("did not match any variant"),
"error must not be the generic untagged-enum fallthrough; got: {msg}"
);
}
#[test]
fn parse_manifest_non_array_non_object_root_is_rejected_with_shape_hint() {
let err = parse_manifest(br#""just a string""#, Path::new("quantize_index.json"))
.expect_err("scalar manifest root must fail");
assert!(
matches!(err, Q4ManifestError::InvalidShape(_)),
"wrong error variant: {err:?}"
);
let msg = err.to_string();
assert!(
msg.contains("either a bare array") && msg.contains("tensors"),
"error must explain both accepted shapes; got: {msg}"
);
}
#[test]
fn read_manifest_bytes_bounded_rejects_oversized_file() {
let tmp = tempfile::tempdir().unwrap();
let path = tmp.path().join("quantize_index.json");
let f = fs::File::create(&path).unwrap();
f.set_len(MAX_QUANTIZE_INDEX_LEN + 1).unwrap();
drop(f);
let err = read_manifest_bytes_bounded(&path)
.expect_err("oversized quantize_index.json must be rejected");
assert!(
matches!(err, Q4ManifestError::TooLarge(_)),
"wrong error variant: {err:?}"
);
assert!(
err.to_string().contains("too large"),
"error must name the size-cap failure; got: {err}"
);
}
#[test]
fn read_manifest_bytes_bounded_rejects_truncated_json_via_parse_manifest() {
let tmp = tempfile::tempdir().unwrap();
fs::write(
tmp.path().join("quantize_index.json"),
r#"{"quarot_seed":42,"tensors":[{"name":"foo","file":"foo.q4","quant"#,
)
.unwrap();
let err =
load_manifest(tmp.path()).expect_err("truncated quantize_index.json must be rejected");
assert!(
matches!(err, Q4ManifestError::InvalidJson(_)),
"wrong error variant: {err:?}"
);
}
#[test]
#[cfg(unix)]
fn read_manifest_bytes_bounded_rejects_dangling_symlink() {
use std::os::unix::fs::symlink;
let tmp = tempfile::tempdir().unwrap();
let link_path = tmp.path().join("quantize_index.json");
symlink(tmp.path().join("does-not-exist"), &link_path).unwrap();
let err = read_manifest_bytes_bounded(&link_path).expect_err(
"a quantize_index.json symlink to a missing target must fail closed, not Ok(None)",
);
assert!(
matches!(err, Q4ManifestError::Unreadable(_)),
"wrong error variant: {err:?}"
);
}
#[test]
#[cfg(unix)]
fn load_manifest_rejects_dangling_symlink() {
use std::os::unix::fs::symlink;
let tmp = tempfile::tempdir().unwrap();
let link_path = tmp.path().join("quantize_index.json");
symlink(tmp.path().join("does-not-exist"), &link_path).unwrap();
let err = load_manifest(tmp.path())
.expect_err("a dangling quantize_index.json symlink must not be treated as absent");
assert!(matches!(err, Q4ManifestError::Unreadable(_)));
}
}