1use std::{collections::BTreeMap, fmt};
51
52use ftts_kernels::mmap::{MappedFile, MemoryAdvice, MemoryAdviceOutcome, MemoryResidency};
53use serde_json::{Value, json};
54
55use crate::sha256::{Sha256, hex_digest, to_hex};
56
57pub const MAGIC: &[u8; 8] = b"FTTSQ\0\0\0";
59
60pub const FORMAT_VERSION: u32 = 1;
66
67pub const HEADER_PREFIX_BYTES: u64 = 20;
69
70pub const MAX_DIRECTORY_BYTES: u64 = 64 * 1024 * 1024;
75
76pub const MAX_SECTIONS: usize = 64;
78
79pub const MAX_TENSORS: usize = 16_384;
81
82pub const MAX_RANK: usize = 8;
84
85pub const MAX_DIM: u64 = 1 << 32;
87
88#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
94pub enum AccessClass {
95 HotRecurrentMicrodecoder,
97 HotRecurrentTalker,
99 HotCodecDecoder,
101 ColdTextEmbedding,
103 EnrollmentSpeakerEncoder,
105 EnrollmentCodecEncoder,
107 Metadata,
109}
110
111impl AccessClass {
112 #[must_use]
114 pub const fn as_str(self) -> &'static str {
115 match self {
116 Self::HotRecurrentMicrodecoder => "HOT_RECURRENT_MICRODECODER",
117 Self::HotRecurrentTalker => "HOT_RECURRENT_TALKER",
118 Self::HotCodecDecoder => "HOT_CODEC_DECODER",
119 Self::ColdTextEmbedding => "COLD_TEXT_EMBEDDING",
120 Self::EnrollmentSpeakerEncoder => "ENROLLMENT_SPEAKER_ENCODER",
121 Self::EnrollmentCodecEncoder => "ENROLLMENT_CODEC_ENCODER",
122 Self::Metadata => "METADATA",
123 }
124 }
125
126 #[must_use]
128 pub fn parse(text: &str) -> Option<Self> {
129 Some(match text {
130 "HOT_RECURRENT_MICRODECODER" => Self::HotRecurrentMicrodecoder,
131 "HOT_RECURRENT_TALKER" => Self::HotRecurrentTalker,
132 "HOT_CODEC_DECODER" => Self::HotCodecDecoder,
133 "COLD_TEXT_EMBEDDING" => Self::ColdTextEmbedding,
134 "ENROLLMENT_SPEAKER_ENCODER" => Self::EnrollmentSpeakerEncoder,
135 "ENROLLMENT_CODEC_ENCODER" => Self::EnrollmentCodecEncoder,
136 "METADATA" => Self::Metadata,
137 _ => return None,
138 })
139 }
140
141 #[must_use]
146 pub const fn is_hot(self) -> bool {
147 matches!(
148 self,
149 Self::HotRecurrentMicrodecoder | Self::HotRecurrentTalker | Self::HotCodecDecoder
150 )
151 }
152
153 #[must_use]
155 pub const fn is_row_granular(self) -> bool {
156 matches!(self, Self::ColdTextEmbedding)
157 }
158}
159
160impl fmt::Display for AccessClass {
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 f.write_str(self.as_str())
163 }
164}
165
166#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
174pub enum PagePolicy {
175 Resident,
180 LazyRowGranular,
186 OnDemand,
188}
189
190impl PagePolicy {
191 #[must_use]
193 pub const fn as_str(self) -> &'static str {
194 match self {
195 Self::Resident => "resident",
196 Self::LazyRowGranular => "lazy_row_granular",
197 Self::OnDemand => "on_demand",
198 }
199 }
200
201 #[must_use]
206 pub const fn may_prefetch(self) -> bool {
207 matches!(self, Self::Resident)
208 }
209}
210
211impl fmt::Display for PagePolicy {
212 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213 f.write_str(self.as_str())
214 }
215}
216
217impl AccessClass {
218 #[must_use]
220 pub const fn page_policy(self) -> PagePolicy {
221 match self {
222 Self::HotRecurrentMicrodecoder | Self::HotRecurrentTalker | Self::HotCodecDecoder => {
223 PagePolicy::Resident
224 }
225 Self::ColdTextEmbedding => PagePolicy::LazyRowGranular,
226 Self::EnrollmentSpeakerEncoder | Self::EnrollmentCodecEncoder | Self::Metadata => {
229 PagePolicy::OnDemand
230 }
231 }
232 }
233}
234
235#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
241pub enum StoredDtype {
242 Bf16,
244 F32,
246 Q8,
248 Q4,
250}
251
252impl StoredDtype {
253 #[must_use]
255 pub const fn as_str(self) -> &'static str {
256 match self {
257 Self::Bf16 => "bf16",
258 Self::F32 => "f32",
259 Self::Q8 => "q8",
260 Self::Q4 => "q4",
261 }
262 }
263
264 #[must_use]
266 pub fn parse(text: &str) -> Option<Self> {
267 Some(match text {
268 "bf16" => Self::Bf16,
269 "f32" => Self::F32,
270 "q8" => Self::Q8,
271 "q4" => Self::Q4,
272 _ => return None,
273 })
274 }
275
276 #[must_use]
281 pub const fn storage_bytes(self, elements: u64) -> Option<u64> {
282 match self {
283 Self::Bf16 => elements.checked_mul(2),
284 Self::F32 => elements.checked_mul(4),
285 Self::Q8 => Some(elements),
286 Self::Q4 => match elements.checked_add(1) {
287 Some(padded) => Some(padded / 2),
288 None => None,
289 },
290 }
291 }
292}
293
294impl fmt::Display for StoredDtype {
295 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
296 f.write_str(self.as_str())
297 }
298}
299
300#[derive(Clone, Debug, PartialEq, Eq)]
302pub struct SectionEntry {
303 pub name: String,
305 pub access_class: AccessClass,
307 pub offset: u64,
309 pub length: u64,
311 pub sha256: String,
313}
314
315impl SectionEntry {
316 #[must_use]
318 pub const fn end(&self) -> Option<u64> {
319 self.offset.checked_add(self.length)
320 }
321}
322
323#[derive(Clone, Debug, PartialEq, Eq)]
325pub struct TensorEntry {
326 pub name: String,
328 pub section: String,
330 pub dtype: StoredDtype,
332 pub shape: Vec<u64>,
334 pub offset: u64,
336 pub length: u64,
338 pub scales: Option<String>,
340}
341
342impl TensorEntry {
343 #[must_use]
345 pub fn elements(&self) -> Option<u64> {
346 self.shape
347 .iter()
348 .try_fold(1_u64, |acc, &d| acc.checked_mul(d))
349 }
350}
351
352#[derive(Clone, Debug, PartialEq, Eq)]
357pub enum FttsqError {
358 TooShort {
360 length: u64,
362 },
363 BadMagic {
365 found: [u8; 8],
367 },
368 UnsupportedVersion {
370 found: u32,
372 supported: u32,
374 },
375 DirectoryLength {
377 declared: u64,
379 limit: u64,
381 },
382 DirectoryMalformed {
384 detail: String,
386 },
387 Field {
389 path: String,
391 expected: String,
393 },
394 UnknownValue {
396 path: String,
398 found: String,
400 },
401 LimitExceeded {
403 what: String,
405 found: u64,
407 limit: u64,
409 },
410 RangeOutOfBounds {
412 what: String,
414 offset: u64,
416 length: u64,
418 bound: u64,
420 },
421 SectionOverlap {
423 first: String,
425 second: String,
427 },
428 TensorOverlap {
430 first: String,
432 second: String,
434 },
435 DuplicateName {
437 what: String,
439 name: String,
441 },
442 UnknownSection {
444 tensor: String,
446 section: String,
448 },
449 LengthMismatch {
451 tensor: String,
453 declared: u64,
455 implied: u64,
457 },
458 DigestMismatch {
460 section: String,
462 expected: String,
464 actual: String,
466 },
467 LicenseNoticeMissing,
472 SectionWriteOutOfOrder {
474 expected: Option<String>,
476 actual: String,
478 },
479 SectionLengthExceeded {
481 section: String,
483 declared: u64,
485 attempted: u64,
487 },
488 SectionIncomplete {
490 section: String,
492 declared: u64,
494 written: u64,
496 },
497 Io {
502 operation: String,
504 path: String,
506 detail: String,
508 },
509}
510
511impl fmt::Display for FttsqError {
512 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
513 match self {
514 Self::TooShort { length } => write!(
515 f,
516 "not a .fttsq artifact: {length} bytes is shorter than the {HEADER_PREFIX_BYTES}-byte header"
517 ),
518 Self::BadMagic { found } => {
519 write!(f, "not a .fttsq artifact: magic {found:?} is not {MAGIC:?}")
520 }
521 Self::UnsupportedVersion { found, supported } => write!(
522 f,
523 "artifact format version {found} is newer than this binary supports ({supported}); \
524 upgrade ftts rather than reading it with a stale layout"
525 ),
526 Self::DirectoryLength { declared, limit } => {
527 write!(f, "directory length {declared} exceeds {limit}")
528 }
529 Self::DirectoryMalformed { detail } => write!(f, "directory is malformed: {detail}"),
530 Self::Field { path, expected } => {
531 write!(f, "directory field `{path}` is missing or not {expected}")
532 }
533 Self::UnknownValue { path, found } => write!(
534 f,
535 "directory field `{path}` has unknown value `{found}`; this artifact needs a newer ftts"
536 ),
537 Self::LimitExceeded { what, found, limit } => {
538 write!(f, "{what} count {found} exceeds the cap of {limit}")
539 }
540 Self::RangeOutOfBounds {
541 what,
542 offset,
543 length,
544 bound,
545 } => write!(
546 f,
547 "{what} range [{offset}, {offset}+{length}) runs past its bound {bound}"
548 ),
549 Self::SectionOverlap { first, second } => write!(
550 f,
551 "sections `{first}` and `{second}` claim overlapping bytes"
552 ),
553 Self::TensorOverlap { first, second } => write!(
554 f,
555 "tensors `{first}` and `{second}` claim overlapping bytes"
556 ),
557 Self::DuplicateName { what, name } => write!(f, "{what} `{name}` is declared twice"),
558 Self::UnknownSection { tensor, section } => write!(
559 f,
560 "tensor `{tensor}` names section `{section}`, which is not declared"
561 ),
562 Self::LengthMismatch {
563 tensor,
564 declared,
565 implied,
566 } => write!(
567 f,
568 "tensor `{tensor}` declares {declared} bytes but its shape and dtype imply {implied}"
569 ),
570 Self::DigestMismatch {
571 section,
572 expected,
573 actual,
574 } => write!(
575 f,
576 "section `{section}` is corrupt: recorded sha256 {expected}, computed {actual}"
577 ),
578 Self::LicenseNoticeMissing => f.write_str(
579 "artifact carries no license_notice; Apache-2.0 §4 requires it on every published \
580 artifact, so an artifact without one is refused rather than silently accepted",
581 ),
582 Self::SectionWriteOutOfOrder { expected, actual } => match expected {
583 Some(expected) => write!(
584 f,
585 "streaming .fttsq writer expected section `{expected}`, not `{actual}`"
586 ),
587 None => write!(
588 f,
589 "streaming .fttsq writer is complete and cannot accept section `{actual}`"
590 ),
591 },
592 Self::SectionLengthExceeded {
593 section,
594 declared,
595 attempted,
596 } => write!(
597 f,
598 "section `{section}` declares {declared} bytes but streaming write would reach {attempted}"
599 ),
600 Self::SectionIncomplete {
601 section,
602 declared,
603 written,
604 } => write!(
605 f,
606 "section `{section}` declares {declared} bytes but only {written} were written"
607 ),
608 Self::Io {
609 operation,
610 path,
611 detail,
612 } => write!(f, "{operation} failed for `{path}`: {detail}"),
613 }
614 }
615}
616
617impl std::error::Error for FttsqError {}
618
619#[derive(Clone, Debug)]
621pub struct FttsqReader {
622 format_version: u32,
623 model_family: String,
624 source_sha256: String,
625 license_notice: String,
626 model_config: Value,
627 quantization_manifest: Value,
628 sections: Vec<SectionEntry>,
629 tensors: Vec<TensorEntry>,
630 section_index: BTreeMap<String, usize>,
631 tensor_index: BTreeMap<String, usize>,
632}
633
634#[derive(Clone, Debug, PartialEq, Eq)]
640pub enum PageAdviceOutcome {
641 NotRequested,
643 Applied,
645 SkippedEmpty,
647 Unsupported,
649 Failed(String),
651}
652
653#[derive(Clone, Debug, PartialEq, Eq)]
659pub enum PageResidencyOutcome {
660 Measured {
662 resident_pages: usize,
664 total_pages: usize,
666 },
667 Unsupported,
669 Failed(String),
671}
672
673#[derive(Clone, Debug, PartialEq, Eq)]
675pub struct PageAdviceApplication {
676 pub section: String,
678 pub policy: PagePolicy,
680 pub requested: Option<MemoryAdvice>,
682 pub residency_before: PageResidencyOutcome,
684 pub outcome: PageAdviceOutcome,
686 pub residency_after: PageResidencyOutcome,
688}
689
690#[derive(Debug)]
698pub struct MappedFttsq {
699 mapping: MappedFile,
700 reader: FttsqReader,
701 page_advice: Vec<PageAdviceApplication>,
702 absent_sections: Vec<String>,
710}
711
712impl MappedFttsq {
713 pub fn open(path: impl AsRef<std::path::Path>) -> Result<Self, FttsqError> {
725 let path = path.as_ref();
726 let mapping = MappedFile::open(path).map_err(|error| FttsqError::Io {
727 operation: "memory-map artifact".to_owned(),
728 path: path.display().to_string(),
729 detail: error.to_string(),
730 })?;
731 let reader = FttsqReader::parse_directory(mapping.as_slice())?;
734 let page_advice = apply_page_in_plan(&mapping, &reader);
735 reader.verify_digests(mapping.as_slice())?;
736 Ok(Self {
737 mapping,
738 reader,
739 page_advice,
740 absent_sections: Vec::new(),
741 })
742 }
743
744 #[cfg(not(unix))] pub fn from_bytes(bytes: Vec<u8>) -> Result<Self, FttsqError> {
755 let mapping = MappedFile::from_bytes(bytes);
756 let reader = FttsqReader::parse_directory(mapping.as_slice())?;
757 let page_advice = apply_page_in_plan(&mapping, &reader);
758 reader.verify_digests(mapping.as_slice())?;
759 Ok(Self {
760 mapping,
761 reader,
762 page_advice,
763 absent_sections: Vec::new(),
764 })
765 }
766
767 #[cfg(not(unix))] pub fn from_prefix_bytes(bytes: Vec<u8>, file_len: u64) -> Result<Self, FttsqError> {
802 let mapping = MappedFile::from_bytes(bytes);
803 let reader = FttsqReader::parse_directory_of_prefix(mapping.as_slice(), file_len)?;
806 let available = mapping.as_slice().len() as u64;
807
808 let absent_sections = reader.absent_sections_in_prefix(available)?;
809 let page_advice = apply_page_in_plan(&mapping, &reader);
810 reader.verify_digests_of_present(mapping.as_slice())?;
811 Ok(Self {
812 mapping,
813 reader,
814 page_advice,
815 absent_sections,
816 })
817 }
818
819 #[must_use]
821 pub fn absent_sections(&self) -> &[String] {
822 &self.absent_sections
823 }
824
825 #[must_use]
827 pub fn has_section(&self, name: &str) -> bool {
828 self.reader.section(name).is_some() && !self.absent_sections.iter().any(|s| s == name)
829 }
830
831 #[must_use]
833 pub const fn reader(&self) -> &FttsqReader {
834 &self.reader
835 }
836
837 #[must_use]
839 pub fn page_advice(&self) -> &[PageAdviceApplication] {
840 &self.page_advice
841 }
842
843 pub fn tensor_bytes(&self, name: &str) -> Result<&[u8], FttsqError> {
849 self.reader.tensor_bytes(name, self.mapping.as_slice())
850 }
851
852 #[must_use]
854 pub fn len(&self) -> usize {
855 self.mapping.len()
856 }
857
858 #[must_use]
860 pub fn is_empty(&self) -> bool {
861 self.mapping.is_empty()
862 }
863}
864
865fn apply_page_in_plan(mapping: &MappedFile, reader: &FttsqReader) -> Vec<PageAdviceApplication> {
866 reader
867 .page_in_plan()
868 .into_iter()
869 .map(|(section, policy)| {
870 let requested = match policy {
871 PagePolicy::Resident => Some(MemoryAdvice::WillNeed),
872 PagePolicy::LazyRowGranular => Some(MemoryAdvice::Random),
873 PagePolicy::OnDemand => None,
874 };
875
876 assert!(
880 policy.may_prefetch() || requested != Some(MemoryAdvice::WillNeed),
881 "a non-prefetch policy must never issue MADV_WILLNEED"
882 );
883
884 let residency_before = observe_residency(mapping, section.offset, section.length);
885 let outcome = match requested {
886 Some(advice) => match mapping.advise(section.offset, section.length, advice) {
887 Ok(MemoryAdviceOutcome::Applied) => PageAdviceOutcome::Applied,
888 Ok(MemoryAdviceOutcome::SkippedEmpty) => PageAdviceOutcome::SkippedEmpty,
889 Ok(MemoryAdviceOutcome::Unsupported) => PageAdviceOutcome::Unsupported,
890 Err(error) => PageAdviceOutcome::Failed(error.to_string()),
891 },
892 None => PageAdviceOutcome::NotRequested,
893 };
894 let residency_after = observe_residency(mapping, section.offset, section.length);
895
896 PageAdviceApplication {
897 section: section.name.clone(),
898 policy,
899 requested,
900 residency_before,
901 outcome,
902 residency_after,
903 }
904 })
905 .collect()
906}
907
908fn observe_residency(mapping: &MappedFile, offset: u64, length: u64) -> PageResidencyOutcome {
909 match mapping.resident_pages(offset, length) {
910 Ok(MemoryResidency::Measured {
911 resident_pages,
912 total_pages,
913 }) => PageResidencyOutcome::Measured {
914 resident_pages,
915 total_pages,
916 },
917 Ok(MemoryResidency::Unsupported) => PageResidencyOutcome::Unsupported,
918 Err(error) => PageResidencyOutcome::Failed(error.to_string()),
919 }
920}
921
922impl FttsqReader {
923 pub fn open(bytes: &[u8]) -> Result<Self, FttsqError> {
932 let reader = Self::parse_directory(bytes)?;
933 reader.verify_digests(bytes)?;
934 Ok(reader)
935 }
936
937 pub fn parse_directory(bytes: &[u8]) -> Result<Self, FttsqError> {
947 Self::parse_directory_for_file_len(bytes, bytes.len() as u64)
948 }
949
950 pub fn parse_directory_of_prefix(bytes: &[u8], file_len: u64) -> Result<Self, FttsqError> {
965 Self::parse_directory_for_file_len(bytes, file_len)
966 }
967
968 fn parse_directory_for_file_len(bytes: &[u8], file_len: u64) -> Result<Self, FttsqError> {
975 let present_len = bytes.len() as u64;
976 if present_len < HEADER_PREFIX_BYTES {
977 return Err(FttsqError::TooShort {
978 length: present_len,
979 });
980 }
981
982 let mut magic = [0_u8; 8];
983 magic.copy_from_slice(&bytes[..8]);
984 if &magic != MAGIC {
985 return Err(FttsqError::BadMagic { found: magic });
986 }
987
988 let format_version = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]);
989 if format_version == 0 || format_version > FORMAT_VERSION {
992 return Err(FttsqError::UnsupportedVersion {
993 found: format_version,
994 supported: FORMAT_VERSION,
995 });
996 }
997
998 let mut length_bytes = [0_u8; 8];
999 length_bytes.copy_from_slice(&bytes[12..20]);
1000 let directory_len = u64::from_le_bytes(length_bytes);
1001 if directory_len > MAX_DIRECTORY_BYTES {
1002 return Err(FttsqError::DirectoryLength {
1003 declared: directory_len,
1004 limit: MAX_DIRECTORY_BYTES,
1005 });
1006 }
1007 let directory_end =
1008 HEADER_PREFIX_BYTES
1009 .checked_add(directory_len)
1010 .ok_or(FttsqError::DirectoryLength {
1011 declared: directory_len,
1012 limit: u64::MAX,
1013 })?;
1014 if directory_end > present_len || directory_end > file_len {
1015 return Err(FttsqError::DirectoryLength {
1016 declared: directory_len,
1017 limit: present_len.min(file_len),
1018 });
1019 }
1020
1021 let directory_bytes = &bytes[HEADER_PREFIX_BYTES as usize..directory_end as usize];
1023 let directory: Value = serde_json::from_slice(directory_bytes).map_err(|error| {
1024 FttsqError::DirectoryMalformed {
1025 detail: error.to_string(),
1026 }
1027 })?;
1028 let object = directory
1029 .as_object()
1030 .ok_or_else(|| FttsqError::DirectoryMalformed {
1031 detail: "top level is not a JSON object".to_owned(),
1032 })?;
1033
1034 let model_family = required_str(object.get("model_family"), "model_family")?.to_owned();
1035 let source_sha256 = required_str(object.get("source_sha256"), "source_sha256")?.to_owned();
1036
1037 let license_notice = object
1039 .get("license_notice")
1040 .and_then(Value::as_str)
1041 .unwrap_or_default()
1042 .to_owned();
1043 if license_notice.trim().is_empty() {
1044 return Err(FttsqError::LicenseNoticeMissing);
1045 }
1046
1047 let model_config = object.get("model_config").cloned().unwrap_or(Value::Null);
1048 let quantization_manifest = object
1049 .get("quantization_manifest")
1050 .cloned()
1051 .unwrap_or(Value::Null);
1052
1053 let sections = parse_sections(object.get("sections"), file_len)?;
1054 let section_index: BTreeMap<String, usize> = sections
1055 .iter()
1056 .enumerate()
1057 .map(|(index, section)| (section.name.clone(), index))
1058 .collect();
1059 let tensors = parse_tensors(object.get("tensors"), §ions, §ion_index)?;
1060 let tensor_index: BTreeMap<String, usize> = tensors
1061 .iter()
1062 .enumerate()
1063 .map(|(index, tensor)| (tensor.name.clone(), index))
1064 .collect();
1065
1066 Ok(Self {
1067 format_version,
1068 model_family,
1069 source_sha256,
1070 license_notice,
1071 model_config,
1072 quantization_manifest,
1073 sections,
1074 tensors,
1075 section_index,
1076 tensor_index,
1077 })
1078 }
1079
1080 pub fn verify_digests(&self, bytes: &[u8]) -> Result<(), FttsqError> {
1086 for section in &self.sections {
1087 let payload = self.section_bytes(section, bytes)?;
1088 let mut hasher = Sha256::new();
1089 hasher.update(payload);
1090 let actual = to_hex(&hasher.finish());
1091 if actual != section.sha256 {
1092 return Err(FttsqError::DigestMismatch {
1093 section: section.name.clone(),
1094 expected: section.sha256.clone(),
1095 actual,
1096 });
1097 }
1098 }
1099 Ok(())
1100 }
1101
1102 pub fn verify_digests_of_present(&self, bytes: &[u8]) -> Result<(), FttsqError> {
1112 for section in &self.sections {
1113 let Some(end) = section.end() else { continue };
1114 if end > bytes.len() as u64 {
1115 continue;
1116 }
1117 let payload = self.section_bytes(section, bytes)?;
1118 let mut hasher = Sha256::new();
1119 hasher.update(payload);
1120 let actual = to_hex(&hasher.finish());
1121 if actual != section.sha256 {
1122 return Err(FttsqError::DigestMismatch {
1123 section: section.name.clone(),
1124 expected: section.sha256.clone(),
1125 actual,
1126 });
1127 }
1128 }
1129 Ok(())
1130 }
1131
1132 pub fn absent_sections_in_prefix(&self, available: u64) -> Result<Vec<String>, FttsqError> {
1144 let mut absent = Vec::new();
1145 for section in &self.sections {
1146 let end = section
1147 .end()
1148 .ok_or_else(|| FttsqError::RangeOutOfBounds {
1149 what: format!("section `{}`", section.name),
1150 offset: section.offset,
1151 length: section.length,
1152 bound: available,
1153 })?;
1154 if end <= available {
1155 continue;
1156 }
1157 if section.offset < available {
1158 return Err(FttsqError::RangeOutOfBounds {
1159 what: format!("prefix splits section `{}`", section.name),
1160 offset: section.offset,
1161 length: section.length,
1162 bound: available,
1163 });
1164 }
1165 absent.push(section.name.clone());
1166 }
1167 Ok(absent)
1168 }
1169
1170 #[must_use]
1177 pub fn prefix_len_omitting(&self, omit: &[AccessClass]) -> Option<u64> {
1178 let omitted = |section: &SectionEntry| omit.contains(§ion.access_class);
1179 let first_omitted = self
1180 .sections
1181 .iter()
1182 .filter(|section| omitted(section))
1183 .map(|section| section.offset)
1184 .min()?;
1185 if self
1187 .sections
1188 .iter()
1189 .any(|section| !omitted(section) && section.offset >= first_omitted)
1190 {
1191 return None;
1192 }
1193 Some(first_omitted)
1194 }
1195
1196 fn section_bytes<'a>(
1197 &self,
1198 section: &SectionEntry,
1199 bytes: &'a [u8],
1200 ) -> Result<&'a [u8], FttsqError> {
1201 let end = section.end().ok_or_else(|| FttsqError::RangeOutOfBounds {
1202 what: format!("section `{}`", section.name),
1203 offset: section.offset,
1204 length: section.length,
1205 bound: bytes.len() as u64,
1206 })?;
1207 if end > bytes.len() as u64 {
1208 return Err(FttsqError::RangeOutOfBounds {
1209 what: format!("section `{}`", section.name),
1210 offset: section.offset,
1211 length: section.length,
1212 bound: bytes.len() as u64,
1213 });
1214 }
1215 Ok(&bytes[section.offset as usize..end as usize])
1216 }
1217
1218 #[must_use]
1220 pub const fn format_version(&self) -> u32 {
1221 self.format_version
1222 }
1223
1224 #[must_use]
1226 pub fn model_family(&self) -> &str {
1227 &self.model_family
1228 }
1229
1230 #[must_use]
1232 pub fn source_sha256(&self) -> &str {
1233 &self.source_sha256
1234 }
1235
1236 #[must_use]
1238 pub fn license_notice(&self) -> &str {
1239 &self.license_notice
1240 }
1241
1242 #[must_use]
1244 pub const fn model_config(&self) -> &Value {
1245 &self.model_config
1246 }
1247
1248 #[must_use]
1250 pub const fn quantization_manifest(&self) -> &Value {
1251 &self.quantization_manifest
1252 }
1253
1254 #[must_use]
1256 pub fn sections(&self) -> &[SectionEntry] {
1257 &self.sections
1258 }
1259
1260 #[must_use]
1262 pub fn tensors(&self) -> &[TensorEntry] {
1263 &self.tensors
1264 }
1265
1266 #[must_use]
1268 pub fn section(&self, name: &str) -> Option<&SectionEntry> {
1269 self.section_index
1270 .get(name)
1271 .and_then(|&index| self.sections.get(index))
1272 }
1273
1274 #[must_use]
1276 pub fn tensor(&self, name: &str) -> Option<&TensorEntry> {
1277 self.tensor_index
1278 .get(name)
1279 .and_then(|&index| self.tensors.get(index))
1280 }
1281
1282 #[must_use]
1284 pub fn sections_in_class(&self, class: AccessClass) -> Vec<&SectionEntry> {
1285 self.sections
1286 .iter()
1287 .filter(|section| section.access_class == class)
1288 .collect()
1289 }
1290
1291 pub fn tensor_bytes<'a>(&self, name: &str, bytes: &'a [u8]) -> Result<&'a [u8], FttsqError> {
1298 let tensor = self
1299 .tensor(name)
1300 .ok_or_else(|| FttsqError::UnknownSection {
1301 tensor: name.to_owned(),
1302 section: "<unknown tensor>".to_owned(),
1303 })?;
1304 let section = self
1305 .section(&tensor.section)
1306 .ok_or_else(|| FttsqError::UnknownSection {
1307 tensor: tensor.name.clone(),
1308 section: tensor.section.clone(),
1309 })?;
1310 let payload = self.section_bytes(section, bytes)?;
1311 let end = tensor.offset.checked_add(tensor.length).ok_or_else(|| {
1312 FttsqError::RangeOutOfBounds {
1313 what: format!("tensor `{}`", tensor.name),
1314 offset: tensor.offset,
1315 length: tensor.length,
1316 bound: payload.len() as u64,
1317 }
1318 })?;
1319 if end > payload.len() as u64 {
1320 return Err(FttsqError::RangeOutOfBounds {
1321 what: format!("tensor `{}`", tensor.name),
1322 offset: tensor.offset,
1323 length: tensor.length,
1324 bound: payload.len() as u64,
1325 });
1326 }
1327 Ok(&payload[tensor.offset as usize..end as usize])
1328 }
1329
1330 #[must_use]
1340 pub fn page_in_plan(&self) -> Vec<(&SectionEntry, PagePolicy)> {
1341 let mut plan: Vec<(&SectionEntry, PagePolicy)> = self
1342 .sections
1343 .iter()
1344 .map(|section| (section, section.access_class.page_policy()))
1345 .collect();
1346 plan.sort_by_key(|(section, policy)| {
1347 let rank = match policy {
1348 PagePolicy::Resident => 0_u8,
1349 PagePolicy::LazyRowGranular => 1,
1350 PagePolicy::OnDemand => 2,
1351 };
1352 (rank, section.length)
1353 });
1354 plan
1355 }
1356
1357 pub fn verify_census(&self, manifest: &ArtifactManifest) -> Result<(), Box<ArtifactCensus>> {
1363 let report = manifest.audit(self);
1364 if report.is_green() {
1365 Ok(())
1366 } else {
1367 Err(Box::new(report))
1368 }
1369 }
1370}
1371
1372#[derive(Clone, Debug, PartialEq, Eq)]
1382pub struct ExpectedArtifactTensor {
1383 pub name: String,
1385 pub shape: Vec<u64>,
1387 pub dtype: StoredDtype,
1389 pub access_class: AccessClass,
1391}
1392
1393#[derive(Clone, Debug, PartialEq, Eq)]
1395pub enum ArtifactFinding {
1396 Missing {
1398 name: String,
1400 },
1401 Extra {
1404 name: String,
1406 },
1407 ShapeMismatch {
1409 name: String,
1411 expected: Vec<u64>,
1413 found: Vec<u64>,
1415 },
1416 DtypeMismatch {
1418 name: String,
1420 expected: StoredDtype,
1422 found: StoredDtype,
1424 },
1425 WrongAccessClass {
1429 name: String,
1431 expected: AccessClass,
1433 found: AccessClass,
1435 },
1436 DanglingSection {
1438 name: String,
1440 section: String,
1442 },
1443}
1444
1445impl ArtifactFinding {
1446 #[must_use]
1448 pub fn tensor(&self) -> &str {
1449 match self {
1450 Self::Missing { name }
1451 | Self::Extra { name }
1452 | Self::ShapeMismatch { name, .. }
1453 | Self::DtypeMismatch { name, .. }
1454 | Self::WrongAccessClass { name, .. }
1455 | Self::DanglingSection { name, .. } => name,
1456 }
1457 }
1458
1459 #[must_use]
1461 pub const fn class(&self) -> &'static str {
1462 match self {
1463 Self::Missing { .. } => "missing",
1464 Self::Extra { .. } => "extra",
1465 Self::ShapeMismatch { .. } => "shape_mismatch",
1466 Self::DtypeMismatch { .. } => "dtype_mismatch",
1467 Self::WrongAccessClass { .. } => "wrong_access_class",
1468 Self::DanglingSection { .. } => "dangling_section",
1469 }
1470 }
1471}
1472
1473impl fmt::Display for ArtifactFinding {
1474 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1475 match self {
1476 Self::Missing { name } => write!(f, "MISSING {name}"),
1477 Self::Extra { name } => write!(f, "EXTRA {name}"),
1478 Self::ShapeMismatch {
1479 name,
1480 expected,
1481 found,
1482 } => write!(
1483 f,
1484 "SHAPE {name}: expected {expected:?}, found {found:?}"
1485 ),
1486 Self::DtypeMismatch {
1487 name,
1488 expected,
1489 found,
1490 } => write!(
1491 f,
1492 "DTYPE {name}: expected {expected}, found {found}"
1493 ),
1494 Self::WrongAccessClass {
1495 name,
1496 expected,
1497 found,
1498 } => write!(
1499 f,
1500 "ACCESS_CLASS {name}: expected {expected}, found {found}"
1501 ),
1502 Self::DanglingSection { name, section } => {
1503 write!(
1504 f,
1505 "DANGLING {name}: names undeclared section `{section}`"
1506 )
1507 }
1508 }
1509 }
1510}
1511
1512#[derive(Clone, Debug, Default)]
1518pub struct ArtifactManifest {
1519 label: String,
1520 expected: Vec<ExpectedArtifactTensor>,
1521}
1522
1523impl ArtifactManifest {
1524 #[must_use]
1526 pub fn new(label: impl Into<String>) -> Self {
1527 Self {
1528 label: label.into(),
1529 expected: Vec::new(),
1530 }
1531 }
1532
1533 #[must_use]
1535 pub fn expect(mut self, tensor: ExpectedArtifactTensor) -> Self {
1536 self.expected.push(tensor);
1537 self
1538 }
1539
1540 #[must_use]
1542 pub fn label(&self) -> &str {
1543 &self.label
1544 }
1545
1546 #[must_use]
1548 pub fn len(&self) -> usize {
1549 self.expected.len()
1550 }
1551
1552 #[must_use]
1554 pub fn is_empty(&self) -> bool {
1555 self.expected.is_empty()
1556 }
1557
1558 #[must_use]
1564 pub fn audit(&self, reader: &FttsqReader) -> ArtifactCensus {
1565 let mut findings = Vec::new();
1566 let expected_names: BTreeMap<&str, &ExpectedArtifactTensor> = self
1567 .expected
1568 .iter()
1569 .map(|tensor| (tensor.name.as_str(), tensor))
1570 .collect();
1571
1572 for expectation in &self.expected {
1573 let Some(found) = reader.tensor(&expectation.name) else {
1574 findings.push(ArtifactFinding::Missing {
1575 name: expectation.name.clone(),
1576 });
1577 continue;
1578 };
1579 if found.shape != expectation.shape {
1580 findings.push(ArtifactFinding::ShapeMismatch {
1581 name: expectation.name.clone(),
1582 expected: expectation.shape.clone(),
1583 found: found.shape.clone(),
1584 });
1585 }
1586 if found.dtype != expectation.dtype {
1587 findings.push(ArtifactFinding::DtypeMismatch {
1588 name: expectation.name.clone(),
1589 expected: expectation.dtype,
1590 found: found.dtype,
1591 });
1592 }
1593 match reader.section(&found.section) {
1594 Some(section) if section.access_class != expectation.access_class => {
1595 findings.push(ArtifactFinding::WrongAccessClass {
1596 name: expectation.name.clone(),
1597 expected: expectation.access_class,
1598 found: section.access_class,
1599 });
1600 }
1601 Some(_) => {}
1602 None => findings.push(ArtifactFinding::DanglingSection {
1603 name: expectation.name.clone(),
1604 section: found.section.clone(),
1605 }),
1606 }
1607 }
1608
1609 for tensor in reader.tensors() {
1610 if !expected_names.contains_key(tensor.name.as_str()) {
1611 findings.push(ArtifactFinding::Extra {
1612 name: tensor.name.clone(),
1613 });
1614 }
1615 }
1616
1617 ArtifactCensus {
1618 label: self.label.clone(),
1619 expected: self.expected.len(),
1620 found: reader.tensors().len(),
1621 findings,
1622 }
1623 }
1624}
1625
1626#[derive(Clone, Debug)]
1628pub struct ArtifactCensus {
1629 label: String,
1630 expected: usize,
1631 found: usize,
1632 findings: Vec<ArtifactFinding>,
1633}
1634
1635impl ArtifactCensus {
1636 #[must_use]
1638 pub fn is_green(&self) -> bool {
1639 self.findings.is_empty()
1640 }
1641
1642 #[must_use]
1644 pub fn findings(&self) -> &[ArtifactFinding] {
1645 &self.findings
1646 }
1647
1648 #[must_use]
1650 pub fn count_of(&self, class: &str) -> usize {
1651 self.findings
1652 .iter()
1653 .filter(|finding| finding.class() == class)
1654 .count()
1655 }
1656
1657 #[must_use]
1659 pub fn render(&self) -> String {
1660 let mut out = format!(
1661 "artifact census `{}`: expected {} tensors, artifact declares {} — {}\n",
1662 self.label,
1663 self.expected,
1664 self.found,
1665 if self.is_green() {
1666 "GREEN".to_owned()
1667 } else {
1668 format!("{} FINDINGS", self.findings.len())
1669 }
1670 );
1671 for finding in &self.findings {
1672 out.push_str(&format!(" {finding}\n"));
1673 }
1674 out
1675 }
1676}
1677
1678impl fmt::Display for ArtifactCensus {
1679 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1680 f.write_str(&self.render())
1681 }
1682}
1683
1684impl std::error::Error for ArtifactCensus {}
1685
1686fn required_str<'a>(value: Option<&'a Value>, path: &str) -> Result<&'a str, FttsqError> {
1687 value
1688 .and_then(Value::as_str)
1689 .filter(|text| !text.is_empty())
1690 .ok_or_else(|| FttsqError::Field {
1691 path: path.to_owned(),
1692 expected: "a non-empty string".to_owned(),
1693 })
1694}
1695
1696fn required_u64(value: Option<&Value>, path: &str) -> Result<u64, FttsqError> {
1697 value
1698 .and_then(Value::as_u64)
1699 .ok_or_else(|| FttsqError::Field {
1700 path: path.to_owned(),
1701 expected: "a non-negative integer".to_owned(),
1702 })
1703}
1704
1705fn parse_sections(value: Option<&Value>, file_len: u64) -> Result<Vec<SectionEntry>, FttsqError> {
1706 let array = value
1707 .and_then(Value::as_array)
1708 .ok_or_else(|| FttsqError::Field {
1709 path: "sections".to_owned(),
1710 expected: "an array".to_owned(),
1711 })?;
1712 if array.len() > MAX_SECTIONS {
1713 return Err(FttsqError::LimitExceeded {
1714 what: "section".to_owned(),
1715 found: array.len() as u64,
1716 limit: MAX_SECTIONS as u64,
1717 });
1718 }
1719
1720 let mut sections = Vec::with_capacity(array.len());
1721 let mut seen: BTreeMap<String, ()> = BTreeMap::new();
1722 for (index, entry) in array.iter().enumerate() {
1723 let path = |field: &str| format!("sections[{index}].{field}");
1724 let name = required_str(entry.get("name"), &path("name"))?.to_owned();
1725 if seen.insert(name.clone(), ()).is_some() {
1726 return Err(FttsqError::DuplicateName {
1727 what: "section".to_owned(),
1728 name,
1729 });
1730 }
1731 let class_text = required_str(entry.get("access_class"), &path("access_class"))?;
1732 let access_class =
1733 AccessClass::parse(class_text).ok_or_else(|| FttsqError::UnknownValue {
1734 path: path("access_class"),
1735 found: class_text.to_owned(),
1736 })?;
1737 let offset = required_u64(entry.get("offset"), &path("offset"))?;
1738 let length = required_u64(entry.get("length"), &path("length"))?;
1739 let sha256 = required_str(entry.get("sha256"), &path("sha256"))?.to_owned();
1740
1741 let end = offset
1742 .checked_add(length)
1743 .ok_or_else(|| FttsqError::RangeOutOfBounds {
1744 what: format!("section `{name}`"),
1745 offset,
1746 length,
1747 bound: file_len,
1748 })?;
1749 if end > file_len {
1750 return Err(FttsqError::RangeOutOfBounds {
1751 what: format!("section `{name}`"),
1752 offset,
1753 length,
1754 bound: file_len,
1755 });
1756 }
1757
1758 sections.push(SectionEntry {
1759 name,
1760 access_class,
1761 offset,
1762 length,
1763 sha256,
1764 });
1765 }
1766
1767 let mut ordered: Vec<&SectionEntry> = sections.iter().collect();
1769 ordered.sort_by_key(|section| section.offset);
1770 for pair in ordered.windows(2) {
1771 let (first, second) = (pair[0], pair[1]);
1772 let first_end = first.end().unwrap_or(u64::MAX);
1773 if first_end > second.offset {
1774 return Err(FttsqError::SectionOverlap {
1775 first: first.name.clone(),
1776 second: second.name.clone(),
1777 });
1778 }
1779 }
1780
1781 Ok(sections)
1782}
1783
1784fn parse_tensors(
1785 value: Option<&Value>,
1786 sections: &[SectionEntry],
1787 section_index: &BTreeMap<String, usize>,
1788) -> Result<Vec<TensorEntry>, FttsqError> {
1789 let array = value
1790 .and_then(Value::as_array)
1791 .ok_or_else(|| FttsqError::Field {
1792 path: "tensors".to_owned(),
1793 expected: "an array".to_owned(),
1794 })?;
1795 if array.len() > MAX_TENSORS {
1796 return Err(FttsqError::LimitExceeded {
1797 what: "tensor".to_owned(),
1798 found: array.len() as u64,
1799 limit: MAX_TENSORS as u64,
1800 });
1801 }
1802
1803 let mut tensors = Vec::with_capacity(array.len());
1804 let mut seen: BTreeMap<String, ()> = BTreeMap::new();
1805 for (index, entry) in array.iter().enumerate() {
1806 let path = |field: &str| format!("tensors[{index}].{field}");
1807 let name = required_str(entry.get("name"), &path("name"))?.to_owned();
1808 if seen.insert(name.clone(), ()).is_some() {
1809 return Err(FttsqError::DuplicateName {
1810 what: "tensor".to_owned(),
1811 name,
1812 });
1813 }
1814 let section = required_str(entry.get("section"), &path("section"))?.to_owned();
1815 let dtype_text = required_str(entry.get("dtype"), &path("dtype"))?;
1816 let dtype = StoredDtype::parse(dtype_text).ok_or_else(|| FttsqError::UnknownValue {
1817 path: path("dtype"),
1818 found: dtype_text.to_owned(),
1819 })?;
1820
1821 let shape_array = entry
1822 .get("shape")
1823 .and_then(Value::as_array)
1824 .ok_or_else(|| FttsqError::Field {
1825 path: path("shape"),
1826 expected: "an array".to_owned(),
1827 })?;
1828 if shape_array.len() > MAX_RANK {
1829 return Err(FttsqError::LimitExceeded {
1830 what: format!("tensor `{name}` rank"),
1831 found: shape_array.len() as u64,
1832 limit: MAX_RANK as u64,
1833 });
1834 }
1835 let mut shape = Vec::with_capacity(shape_array.len());
1836 for (axis, dim) in shape_array.iter().enumerate() {
1837 let dim = dim.as_u64().ok_or_else(|| FttsqError::Field {
1838 path: format!("{}[{axis}]", path("shape")),
1839 expected: "a non-negative integer".to_owned(),
1840 })?;
1841 if dim > MAX_DIM {
1842 return Err(FttsqError::LimitExceeded {
1843 what: format!("tensor `{name}` dimension {axis}"),
1844 found: dim,
1845 limit: MAX_DIM,
1846 });
1847 }
1848 shape.push(dim);
1849 }
1850
1851 let offset = required_u64(entry.get("offset"), &path("offset"))?;
1852 let length = required_u64(entry.get("length"), &path("length"))?;
1853 let scales = entry
1854 .get("scales")
1855 .and_then(Value::as_str)
1856 .map(str::to_owned);
1857
1858 let tensor = TensorEntry {
1859 name,
1860 section,
1861 dtype,
1862 shape,
1863 offset,
1864 length,
1865 scales,
1866 };
1867
1868 let elements = tensor.elements().ok_or_else(|| FttsqError::LimitExceeded {
1872 what: format!("tensor `{}` element count", tensor.name),
1873 found: u64::MAX,
1874 limit: MAX_DIM,
1875 })?;
1876 let implied = dtype
1877 .storage_bytes(elements)
1878 .ok_or_else(|| FttsqError::LimitExceeded {
1879 what: format!("tensor `{}` storage size", tensor.name),
1880 found: u64::MAX,
1881 limit: MAX_DIM,
1882 })?;
1883 if implied != tensor.length {
1884 return Err(FttsqError::LengthMismatch {
1885 tensor: tensor.name.clone(),
1886 declared: tensor.length,
1887 implied,
1888 });
1889 }
1890
1891 let owner = section_index
1892 .get(&tensor.section)
1893 .and_then(|&index| sections.get(index))
1894 .ok_or_else(|| FttsqError::UnknownSection {
1895 tensor: tensor.name.clone(),
1896 section: tensor.section.clone(),
1897 })?;
1898 let end = tensor.offset.checked_add(tensor.length).ok_or_else(|| {
1899 FttsqError::RangeOutOfBounds {
1900 what: format!("tensor `{}`", tensor.name),
1901 offset: tensor.offset,
1902 length: tensor.length,
1903 bound: owner.length,
1904 }
1905 })?;
1906 if end > owner.length {
1907 return Err(FttsqError::RangeOutOfBounds {
1908 what: format!("tensor `{}`", tensor.name),
1909 offset: tensor.offset,
1910 length: tensor.length,
1911 bound: owner.length,
1912 });
1913 }
1914
1915 tensors.push(tensor);
1916 }
1917
1918 let mut by_section: BTreeMap<&str, Vec<&TensorEntry>> = BTreeMap::new();
1921 for tensor in &tensors {
1922 by_section
1923 .entry(tensor.section.as_str())
1924 .or_default()
1925 .push(tensor);
1926 }
1927 for group in by_section.values_mut() {
1928 group.sort_by_key(|tensor| tensor.offset);
1929 for pair in group.windows(2) {
1930 let (first, second) = (pair[0], pair[1]);
1931 let first_end = first.offset.saturating_add(first.length);
1932 if first_end > second.offset {
1933 return Err(FttsqError::TensorOverlap {
1934 first: first.name.clone(),
1935 second: second.name.clone(),
1936 });
1937 }
1938 }
1939 }
1940
1941 Ok(tensors)
1942}
1943
1944#[derive(Debug)]
1952pub struct FttsqStreamPlan {
1953 model_family: String,
1954 source_sha256: String,
1955 license_notice: String,
1956 model_config: Value,
1957 quantization_manifest: Value,
1958 sections: Vec<(String, AccessClass, u64)>,
1959 tensors: Vec<TensorEntry>,
1960}
1961
1962impl FttsqStreamPlan {
1963 #[must_use]
1965 pub fn new(model_family: impl Into<String>, source_sha256: impl Into<String>) -> Self {
1966 Self {
1967 model_family: model_family.into(),
1968 source_sha256: source_sha256.into(),
1969 license_notice: String::new(),
1970 model_config: Value::Null,
1971 quantization_manifest: Value::Null,
1972 sections: Vec::new(),
1973 tensors: Vec::new(),
1974 }
1975 }
1976
1977 #[must_use]
1979 pub fn license_notice(mut self, notice: impl Into<String>) -> Self {
1980 self.license_notice = notice.into();
1981 self
1982 }
1983
1984 #[must_use]
1986 pub fn model_config(mut self, config: Value) -> Self {
1987 self.model_config = config;
1988 self
1989 }
1990
1991 #[must_use]
1993 pub fn quantization_manifest(mut self, manifest: Value) -> Self {
1994 self.quantization_manifest = manifest;
1995 self
1996 }
1997
1998 #[must_use]
2000 pub fn section(
2001 mut self,
2002 name: impl Into<String>,
2003 access_class: AccessClass,
2004 length: u64,
2005 ) -> Self {
2006 self.sections.push((name.into(), access_class, length));
2007 self
2008 }
2009
2010 #[must_use]
2012 pub fn tensor(mut self, tensor: TensorEntry) -> Self {
2013 self.tensors.push(tensor);
2014 self
2015 }
2016
2017 pub fn begin<W: std::io::Write + std::io::Seek>(
2029 self,
2030 mut writer: W,
2031 ) -> Result<FttsqStreamingWriter<W>, FttsqError> {
2032 if self.license_notice.trim().is_empty() {
2033 return Err(FttsqError::LicenseNoticeMissing);
2034 }
2035
2036 let mut sections: Vec<SectionEntry> = self
2040 .sections
2041 .into_iter()
2042 .map(|(name, access_class, length)| SectionEntry {
2043 name,
2044 access_class,
2045 offset: 0,
2046 length,
2047 sha256: "0".repeat(64),
2048 })
2049 .collect();
2050 let mut probe_sections = sections.clone();
2051 for section in &mut probe_sections {
2052 section.offset = u64::MAX;
2055 }
2056 let probe = stream_directory_json(
2057 &self.model_family,
2058 &self.source_sha256,
2059 &self.license_notice,
2060 &self.model_config,
2061 &self.quantization_manifest,
2062 &probe_sections,
2063 &self.tensors,
2064 );
2065 let directory_len = serde_json::to_vec(&probe)
2066 .map_err(|error| FttsqError::DirectoryMalformed {
2067 detail: error.to_string(),
2068 })?
2069 .len() as u64;
2070 if directory_len > MAX_DIRECTORY_BYTES {
2071 return Err(FttsqError::DirectoryLength {
2072 declared: directory_len,
2073 limit: MAX_DIRECTORY_BYTES,
2074 });
2075 }
2076 let payload_start =
2077 HEADER_PREFIX_BYTES
2078 .checked_add(directory_len)
2079 .ok_or(FttsqError::DirectoryLength {
2080 declared: directory_len,
2081 limit: u64::MAX,
2082 })?;
2083 let final_file_len = layout_stream_sections(&mut sections, payload_start)?;
2084
2085 let directory = stream_directory_json(
2086 &self.model_family,
2087 &self.source_sha256,
2088 &self.license_notice,
2089 &self.model_config,
2090 &self.quantization_manifest,
2091 §ions,
2092 &self.tensors,
2093 );
2094 let mut directory_bytes =
2095 serde_json::to_vec(&directory).map_err(|error| FttsqError::DirectoryMalformed {
2096 detail: error.to_string(),
2097 })?;
2098 if directory_bytes.len() as u64 > directory_len {
2099 return Err(FttsqError::DirectoryLength {
2100 declared: directory_bytes.len() as u64,
2101 limit: directory_len,
2102 });
2103 }
2104 directory_bytes.resize(directory_len as usize, b' ');
2105
2106 let mut header_and_directory = Vec::with_capacity(
2107 (HEADER_PREFIX_BYTES as usize).saturating_add(directory_bytes.len()),
2108 );
2109 header_and_directory.extend_from_slice(MAGIC);
2110 header_and_directory.extend_from_slice(&FORMAT_VERSION.to_le_bytes());
2111 header_and_directory.extend_from_slice(&directory_len.to_le_bytes());
2112 header_and_directory.extend_from_slice(&directory_bytes);
2113
2114 FttsqReader::parse_directory_for_file_len(&header_and_directory, final_file_len)?;
2117 writer
2118 .write_all(&header_and_directory)
2119 .map_err(|error| stream_io_error("write header and directory", &error))?;
2120
2121 let mut streaming = FttsqStreamingWriter {
2122 writer,
2123 model_family: self.model_family,
2124 source_sha256: self.source_sha256,
2125 license_notice: self.license_notice,
2126 model_config: self.model_config,
2127 quantization_manifest: self.quantization_manifest,
2128 sections,
2129 tensors: self.tensors,
2130 directory_len,
2131 current_section: 0,
2132 section_written: 0,
2133 section_hasher: Sha256::new(),
2134 };
2135 streaming.finalize_empty_sections();
2136 Ok(streaming)
2137 }
2138}
2139
2140#[derive(Debug)]
2147pub struct FttsqStreamingWriter<W> {
2148 writer: W,
2149 model_family: String,
2150 source_sha256: String,
2151 license_notice: String,
2152 model_config: Value,
2153 quantization_manifest: Value,
2154 sections: Vec<SectionEntry>,
2155 tensors: Vec<TensorEntry>,
2156 directory_len: u64,
2157 current_section: usize,
2158 section_written: u64,
2159 section_hasher: Sha256,
2160}
2161
2162impl<W: std::io::Write + std::io::Seek> FttsqStreamingWriter<W> {
2163 pub fn write_section(&mut self, section: &str, bytes: &[u8]) -> Result<(), FttsqError> {
2174 let Some(entry) = self.sections.get(self.current_section) else {
2175 return Err(FttsqError::SectionWriteOutOfOrder {
2176 expected: None,
2177 actual: section.to_owned(),
2178 });
2179 };
2180 let expected = entry.name.clone();
2181 let declared = entry.length;
2182 if expected != section {
2183 return Err(FttsqError::SectionWriteOutOfOrder {
2184 expected: Some(expected),
2185 actual: section.to_owned(),
2186 });
2187 }
2188 let bytes_len = bytes.len() as u64;
2189 let attempted = self.section_written.checked_add(bytes_len).ok_or_else(|| {
2190 FttsqError::SectionLengthExceeded {
2191 section: expected.clone(),
2192 declared,
2193 attempted: u64::MAX,
2194 }
2195 })?;
2196 if attempted > declared {
2197 return Err(FttsqError::SectionLengthExceeded {
2198 section: expected,
2199 declared,
2200 attempted,
2201 });
2202 }
2203
2204 self.writer
2205 .write_all(bytes)
2206 .map_err(|error| stream_io_error("write section", &error))?;
2207 self.section_hasher.update(bytes);
2208 self.section_written = attempted;
2209 self.finalize_empty_sections();
2210 Ok(())
2211 }
2212
2213 pub fn finish(mut self) -> Result<W, FttsqError> {
2224 if let Some(section) = self.sections.get(self.current_section) {
2225 return Err(FttsqError::SectionIncomplete {
2226 section: section.name.clone(),
2227 declared: section.length,
2228 written: self.section_written,
2229 });
2230 }
2231
2232 let directory = stream_directory_json(
2233 &self.model_family,
2234 &self.source_sha256,
2235 &self.license_notice,
2236 &self.model_config,
2237 &self.quantization_manifest,
2238 &self.sections,
2239 &self.tensors,
2240 );
2241 let directory_bytes =
2242 serde_json::to_vec(&directory).map_err(|error| FttsqError::DirectoryMalformed {
2243 detail: error.to_string(),
2244 })?;
2245 if directory_bytes.len() as u64 > self.directory_len {
2246 return Err(FttsqError::DirectoryLength {
2247 declared: directory_bytes.len() as u64,
2248 limit: self.directory_len,
2249 });
2250 }
2251
2252 self.writer
2253 .seek(std::io::SeekFrom::Start(HEADER_PREFIX_BYTES))
2254 .map_err(|error| stream_io_error("seek to directory", &error))?;
2255 self.writer
2256 .write_all(&directory_bytes)
2257 .map_err(|error| stream_io_error("finalize directory", &error))?;
2258 write_space_padding(
2259 &mut self.writer,
2260 self.directory_len - directory_bytes.len() as u64,
2261 )?;
2262 self.writer
2263 .seek(std::io::SeekFrom::End(0))
2264 .map_err(|error| stream_io_error("seek to artifact end", &error))?;
2265 self.writer
2266 .flush()
2267 .map_err(|error| stream_io_error("flush finalized artifact", &error))?;
2268 Ok(self.writer)
2269 }
2270
2271 fn finalize_empty_sections(&mut self) {
2272 while let Some(section) = self.sections.get_mut(self.current_section) {
2273 if self.section_written != section.length {
2274 break;
2275 }
2276 section.sha256 = to_hex(&std::mem::take(&mut self.section_hasher).finish());
2277 self.current_section += 1;
2278 self.section_written = 0;
2279 }
2280 }
2281}
2282
2283fn layout_stream_sections(
2284 sections: &mut [SectionEntry],
2285 payload_start: u64,
2286) -> Result<u64, FttsqError> {
2287 let mut cursor = payload_start;
2288 for section in sections {
2289 section.offset = cursor;
2290 cursor =
2291 cursor
2292 .checked_add(section.length)
2293 .ok_or_else(|| FttsqError::RangeOutOfBounds {
2294 what: format!("section `{}`", section.name),
2295 offset: section.offset,
2296 length: section.length,
2297 bound: u64::MAX,
2298 })?;
2299 }
2300 Ok(cursor)
2301}
2302
2303fn stream_directory_json(
2304 model_family: &str,
2305 source_sha256: &str,
2306 license_notice: &str,
2307 model_config: &Value,
2308 quantization_manifest: &Value,
2309 sections: &[SectionEntry],
2310 tensors: &[TensorEntry],
2311) -> Value {
2312 let sections: Vec<Value> = sections
2313 .iter()
2314 .map(|section| {
2315 json!({
2316 "name": section.name,
2317 "access_class": section.access_class.as_str(),
2318 "offset": section.offset,
2319 "length": section.length,
2320 "sha256": section.sha256,
2321 })
2322 })
2323 .collect();
2324 let tensors: Vec<Value> = tensors
2325 .iter()
2326 .map(|tensor| {
2327 json!({
2328 "name": tensor.name,
2329 "section": tensor.section,
2330 "dtype": tensor.dtype.as_str(),
2331 "shape": tensor.shape,
2332 "offset": tensor.offset,
2333 "length": tensor.length,
2334 "scales": tensor.scales,
2335 })
2336 })
2337 .collect();
2338 json!({
2339 "format_version": FORMAT_VERSION,
2340 "model_family": model_family,
2341 "source_sha256": source_sha256,
2342 "license_notice": license_notice,
2343 "model_config": model_config,
2344 "quantization_manifest": quantization_manifest,
2345 "sections": sections,
2346 "tensors": tensors,
2347 })
2348}
2349
2350fn stream_io_error(operation: &str, error: &std::io::Error) -> FttsqError {
2351 FttsqError::Io {
2352 operation: operation.to_owned(),
2353 path: "<fttsq stream>".to_owned(),
2354 detail: error.to_string(),
2355 }
2356}
2357
2358fn write_space_padding<W: std::io::Write>(
2359 writer: &mut W,
2360 mut remaining: u64,
2361) -> Result<(), FttsqError> {
2362 const SPACES: [u8; 4096] = [b' '; 4096];
2363 while remaining > 0 {
2364 let count = remaining.min(SPACES.len() as u64) as usize;
2365 writer
2366 .write_all(&SPACES[..count])
2367 .map_err(|error| stream_io_error("pad finalized directory", &error))?;
2368 remaining -= count as u64;
2369 }
2370 Ok(())
2371}
2372
2373#[derive(Debug, Default)]
2378pub struct FttsqWriter {
2379 model_family: String,
2380 source_sha256: String,
2381 license_notice: String,
2382 model_config: Value,
2383 quantization_manifest: Value,
2384 sections: Vec<(SectionEntry, Vec<u8>)>,
2385 tensors: Vec<TensorEntry>,
2386}
2387
2388impl FttsqWriter {
2389 #[must_use]
2391 pub fn new(model_family: impl Into<String>, source_sha256: impl Into<String>) -> Self {
2392 Self {
2393 model_family: model_family.into(),
2394 source_sha256: source_sha256.into(),
2395 license_notice: String::new(),
2396 model_config: Value::Null,
2397 quantization_manifest: Value::Null,
2398 sections: Vec::new(),
2399 tensors: Vec::new(),
2400 }
2401 }
2402
2403 #[must_use]
2405 pub fn license_notice(mut self, notice: impl Into<String>) -> Self {
2406 self.license_notice = notice.into();
2407 self
2408 }
2409
2410 #[must_use]
2412 pub fn model_config(mut self, config: Value) -> Self {
2413 self.model_config = config;
2414 self
2415 }
2416
2417 #[must_use]
2419 pub fn quantization_manifest(mut self, manifest: Value) -> Self {
2420 self.quantization_manifest = manifest;
2421 self
2422 }
2423
2424 #[must_use]
2426 pub fn section(
2427 mut self,
2428 name: impl Into<String>,
2429 access_class: AccessClass,
2430 payload: Vec<u8>,
2431 ) -> Self {
2432 let entry = SectionEntry {
2433 name: name.into(),
2434 access_class,
2435 offset: 0,
2436 length: payload.len() as u64,
2437 sha256: String::new(),
2438 };
2439 self.sections.push((entry, payload));
2440 self
2441 }
2442
2443 #[must_use]
2445 pub fn tensor(mut self, tensor: TensorEntry) -> Self {
2446 self.tensors.push(tensor);
2447 self
2448 }
2449
2450 pub fn finish(mut self) -> Result<Vec<u8>, FttsqError> {
2460 if self.license_notice.trim().is_empty() {
2461 return Err(FttsqError::LicenseNoticeMissing);
2462 }
2463
2464 for (entry, payload) in &mut self.sections {
2465 entry.length = payload.len() as u64;
2466 entry.sha256 = hex_digest(payload);
2467 }
2468
2469 let probe = self.directory_json(u64::MAX);
2474 let probe_len = serde_json::to_vec(&probe)
2475 .map_err(|error| FttsqError::DirectoryMalformed {
2476 detail: error.to_string(),
2477 })?
2478 .len() as u64;
2479
2480 let payload_start = HEADER_PREFIX_BYTES + probe_len;
2481 let directory = self.directory_json(payload_start);
2482 let mut directory_bytes =
2483 serde_json::to_vec(&directory).map_err(|error| FttsqError::DirectoryMalformed {
2484 detail: error.to_string(),
2485 })?;
2486 while (directory_bytes.len() as u64) < probe_len {
2489 directory_bytes.push(b' ');
2490 }
2491
2492 let mut out = Vec::with_capacity(payload_start as usize);
2493 out.extend_from_slice(MAGIC);
2494 out.extend_from_slice(&FORMAT_VERSION.to_le_bytes());
2495 out.extend_from_slice(&(directory_bytes.len() as u64).to_le_bytes());
2496 out.extend_from_slice(&directory_bytes);
2497 for (_, payload) in &self.sections {
2498 out.extend_from_slice(payload);
2499 }
2500
2501 FttsqReader::open(&out)?;
2503 Ok(out)
2504 }
2505
2506 pub fn write_to_path(self, path: &std::path::Path) -> Result<(), FttsqError> {
2522 use std::io::Write as _;
2523
2524 let bytes = self.finish()?;
2525
2526 let parent = path.parent().unwrap_or_else(|| std::path::Path::new("."));
2527 let file_name = path.file_name().map_or_else(
2529 || std::ffi::OsString::from("artifact.fttsq"),
2530 std::ffi::OsStr::to_os_string,
2531 );
2532 let mut temp_name = file_name;
2533 temp_name.push(format!(".tmp.{}", std::process::id()));
2534 let temp_path = parent.join(temp_name);
2535
2536 let io =
2537 |operation: &str, target: &std::path::Path, error: &std::io::Error| FttsqError::Io {
2538 operation: operation.to_owned(),
2539 path: target.display().to_string(),
2540 detail: error.to_string(),
2541 };
2542
2543 let result = (|| -> Result<(), FttsqError> {
2545 let mut file = std::fs::File::create(&temp_path)
2546 .map_err(|error| io("create", &temp_path, &error))?;
2547 file.write_all(&bytes)
2548 .map_err(|error| io("write", &temp_path, &error))?;
2549 file.sync_all()
2552 .map_err(|error| io("fsync", &temp_path, &error))?;
2553 drop(file);
2554 std::fs::rename(&temp_path, path).map_err(|error| io("rename", path, &error))
2555 })();
2556
2557 if result.is_err() {
2558 let _ = std::fs::remove_file(&temp_path);
2559 }
2560 result
2561 }
2562
2563 fn directory_json(&self, payload_start: u64) -> Value {
2564 let mut cursor = payload_start;
2565 let sections: Vec<Value> = self
2566 .sections
2567 .iter()
2568 .map(|(entry, _)| {
2569 let offset = cursor;
2570 cursor = cursor.saturating_add(entry.length);
2575 json!({
2576 "name": entry.name,
2577 "access_class": entry.access_class.as_str(),
2578 "offset": offset,
2579 "length": entry.length,
2580 "sha256": entry.sha256,
2581 })
2582 })
2583 .collect();
2584
2585 let tensors: Vec<Value> = self
2586 .tensors
2587 .iter()
2588 .map(|tensor| {
2589 json!({
2590 "name": tensor.name,
2591 "section": tensor.section,
2592 "dtype": tensor.dtype.as_str(),
2593 "shape": tensor.shape,
2594 "offset": tensor.offset,
2595 "length": tensor.length,
2596 "scales": tensor.scales,
2597 })
2598 })
2599 .collect();
2600
2601 json!({
2602 "format_version": FORMAT_VERSION,
2603 "model_family": self.model_family,
2604 "source_sha256": self.source_sha256,
2605 "license_notice": self.license_notice,
2606 "model_config": self.model_config,
2607 "quantization_manifest": self.quantization_manifest,
2608 "sections": sections,
2609 "tensors": tensors,
2610 })
2611 }
2612}
2613
2614#[cfg(test)]
2615mod tests {
2616 use super::*;
2617 use std::io::Cursor;
2618
2619 const NOTICE: &str = "Copyright 2026 Alibaba Cloud\nApache-2.0\nCHANGES: requantized to .fttsq";
2621
2622 fn artifact() -> Vec<u8> {
2623 FttsqWriter::new("qwen3-tts-12hz-0.6b-base", "a".repeat(64))
2624 .license_notice(NOTICE)
2625 .model_config(json!({ "hidden_size": 1024 }))
2626 .quantization_manifest(json!({ "talker": "q8" }))
2627 .section(
2628 "microdecoder",
2629 AccessClass::HotRecurrentMicrodecoder,
2630 vec![7_u8; 64],
2631 )
2632 .section(
2633 "text_embedding",
2634 AccessClass::ColdTextEmbedding,
2635 vec![9_u8; 32],
2636 )
2637 .tensor(TensorEntry {
2638 name: "microdecoder.body".to_owned(),
2639 section: "microdecoder".to_owned(),
2640 dtype: StoredDtype::Q8,
2641 shape: vec![8, 8],
2642 offset: 0,
2643 length: 64,
2644 scales: Some("microdecoder.body.scales".to_owned()),
2645 })
2646 .tensor(TensorEntry {
2647 name: "text_embedding.weight".to_owned(),
2648 section: "text_embedding".to_owned(),
2649 dtype: StoredDtype::Bf16,
2650 shape: vec![4, 4],
2651 offset: 0,
2652 length: 32,
2653 scales: None,
2654 })
2655 .finish()
2656 .expect("the fixture artifact is writable")
2657 }
2658
2659 fn stream_plan() -> FttsqStreamPlan {
2660 FttsqStreamPlan::new("qwen3-tts-12hz-0.6b-base", "a".repeat(64))
2661 .license_notice(NOTICE)
2662 .model_config(json!({ "hidden_size": 1024 }))
2663 .quantization_manifest(json!({ "talker": "q8" }))
2664 .section("microdecoder", AccessClass::HotRecurrentMicrodecoder, 64)
2665 .section("text_embedding", AccessClass::ColdTextEmbedding, 32)
2666 .tensor(TensorEntry {
2667 name: "microdecoder.body".to_owned(),
2668 section: "microdecoder".to_owned(),
2669 dtype: StoredDtype::Q8,
2670 shape: vec![8, 8],
2671 offset: 0,
2672 length: 64,
2673 scales: Some("microdecoder.body.scales".to_owned()),
2674 })
2675 .tensor(TensorEntry {
2676 name: "text_embedding.weight".to_owned(),
2677 section: "text_embedding".to_owned(),
2678 dtype: StoredDtype::Bf16,
2679 shape: vec![4, 4],
2680 offset: 0,
2681 length: 32,
2682 scales: None,
2683 })
2684 }
2685
2686 fn streamed_artifact() -> Vec<u8> {
2687 let mut writer = stream_plan()
2688 .begin(Cursor::new(Vec::new()))
2689 .expect("the stream plan is structurally valid");
2690 writer
2691 .write_section("microdecoder", &[7_u8; 64])
2692 .expect("first section streams");
2693 writer
2694 .write_section("text_embedding", &[9_u8; 32])
2695 .expect("second section streams");
2696 writer
2697 .finish()
2698 .expect("complete stream finalizes")
2699 .into_inner()
2700 }
2701
2702 #[test]
2703 fn streaming_writer_is_canonical_and_never_retains_section_payloads() {
2704 let bytes = streamed_artifact();
2708 assert_eq!(bytes, artifact());
2709 let reader = FttsqReader::open(&bytes).expect("finalized stream verifies");
2710 assert_eq!(
2711 reader
2712 .tensor_bytes("microdecoder.body", &bytes)
2713 .expect("streamed tensor resolves"),
2714 &[7_u8; 64]
2715 );
2716 }
2717
2718 #[test]
2719 fn streaming_writer_refuses_out_of_order_or_incomplete_sections() {
2720 let mut writer = stream_plan()
2721 .begin(Cursor::new(Vec::new()))
2722 .expect("the stream plan is structurally valid");
2723 assert_eq!(
2724 writer
2725 .write_section("text_embedding", &[9_u8; 32])
2726 .expect_err("later sections cannot be buffered"),
2727 FttsqError::SectionWriteOutOfOrder {
2728 expected: Some("microdecoder".to_owned()),
2729 actual: "text_embedding".to_owned(),
2730 }
2731 );
2732 writer
2733 .write_section("microdecoder", &[7_u8; 63])
2734 .expect("a bounded partial chunk is accepted");
2735 assert_eq!(
2736 writer
2737 .finish()
2738 .expect_err("a partial section cannot acquire a digest"),
2739 FttsqError::SectionIncomplete {
2740 section: "microdecoder".to_owned(),
2741 declared: 64,
2742 written: 63,
2743 }
2744 );
2745 }
2746
2747 #[test]
2748 fn round_trips_through_write_and_read() {
2749 let bytes = artifact();
2750 let reader =
2751 FttsqReader::open(&bytes).expect("the artifact we just wrote must be readable");
2752
2753 assert_eq!(reader.format_version(), FORMAT_VERSION);
2754 assert_eq!(reader.model_family(), "qwen3-tts-12hz-0.6b-base");
2755 assert!(reader.license_notice().contains("Alibaba Cloud"));
2756 assert_eq!(reader.model_config()["hidden_size"], 1024);
2757 assert_eq!(reader.sections().len(), 2);
2758 assert_eq!(reader.tensors().len(), 2);
2759
2760 assert_eq!(
2762 reader
2763 .tensor_bytes("microdecoder.body", &bytes)
2764 .expect("tensor resolves"),
2765 &vec![7_u8; 64][..]
2766 );
2767 assert_eq!(
2768 reader
2769 .tensor_bytes("text_embedding.weight", &bytes)
2770 .expect("tensor resolves"),
2771 &vec![9_u8; 32][..]
2772 );
2773 }
2774
2775 #[test]
2776 fn bf16_payload_is_byte_identical_across_the_round_trip() {
2777 let payload: Vec<u8> = (0..=255_u8).cycle().take(4096).collect();
2780 let bytes = FttsqWriter::new("qwen3-tts-12hz-0.6b-base", "b".repeat(64))
2781 .license_notice(NOTICE)
2782 .section("talker", AccessClass::HotRecurrentTalker, payload.clone())
2783 .tensor(TensorEntry {
2784 name: "talker.weight".to_owned(),
2785 section: "talker".to_owned(),
2786 dtype: StoredDtype::Bf16,
2787 shape: vec![64, 32],
2788 offset: 0,
2789 length: 4096,
2790 scales: None,
2791 })
2792 .finish()
2793 .expect("writable");
2794 let reader = FttsqReader::open(&bytes).expect("readable");
2795 assert_eq!(
2796 reader
2797 .tensor_bytes("talker.weight", &bytes)
2798 .expect("resolves"),
2799 &payload[..]
2800 );
2801 }
2802
2803 #[test]
2804 fn access_classes_drive_the_page_in_policy() {
2805 let bytes = artifact();
2806 let reader = FttsqReader::open(&bytes).expect("readable");
2807
2808 let hot = reader.sections_in_class(AccessClass::HotRecurrentMicrodecoder);
2809 assert_eq!(hot.len(), 1);
2810 assert!(hot[0].access_class.is_hot());
2811 assert!(!hot[0].access_class.is_row_granular());
2812
2813 let cold = reader.sections_in_class(AccessClass::ColdTextEmbedding);
2814 assert_eq!(cold.len(), 1);
2815 assert!(
2816 !cold[0].access_class.is_hot(),
2817 "the 622 MB embedding must never be advised resident"
2818 );
2819 assert!(
2820 cold[0].access_class.is_row_granular(),
2821 "the cold embedding is accessed a row at a time, never as a unit"
2822 );
2823 }
2824
2825 #[test]
2826 fn a_newer_format_version_is_refused_rather_than_guessed_at() {
2827 let mut bytes = artifact();
2828 bytes[8..12].copy_from_slice(&(FORMAT_VERSION + 1).to_le_bytes());
2829 let error = FttsqReader::parse_directory(&bytes).expect_err("must refuse");
2830 assert_eq!(
2831 error,
2832 FttsqError::UnsupportedVersion {
2833 found: FORMAT_VERSION + 1,
2834 supported: FORMAT_VERSION,
2835 }
2836 );
2837 }
2838
2839 #[test]
2840 fn bad_magic_and_truncation_are_named_refusals() {
2841 assert!(matches!(
2842 FttsqReader::parse_directory(&[]),
2843 Err(FttsqError::TooShort { .. })
2844 ));
2845 let mut bytes = artifact();
2846 bytes[0] = b'X';
2847 assert!(matches!(
2848 FttsqReader::parse_directory(&bytes),
2849 Err(FttsqError::BadMagic { .. })
2850 ));
2851 }
2852
2853 #[test]
2854 fn a_truncated_file_never_yields_a_partial_load() {
2855 let full = artifact();
2856 for cut in [full.len() - 1, full.len() - 40, full.len() - 90] {
2858 let error = FttsqReader::open(&full[..cut]).expect_err("truncation must be refused");
2859 assert!(
2860 matches!(
2861 error,
2862 FttsqError::RangeOutOfBounds { .. } | FttsqError::DirectoryLength { .. }
2863 ),
2864 "unexpected error for cut at {cut}: {error}"
2865 );
2866 }
2867 }
2868
2869 #[test]
2870 fn a_single_flipped_payload_bit_fails_digest_verification() {
2871 let mut bytes = artifact();
2872 let last = bytes.len() - 1;
2873 bytes[last] ^= 0x01;
2874 let error = FttsqReader::open(&bytes).expect_err("a bit flip must be caught");
2875 assert!(
2876 matches!(
2877 &error,
2878 FttsqError::DigestMismatch { section, .. } if section == "text_embedding"
2879 ),
2880 "expected a digest mismatch for text_embedding, got {error}"
2881 );
2882 assert!(FttsqReader::parse_directory(&bytes).is_ok());
2884 }
2885
2886 #[test]
2887 fn a_hostile_directory_length_cannot_provoke_a_huge_read() {
2888 let mut bytes = artifact();
2889 bytes[12..20].copy_from_slice(&u64::MAX.to_le_bytes());
2890 let error = FttsqReader::parse_directory(&bytes).expect_err("must refuse");
2891 assert!(matches!(error, FttsqError::DirectoryLength { .. }));
2892 }
2893
2894 #[test]
2896 fn structural_violations_are_each_refused_by_name() {
2897 type StructuralCase = (&'static str, Value, fn(&FttsqError) -> bool);
2898 let cases: Vec<StructuralCase> = vec![
2899 (
2900 "overlapping sections",
2901 json!([
2902 {"name": "a", "access_class": "METADATA", "offset": 100, "length": 50, "sha256": "x"},
2903 {"name": "b", "access_class": "METADATA", "offset": 120, "length": 10, "sha256": "x"},
2904 ]),
2905 |e| matches!(e, FttsqError::SectionOverlap { .. }),
2906 ),
2907 (
2908 "a section running past the file",
2909 json!([
2910 {"name": "a", "access_class": "METADATA", "offset": 100, "length": u64::MAX, "sha256": "x"},
2911 ]),
2912 |e| matches!(e, FttsqError::RangeOutOfBounds { .. }),
2913 ),
2914 (
2915 "a duplicate section name",
2916 json!([
2917 {"name": "a", "access_class": "METADATA", "offset": 100, "length": 10, "sha256": "x"},
2918 {"name": "a", "access_class": "METADATA", "offset": 200, "length": 10, "sha256": "x"},
2919 ]),
2920 |e| matches!(e, FttsqError::DuplicateName { .. }),
2921 ),
2922 (
2923 "an unknown access class",
2924 json!([
2925 {"name": "a", "access_class": "PROBABLY_HOT", "offset": 100, "length": 10, "sha256": "x"},
2926 ]),
2927 |e| matches!(e, FttsqError::UnknownValue { .. }),
2928 ),
2929 ];
2930
2931 for (description, sections, matches_expected) in cases {
2932 let error = parse_sections(Some(§ions), 4096)
2933 .expect_err(&format!("`{description}` must be refused"));
2934 assert!(
2935 matches_expected(&error),
2936 "`{description}` produced the wrong error: {error}"
2937 );
2938 }
2939 }
2940
2941 #[test]
2942 fn a_tensor_whose_length_disagrees_with_its_shape_is_refused() {
2943 let sections = vec![SectionEntry {
2944 name: "s".to_owned(),
2945 access_class: AccessClass::Metadata,
2946 offset: 0,
2947 length: 4096,
2948 sha256: String::new(),
2949 }];
2950 let index: BTreeMap<String, usize> = [("s".to_owned(), 0)].into_iter().collect();
2951
2952 let tensors = json!([
2954 {"name": "t", "section": "s", "dtype": "bf16", "shape": [8, 8], "offset": 0, "length": 64},
2955 ]);
2956 let error = parse_tensors(Some(&tensors), §ions, &index).expect_err("must refuse");
2957 assert_eq!(
2958 error,
2959 FttsqError::LengthMismatch {
2960 tensor: "t".to_owned(),
2961 declared: 64,
2962 implied: 128,
2963 }
2964 );
2965 }
2966
2967 #[test]
2968 fn tensors_may_not_overlap_within_a_section() {
2969 let sections = vec![SectionEntry {
2970 name: "s".to_owned(),
2971 access_class: AccessClass::Metadata,
2972 offset: 0,
2973 length: 4096,
2974 sha256: String::new(),
2975 }];
2976 let index: BTreeMap<String, usize> = [("s".to_owned(), 0)].into_iter().collect();
2977 let tensors = json!([
2978 {"name": "a", "section": "s", "dtype": "q8", "shape": [64], "offset": 0, "length": 64},
2979 {"name": "b", "section": "s", "dtype": "q8", "shape": [64], "offset": 32, "length": 64},
2980 ]);
2981 let error = parse_tensors(Some(&tensors), §ions, &index).expect_err("must refuse");
2982 assert!(matches!(error, FttsqError::TensorOverlap { .. }), "{error}");
2983 }
2984
2985 #[test]
2986 fn a_tensor_leaving_its_section_is_refused() {
2987 let sections = vec![SectionEntry {
2988 name: "s".to_owned(),
2989 access_class: AccessClass::Metadata,
2990 offset: 0,
2991 length: 64,
2992 sha256: String::new(),
2993 }];
2994 let index: BTreeMap<String, usize> = [("s".to_owned(), 0)].into_iter().collect();
2995 let tensors = json!([
2996 {"name": "a", "section": "s", "dtype": "q8", "shape": [64], "offset": 32, "length": 64},
2997 ]);
2998 let error = parse_tensors(Some(&tensors), §ions, &index).expect_err("must refuse");
2999 assert!(
3000 matches!(error, FttsqError::RangeOutOfBounds { .. }),
3001 "{error}"
3002 );
3003 }
3004
3005 #[test]
3006 fn an_artifact_without_a_license_notice_cannot_be_written_or_read() {
3007 let error = FttsqWriter::new("qwen3-tts-12hz-0.6b-base", "c".repeat(64))
3009 .section("m", AccessClass::Metadata, vec![1, 2, 3])
3010 .finish()
3011 .expect_err("Apache-2.0 §4 makes the notice mandatory");
3012 assert_eq!(error, FttsqError::LicenseNoticeMissing);
3013
3014 let mut bytes = artifact();
3016 let directory_len = u64::from_le_bytes(bytes[12..20].try_into().expect("header length"));
3017 let directory_start = HEADER_PREFIX_BYTES as usize;
3018 let directory_end = directory_start + directory_len as usize;
3019 let mut directory: Value = serde_json::from_slice(&bytes[directory_start..directory_end])
3020 .expect("fixture directory");
3021 directory["license_notice"] = Value::String(String::new());
3022 let mut replacement = serde_json::to_vec(&directory).expect("serializes directory");
3023 assert!(
3024 replacement.len() <= directory_len as usize,
3025 "removing a notice cannot grow it"
3026 );
3027 replacement.resize(directory_len as usize, b' ');
3028 bytes[directory_start..directory_end].copy_from_slice(&replacement);
3029 assert_eq!(
3030 FttsqReader::open(&bytes).expect_err("must refuse a missing notice"),
3031 FttsqError::LicenseNoticeMissing
3032 );
3033 }
3034
3035 #[test]
3036 fn write_to_path_lands_a_complete_readable_artifact_and_leaves_no_temporary() {
3037 let dir = std::env::temp_dir().join(format!("ftts-fttsq-write-{}", std::process::id()));
3038 std::fs::create_dir_all(&dir).expect("scratch dir");
3039 let path = dir.join("model.fttsq");
3040
3041 FttsqWriter::new("qwen3-tts-12hz-0.6b-base", "d".repeat(64))
3042 .license_notice(NOTICE)
3043 .section("m", AccessClass::HotRecurrentMicrodecoder, vec![3_u8; 128])
3044 .section(
3045 "embedding",
3046 AccessClass::ColdTextEmbedding,
3047 vec![9_u8; 8192],
3048 )
3049 .tensor(TensorEntry {
3050 name: "m.w".to_owned(),
3051 section: "m".to_owned(),
3052 dtype: StoredDtype::Q8,
3053 shape: vec![128],
3054 offset: 0,
3055 length: 128,
3056 scales: None,
3057 })
3058 .tensor(TensorEntry {
3059 name: "embedding.one_row".to_owned(),
3060 section: "embedding".to_owned(),
3061 dtype: StoredDtype::Q8,
3062 shape: vec![32],
3063 offset: 4096,
3064 length: 32,
3065 scales: None,
3066 })
3067 .write_to_path(&path)
3068 .expect("artifact is writable");
3069
3070 let bytes = std::fs::read(&path).expect("artifact is readable");
3071 let reader = FttsqReader::open(&bytes).expect("what landed on disk must verify");
3072 assert_eq!(
3073 reader.tensor_bytes("m.w", &bytes).expect("resolves"),
3074 &vec![3_u8; 128][..]
3075 );
3076
3077 let mapped = MappedFttsq::open(&path).expect("mapped artifact validates");
3078 assert_eq!(mapped.len(), bytes.len());
3079 assert_eq!(
3080 mapped
3081 .tensor_bytes("embedding.one_row")
3082 .expect("row range resolves without copying the section"),
3083 &vec![9_u8; 32][..]
3084 );
3085
3086 let micro = mapped
3087 .page_advice()
3088 .iter()
3089 .find(|application| application.section == "m")
3090 .expect("microdecoder application is recorded");
3091 assert_eq!(micro.policy, PagePolicy::Resident);
3092 assert_eq!(micro.requested, Some(MemoryAdvice::WillNeed));
3093 assert!(
3094 !matches!(micro.outcome, PageAdviceOutcome::Failed(_)),
3095 "a valid mapped microdecoder section must receive a usable advice result: {micro:?}"
3096 );
3097
3098 let embedding = mapped
3099 .page_advice()
3100 .iter()
3101 .find(|application| application.section == "embedding")
3102 .expect("embedding application is recorded");
3103 assert_eq!(embedding.policy, PagePolicy::LazyRowGranular);
3104 assert_eq!(embedding.requested, Some(MemoryAdvice::Random));
3105 assert!(
3106 !embedding.policy.may_prefetch(),
3107 "the cold embedding policy must make wholesale prefetch impossible"
3108 );
3109 for observation in [&embedding.residency_before, &embedding.residency_after] {
3110 match observation {
3111 PageResidencyOutcome::Measured {
3112 resident_pages,
3113 total_pages,
3114 } => assert!(
3115 resident_pages <= total_pages,
3116 "the OQ-18 residency measurement exceeded the section's page span"
3117 ),
3118 PageResidencyOutcome::Unsupported => {}
3119 PageResidencyOutcome::Failed(detail) => {
3120 panic!("the cold embedding residency measurement failed: {detail}");
3121 }
3122 }
3123 }
3124 assert!(
3125 mapped.page_advice().iter().all(|application| {
3126 application.policy.may_prefetch()
3127 || application.requested != Some(MemoryAdvice::WillNeed)
3128 }),
3129 "a non-prefetch section was routed to MADV_WILLNEED"
3130 );
3131
3132 let strays: Vec<_> = std::fs::read_dir(&dir)
3134 .expect("dir is listable")
3135 .filter_map(Result::ok)
3136 .map(|entry| entry.file_name().to_string_lossy().into_owned())
3137 .filter(|name| name.contains(".tmp."))
3138 .collect();
3139 assert!(strays.is_empty(), "temporary files left behind: {strays:?}");
3140
3141 std::fs::remove_file(&path).expect("cleanup");
3142 }
3143
3144 #[test]
3145 fn write_to_path_refuses_before_touching_the_filesystem_when_the_notice_is_missing() {
3146 let dir = std::env::temp_dir().join(format!("ftts-fttsq-refuse-{}", std::process::id()));
3147 std::fs::create_dir_all(&dir).expect("scratch dir");
3148 let path = dir.join("model.fttsq");
3149
3150 let error = FttsqWriter::new("qwen3-tts-12hz-0.6b-base", "e".repeat(64))
3151 .section("m", AccessClass::Metadata, vec![1, 2, 3])
3152 .write_to_path(&path)
3153 .expect_err("a notice-less artifact must never reach disk");
3154 assert_eq!(error, FttsqError::LicenseNoticeMissing);
3155 assert!(
3156 !path.exists(),
3157 "a refused artifact must not leave a file behind"
3158 );
3159 }
3160
3161 #[test]
3163 fn the_cold_text_embedding_is_never_prefetched_and_hot_classes_always_are() {
3164 assert_eq!(
3165 AccessClass::ColdTextEmbedding.page_policy(),
3166 PagePolicy::LazyRowGranular
3167 );
3168 assert!(
3169 !AccessClass::ColdTextEmbedding.page_policy().may_prefetch(),
3170 "MADV_WILLNEED over the ~622 MB embedding would evict the microdecoder pack"
3171 );
3172
3173 for hot in [
3174 AccessClass::HotRecurrentMicrodecoder,
3175 AccessClass::HotRecurrentTalker,
3176 AccessClass::HotCodecDecoder,
3177 ] {
3178 assert_eq!(hot.page_policy(), PagePolicy::Resident);
3179 assert!(hot.page_policy().may_prefetch());
3180 }
3181 for cold in [
3182 AccessClass::EnrollmentSpeakerEncoder,
3183 AccessClass::EnrollmentCodecEncoder,
3184 AccessClass::Metadata,
3185 ] {
3186 assert_eq!(cold.page_policy(), PagePolicy::OnDemand);
3187 assert!(!cold.page_policy().may_prefetch());
3188 }
3189
3190 for class in [
3192 AccessClass::HotRecurrentMicrodecoder,
3193 AccessClass::HotRecurrentTalker,
3194 AccessClass::HotCodecDecoder,
3195 AccessClass::ColdTextEmbedding,
3196 AccessClass::EnrollmentSpeakerEncoder,
3197 AccessClass::EnrollmentCodecEncoder,
3198 AccessClass::Metadata,
3199 ] {
3200 assert_eq!(
3201 class.is_hot(),
3202 class.page_policy().may_prefetch(),
3203 "is_hot() and page_policy() disagree for {class}"
3204 );
3205 assert_eq!(
3206 class.is_row_granular(),
3207 class.page_policy() == PagePolicy::LazyRowGranular,
3208 "is_row_granular() and page_policy() disagree for {class}"
3209 );
3210 }
3211 }
3212
3213 #[test]
3214 fn the_page_in_plan_prefetches_the_microdecoder_before_the_larger_talker() {
3215 let bytes = FttsqWriter::new("qwen3-tts-12hz-0.6b-base", "f".repeat(64))
3217 .license_notice(NOTICE)
3218 .section("talker", AccessClass::HotRecurrentTalker, vec![1_u8; 400])
3219 .section("embedding", AccessClass::ColdTextEmbedding, vec![2_u8; 900])
3220 .section(
3221 "micro",
3222 AccessClass::HotRecurrentMicrodecoder,
3223 vec![3_u8; 100],
3224 )
3225 .section("meta", AccessClass::Metadata, vec![4_u8; 8])
3226 .finish()
3227 .expect("writable");
3228 let reader = FttsqReader::open(&bytes).expect("readable");
3229
3230 let plan = reader.page_in_plan();
3231 let order: Vec<&str> = plan
3232 .iter()
3233 .map(|(section, _)| section.name.as_str())
3234 .collect();
3235 assert_eq!(
3236 order,
3237 vec!["micro", "talker", "embedding", "meta"],
3238 "resident sections first, smallest first, so the 15x-reread pack wins the cache race"
3239 );
3240 assert_eq!(plan[0].1, PagePolicy::Resident);
3241 assert_eq!(plan[2].1, PagePolicy::LazyRowGranular);
3242 assert_eq!(plan[3].1, PagePolicy::OnDemand);
3243
3244 for (section, policy) in &plan {
3246 assert_eq!(
3247 policy.may_prefetch(),
3248 section.access_class.is_hot(),
3249 "section `{}` would be prefetched against policy",
3250 section.name
3251 );
3252 }
3253 }
3254
3255 fn census_fixture() -> (Vec<u8>, ArtifactManifest) {
3256 let bytes = FttsqWriter::new("qwen3-tts-12hz-0.6b-base", "g".repeat(64))
3257 .license_notice(NOTICE)
3258 .section(
3259 "micro",
3260 AccessClass::HotRecurrentMicrodecoder,
3261 vec![1_u8; 64],
3262 )
3263 .section("embedding", AccessClass::ColdTextEmbedding, vec![2_u8; 32])
3264 .tensor(TensorEntry {
3265 name: "micro.body".to_owned(),
3266 section: "micro".to_owned(),
3267 dtype: StoredDtype::Q8,
3268 shape: vec![8, 8],
3269 offset: 0,
3270 length: 64,
3271 scales: None,
3272 })
3273 .tensor(TensorEntry {
3274 name: "text_embedding.weight".to_owned(),
3275 section: "embedding".to_owned(),
3276 dtype: StoredDtype::Bf16,
3277 shape: vec![4, 4],
3278 offset: 0,
3279 length: 32,
3280 scales: None,
3281 })
3282 .finish()
3283 .expect("writable");
3284
3285 let manifest = ArtifactManifest::new("qwen3-tts pinned")
3286 .expect(ExpectedArtifactTensor {
3287 name: "micro.body".to_owned(),
3288 shape: vec![8, 8],
3289 dtype: StoredDtype::Q8,
3290 access_class: AccessClass::HotRecurrentMicrodecoder,
3291 })
3292 .expect(ExpectedArtifactTensor {
3293 name: "text_embedding.weight".to_owned(),
3294 shape: vec![4, 4],
3295 dtype: StoredDtype::Bf16,
3296 access_class: AccessClass::ColdTextEmbedding,
3297 });
3298 (bytes, manifest)
3299 }
3300
3301 #[test]
3302 fn a_matching_artifact_passes_its_census() {
3303 let (bytes, manifest) = census_fixture();
3304 let reader = FttsqReader::open(&bytes).expect("readable");
3305 let report = manifest.audit(&reader);
3306 assert!(report.is_green(), "{}", report.render());
3307 assert!(reader.verify_census(&manifest).is_ok());
3308 }
3309
3310 #[test]
3312 fn the_census_names_every_divergence_class_in_one_pass() {
3313 let (bytes, _) = census_fixture();
3314 let reader = FttsqReader::open(&bytes).expect("readable");
3315
3316 let manifest = ArtifactManifest::new("deliberately wrong")
3317 .expect(ExpectedArtifactTensor {
3319 name: "micro.body".to_owned(),
3320 shape: vec![16, 4],
3321 dtype: StoredDtype::Q4,
3322 access_class: AccessClass::HotRecurrentMicrodecoder,
3323 })
3324 .expect(ExpectedArtifactTensor {
3326 name: "text_embedding.weight".to_owned(),
3327 shape: vec![4, 4],
3328 dtype: StoredDtype::Bf16,
3329 access_class: AccessClass::HotRecurrentTalker,
3330 })
3331 .expect(ExpectedArtifactTensor {
3333 name: "codec.decoder.weight".to_owned(),
3334 shape: vec![2],
3335 dtype: StoredDtype::Q8,
3336 access_class: AccessClass::HotCodecDecoder,
3337 });
3338
3339 let report = manifest.audit(&reader);
3340 assert!(!report.is_green());
3341 assert_eq!(report.count_of("shape_mismatch"), 1, "{}", report.render());
3342 assert_eq!(report.count_of("dtype_mismatch"), 1, "{}", report.render());
3343 assert_eq!(
3344 report.count_of("wrong_access_class"),
3345 1,
3346 "a tensor in the wrong access class still produces correct audio while destroying \
3347 residency — the census is the only thing that catches it:\n{}",
3348 report.render()
3349 );
3350 assert_eq!(report.count_of("missing"), 1, "{}", report.render());
3351
3352 let rendered = report.render();
3353 for expected in [
3354 "micro.body",
3355 "text_embedding.weight",
3356 "codec.decoder.weight",
3357 "ACCESS_CLASS",
3358 "SHAPE",
3359 "DTYPE",
3360 "MISSING",
3361 ] {
3362 assert!(
3363 rendered.contains(expected),
3364 "census report is missing `{expected}`:\n{rendered}"
3365 );
3366 }
3367
3368 assert!(reader.verify_census(&manifest).is_err());
3369 }
3370
3371 #[test]
3373 fn unexpected_tensors_are_reported_as_extra() {
3374 let (bytes, _) = census_fixture();
3375 let reader = FttsqReader::open(&bytes).expect("readable");
3376 let manifest = ArtifactManifest::new("partial").expect(ExpectedArtifactTensor {
3377 name: "micro.body".to_owned(),
3378 shape: vec![8, 8],
3379 dtype: StoredDtype::Q8,
3380 access_class: AccessClass::HotRecurrentMicrodecoder,
3381 });
3382 let report = manifest.audit(&reader);
3383 assert_eq!(report.count_of("extra"), 1, "{}", report.render());
3384 assert!(report.render().contains("text_embedding.weight"));
3385 }
3386
3387 #[test]
3388 fn quantized_dtype_sizes_are_exact_including_the_odd_q4_tail() {
3389 assert_eq!(StoredDtype::Bf16.storage_bytes(10), Some(20));
3390 assert_eq!(StoredDtype::F32.storage_bytes(10), Some(40));
3391 assert_eq!(StoredDtype::Q8.storage_bytes(10), Some(10));
3392 assert_eq!(StoredDtype::Q4.storage_bytes(10), Some(5));
3394 assert_eq!(StoredDtype::Q4.storage_bytes(11), Some(6));
3395 assert_eq!(StoredDtype::F32.storage_bytes(u64::MAX), None);
3397 }
3398
3399 #[test]
3400 fn wire_strings_round_trip_for_every_enum_value() {
3401 for class in [
3402 AccessClass::HotRecurrentMicrodecoder,
3403 AccessClass::HotRecurrentTalker,
3404 AccessClass::HotCodecDecoder,
3405 AccessClass::ColdTextEmbedding,
3406 AccessClass::EnrollmentSpeakerEncoder,
3407 AccessClass::EnrollmentCodecEncoder,
3408 AccessClass::Metadata,
3409 ] {
3410 assert_eq!(AccessClass::parse(class.as_str()), Some(class));
3411 }
3412 for dtype in [
3413 StoredDtype::Bf16,
3414 StoredDtype::F32,
3415 StoredDtype::Q8,
3416 StoredDtype::Q4,
3417 ] {
3418 assert_eq!(StoredDtype::parse(dtype.as_str()), Some(dtype));
3419 }
3420 assert_eq!(AccessClass::parse("HOT_SOMETHING"), None);
3421 assert_eq!(StoredDtype::parse("f16"), None);
3422 }
3423}