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 actual = to_hex(&crate::sha256::digest(payload));
1089 if actual != section.sha256 {
1090 return Err(FttsqError::DigestMismatch {
1091 section: section.name.clone(),
1092 expected: section.sha256.clone(),
1093 actual,
1094 });
1095 }
1096 }
1097 Ok(())
1098 }
1099
1100 pub fn verify_digests_of_present(&self, bytes: &[u8]) -> Result<(), FttsqError> {
1110 for section in &self.sections {
1111 let Some(end) = section.end() else { continue };
1112 if end > bytes.len() as u64 {
1113 continue;
1114 }
1115 let payload = self.section_bytes(section, bytes)?;
1116 let actual = to_hex(&crate::sha256::digest(payload));
1117 if actual != section.sha256 {
1118 return Err(FttsqError::DigestMismatch {
1119 section: section.name.clone(),
1120 expected: section.sha256.clone(),
1121 actual,
1122 });
1123 }
1124 }
1125 Ok(())
1126 }
1127
1128 pub fn absent_sections_in_prefix(&self, available: u64) -> Result<Vec<String>, FttsqError> {
1140 let mut absent = Vec::new();
1141 for section in &self.sections {
1142 let end = section.end().ok_or_else(|| FttsqError::RangeOutOfBounds {
1143 what: format!("section `{}`", section.name),
1144 offset: section.offset,
1145 length: section.length,
1146 bound: available,
1147 })?;
1148 if end <= available {
1149 continue;
1150 }
1151 if section.offset < available {
1152 return Err(FttsqError::RangeOutOfBounds {
1153 what: format!("prefix splits section `{}`", section.name),
1154 offset: section.offset,
1155 length: section.length,
1156 bound: available,
1157 });
1158 }
1159 absent.push(section.name.clone());
1160 }
1161 Ok(absent)
1162 }
1163
1164 #[must_use]
1171 pub fn prefix_len_omitting(&self, omit: &[AccessClass]) -> Option<u64> {
1172 let omitted = |section: &SectionEntry| omit.contains(§ion.access_class);
1173 let first_omitted = self
1174 .sections
1175 .iter()
1176 .filter(|section| omitted(section))
1177 .map(|section| section.offset)
1178 .min()?;
1179 if self
1181 .sections
1182 .iter()
1183 .any(|section| !omitted(section) && section.offset >= first_omitted)
1184 {
1185 return None;
1186 }
1187 Some(first_omitted)
1188 }
1189
1190 fn section_bytes<'a>(
1191 &self,
1192 section: &SectionEntry,
1193 bytes: &'a [u8],
1194 ) -> Result<&'a [u8], FttsqError> {
1195 let end = section.end().ok_or_else(|| FttsqError::RangeOutOfBounds {
1196 what: format!("section `{}`", section.name),
1197 offset: section.offset,
1198 length: section.length,
1199 bound: bytes.len() as u64,
1200 })?;
1201 if end > bytes.len() as u64 {
1202 return Err(FttsqError::RangeOutOfBounds {
1203 what: format!("section `{}`", section.name),
1204 offset: section.offset,
1205 length: section.length,
1206 bound: bytes.len() as u64,
1207 });
1208 }
1209 Ok(&bytes[section.offset as usize..end as usize])
1210 }
1211
1212 #[must_use]
1214 pub const fn format_version(&self) -> u32 {
1215 self.format_version
1216 }
1217
1218 #[must_use]
1220 pub fn model_family(&self) -> &str {
1221 &self.model_family
1222 }
1223
1224 #[must_use]
1226 pub fn source_sha256(&self) -> &str {
1227 &self.source_sha256
1228 }
1229
1230 #[must_use]
1232 pub fn license_notice(&self) -> &str {
1233 &self.license_notice
1234 }
1235
1236 #[must_use]
1238 pub const fn model_config(&self) -> &Value {
1239 &self.model_config
1240 }
1241
1242 #[must_use]
1244 pub const fn quantization_manifest(&self) -> &Value {
1245 &self.quantization_manifest
1246 }
1247
1248 #[must_use]
1250 pub fn sections(&self) -> &[SectionEntry] {
1251 &self.sections
1252 }
1253
1254 #[must_use]
1256 pub fn tensors(&self) -> &[TensorEntry] {
1257 &self.tensors
1258 }
1259
1260 #[must_use]
1262 pub fn section(&self, name: &str) -> Option<&SectionEntry> {
1263 self.section_index
1264 .get(name)
1265 .and_then(|&index| self.sections.get(index))
1266 }
1267
1268 #[must_use]
1270 pub fn tensor(&self, name: &str) -> Option<&TensorEntry> {
1271 self.tensor_index
1272 .get(name)
1273 .and_then(|&index| self.tensors.get(index))
1274 }
1275
1276 #[must_use]
1278 pub fn sections_in_class(&self, class: AccessClass) -> Vec<&SectionEntry> {
1279 self.sections
1280 .iter()
1281 .filter(|section| section.access_class == class)
1282 .collect()
1283 }
1284
1285 pub fn tensor_bytes<'a>(&self, name: &str, bytes: &'a [u8]) -> Result<&'a [u8], FttsqError> {
1292 let tensor = self
1293 .tensor(name)
1294 .ok_or_else(|| FttsqError::UnknownSection {
1295 tensor: name.to_owned(),
1296 section: "<unknown tensor>".to_owned(),
1297 })?;
1298 let section = self
1299 .section(&tensor.section)
1300 .ok_or_else(|| FttsqError::UnknownSection {
1301 tensor: tensor.name.clone(),
1302 section: tensor.section.clone(),
1303 })?;
1304 let payload = self.section_bytes(section, bytes)?;
1305 let end = tensor.offset.checked_add(tensor.length).ok_or_else(|| {
1306 FttsqError::RangeOutOfBounds {
1307 what: format!("tensor `{}`", tensor.name),
1308 offset: tensor.offset,
1309 length: tensor.length,
1310 bound: payload.len() as u64,
1311 }
1312 })?;
1313 if end > payload.len() as u64 {
1314 return Err(FttsqError::RangeOutOfBounds {
1315 what: format!("tensor `{}`", tensor.name),
1316 offset: tensor.offset,
1317 length: tensor.length,
1318 bound: payload.len() as u64,
1319 });
1320 }
1321 Ok(&payload[tensor.offset as usize..end as usize])
1322 }
1323
1324 #[must_use]
1334 pub fn page_in_plan(&self) -> Vec<(&SectionEntry, PagePolicy)> {
1335 let mut plan: Vec<(&SectionEntry, PagePolicy)> = self
1336 .sections
1337 .iter()
1338 .map(|section| (section, section.access_class.page_policy()))
1339 .collect();
1340 plan.sort_by_key(|(section, policy)| {
1341 let rank = match policy {
1342 PagePolicy::Resident => 0_u8,
1343 PagePolicy::LazyRowGranular => 1,
1344 PagePolicy::OnDemand => 2,
1345 };
1346 (rank, section.length)
1347 });
1348 plan
1349 }
1350
1351 pub fn verify_census(&self, manifest: &ArtifactManifest) -> Result<(), Box<ArtifactCensus>> {
1357 let report = manifest.audit(self);
1358 if report.is_green() {
1359 Ok(())
1360 } else {
1361 Err(Box::new(report))
1362 }
1363 }
1364}
1365
1366#[derive(Clone, Debug, PartialEq, Eq)]
1376pub struct ExpectedArtifactTensor {
1377 pub name: String,
1379 pub shape: Vec<u64>,
1381 pub dtype: StoredDtype,
1383 pub access_class: AccessClass,
1385}
1386
1387#[derive(Clone, Debug, PartialEq, Eq)]
1389pub enum ArtifactFinding {
1390 Missing {
1392 name: String,
1394 },
1395 Extra {
1398 name: String,
1400 },
1401 ShapeMismatch {
1403 name: String,
1405 expected: Vec<u64>,
1407 found: Vec<u64>,
1409 },
1410 DtypeMismatch {
1412 name: String,
1414 expected: StoredDtype,
1416 found: StoredDtype,
1418 },
1419 WrongAccessClass {
1423 name: String,
1425 expected: AccessClass,
1427 found: AccessClass,
1429 },
1430 DanglingSection {
1432 name: String,
1434 section: String,
1436 },
1437}
1438
1439impl ArtifactFinding {
1440 #[must_use]
1442 pub fn tensor(&self) -> &str {
1443 match self {
1444 Self::Missing { name }
1445 | Self::Extra { name }
1446 | Self::ShapeMismatch { name, .. }
1447 | Self::DtypeMismatch { name, .. }
1448 | Self::WrongAccessClass { name, .. }
1449 | Self::DanglingSection { name, .. } => name,
1450 }
1451 }
1452
1453 #[must_use]
1455 pub const fn class(&self) -> &'static str {
1456 match self {
1457 Self::Missing { .. } => "missing",
1458 Self::Extra { .. } => "extra",
1459 Self::ShapeMismatch { .. } => "shape_mismatch",
1460 Self::DtypeMismatch { .. } => "dtype_mismatch",
1461 Self::WrongAccessClass { .. } => "wrong_access_class",
1462 Self::DanglingSection { .. } => "dangling_section",
1463 }
1464 }
1465}
1466
1467impl fmt::Display for ArtifactFinding {
1468 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1469 match self {
1470 Self::Missing { name } => write!(f, "MISSING {name}"),
1471 Self::Extra { name } => write!(f, "EXTRA {name}"),
1472 Self::ShapeMismatch {
1473 name,
1474 expected,
1475 found,
1476 } => write!(
1477 f,
1478 "SHAPE {name}: expected {expected:?}, found {found:?}"
1479 ),
1480 Self::DtypeMismatch {
1481 name,
1482 expected,
1483 found,
1484 } => write!(
1485 f,
1486 "DTYPE {name}: expected {expected}, found {found}"
1487 ),
1488 Self::WrongAccessClass {
1489 name,
1490 expected,
1491 found,
1492 } => write!(
1493 f,
1494 "ACCESS_CLASS {name}: expected {expected}, found {found}"
1495 ),
1496 Self::DanglingSection { name, section } => {
1497 write!(
1498 f,
1499 "DANGLING {name}: names undeclared section `{section}`"
1500 )
1501 }
1502 }
1503 }
1504}
1505
1506#[derive(Clone, Debug, Default)]
1512pub struct ArtifactManifest {
1513 label: String,
1514 expected: Vec<ExpectedArtifactTensor>,
1515}
1516
1517impl ArtifactManifest {
1518 #[must_use]
1520 pub fn new(label: impl Into<String>) -> Self {
1521 Self {
1522 label: label.into(),
1523 expected: Vec::new(),
1524 }
1525 }
1526
1527 #[must_use]
1529 pub fn expect(mut self, tensor: ExpectedArtifactTensor) -> Self {
1530 self.expected.push(tensor);
1531 self
1532 }
1533
1534 #[must_use]
1536 pub fn label(&self) -> &str {
1537 &self.label
1538 }
1539
1540 #[must_use]
1542 pub fn len(&self) -> usize {
1543 self.expected.len()
1544 }
1545
1546 #[must_use]
1548 pub fn is_empty(&self) -> bool {
1549 self.expected.is_empty()
1550 }
1551
1552 #[must_use]
1558 pub fn audit(&self, reader: &FttsqReader) -> ArtifactCensus {
1559 let mut findings = Vec::new();
1560 let expected_names: BTreeMap<&str, &ExpectedArtifactTensor> = self
1561 .expected
1562 .iter()
1563 .map(|tensor| (tensor.name.as_str(), tensor))
1564 .collect();
1565
1566 for expectation in &self.expected {
1567 let Some(found) = reader.tensor(&expectation.name) else {
1568 findings.push(ArtifactFinding::Missing {
1569 name: expectation.name.clone(),
1570 });
1571 continue;
1572 };
1573 if found.shape != expectation.shape {
1574 findings.push(ArtifactFinding::ShapeMismatch {
1575 name: expectation.name.clone(),
1576 expected: expectation.shape.clone(),
1577 found: found.shape.clone(),
1578 });
1579 }
1580 if found.dtype != expectation.dtype {
1581 findings.push(ArtifactFinding::DtypeMismatch {
1582 name: expectation.name.clone(),
1583 expected: expectation.dtype,
1584 found: found.dtype,
1585 });
1586 }
1587 match reader.section(&found.section) {
1588 Some(section) if section.access_class != expectation.access_class => {
1589 findings.push(ArtifactFinding::WrongAccessClass {
1590 name: expectation.name.clone(),
1591 expected: expectation.access_class,
1592 found: section.access_class,
1593 });
1594 }
1595 Some(_) => {}
1596 None => findings.push(ArtifactFinding::DanglingSection {
1597 name: expectation.name.clone(),
1598 section: found.section.clone(),
1599 }),
1600 }
1601 }
1602
1603 for tensor in reader.tensors() {
1604 if !expected_names.contains_key(tensor.name.as_str()) {
1605 findings.push(ArtifactFinding::Extra {
1606 name: tensor.name.clone(),
1607 });
1608 }
1609 }
1610
1611 ArtifactCensus {
1612 label: self.label.clone(),
1613 expected: self.expected.len(),
1614 found: reader.tensors().len(),
1615 findings,
1616 }
1617 }
1618}
1619
1620#[derive(Clone, Debug)]
1622pub struct ArtifactCensus {
1623 label: String,
1624 expected: usize,
1625 found: usize,
1626 findings: Vec<ArtifactFinding>,
1627}
1628
1629impl ArtifactCensus {
1630 #[must_use]
1632 pub fn is_green(&self) -> bool {
1633 self.findings.is_empty()
1634 }
1635
1636 #[must_use]
1638 pub fn findings(&self) -> &[ArtifactFinding] {
1639 &self.findings
1640 }
1641
1642 #[must_use]
1644 pub fn count_of(&self, class: &str) -> usize {
1645 self.findings
1646 .iter()
1647 .filter(|finding| finding.class() == class)
1648 .count()
1649 }
1650
1651 #[must_use]
1653 pub fn render(&self) -> String {
1654 let mut out = format!(
1655 "artifact census `{}`: expected {} tensors, artifact declares {} — {}\n",
1656 self.label,
1657 self.expected,
1658 self.found,
1659 if self.is_green() {
1660 "GREEN".to_owned()
1661 } else {
1662 format!("{} FINDINGS", self.findings.len())
1663 }
1664 );
1665 for finding in &self.findings {
1666 out.push_str(&format!(" {finding}\n"));
1667 }
1668 out
1669 }
1670}
1671
1672impl fmt::Display for ArtifactCensus {
1673 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1674 f.write_str(&self.render())
1675 }
1676}
1677
1678impl std::error::Error for ArtifactCensus {}
1679
1680fn required_str<'a>(value: Option<&'a Value>, path: &str) -> Result<&'a str, FttsqError> {
1681 value
1682 .and_then(Value::as_str)
1683 .filter(|text| !text.is_empty())
1684 .ok_or_else(|| FttsqError::Field {
1685 path: path.to_owned(),
1686 expected: "a non-empty string".to_owned(),
1687 })
1688}
1689
1690fn required_u64(value: Option<&Value>, path: &str) -> Result<u64, FttsqError> {
1691 value
1692 .and_then(Value::as_u64)
1693 .ok_or_else(|| FttsqError::Field {
1694 path: path.to_owned(),
1695 expected: "a non-negative integer".to_owned(),
1696 })
1697}
1698
1699fn parse_sections(value: Option<&Value>, file_len: u64) -> Result<Vec<SectionEntry>, FttsqError> {
1700 let array = value
1701 .and_then(Value::as_array)
1702 .ok_or_else(|| FttsqError::Field {
1703 path: "sections".to_owned(),
1704 expected: "an array".to_owned(),
1705 })?;
1706 if array.len() > MAX_SECTIONS {
1707 return Err(FttsqError::LimitExceeded {
1708 what: "section".to_owned(),
1709 found: array.len() as u64,
1710 limit: MAX_SECTIONS as u64,
1711 });
1712 }
1713
1714 let mut sections = Vec::with_capacity(array.len());
1715 let mut seen: BTreeMap<String, ()> = BTreeMap::new();
1716 for (index, entry) in array.iter().enumerate() {
1717 let path = |field: &str| format!("sections[{index}].{field}");
1718 let name = required_str(entry.get("name"), &path("name"))?.to_owned();
1719 if seen.insert(name.clone(), ()).is_some() {
1720 return Err(FttsqError::DuplicateName {
1721 what: "section".to_owned(),
1722 name,
1723 });
1724 }
1725 let class_text = required_str(entry.get("access_class"), &path("access_class"))?;
1726 let access_class =
1727 AccessClass::parse(class_text).ok_or_else(|| FttsqError::UnknownValue {
1728 path: path("access_class"),
1729 found: class_text.to_owned(),
1730 })?;
1731 let offset = required_u64(entry.get("offset"), &path("offset"))?;
1732 let length = required_u64(entry.get("length"), &path("length"))?;
1733 let sha256 = required_str(entry.get("sha256"), &path("sha256"))?.to_owned();
1734
1735 let end = offset
1736 .checked_add(length)
1737 .ok_or_else(|| FttsqError::RangeOutOfBounds {
1738 what: format!("section `{name}`"),
1739 offset,
1740 length,
1741 bound: file_len,
1742 })?;
1743 if end > file_len {
1744 return Err(FttsqError::RangeOutOfBounds {
1745 what: format!("section `{name}`"),
1746 offset,
1747 length,
1748 bound: file_len,
1749 });
1750 }
1751
1752 sections.push(SectionEntry {
1753 name,
1754 access_class,
1755 offset,
1756 length,
1757 sha256,
1758 });
1759 }
1760
1761 let mut ordered: Vec<&SectionEntry> = sections.iter().collect();
1763 ordered.sort_by_key(|section| section.offset);
1764 for pair in ordered.windows(2) {
1765 let (first, second) = (pair[0], pair[1]);
1766 let first_end = first.end().unwrap_or(u64::MAX);
1767 if first_end > second.offset {
1768 return Err(FttsqError::SectionOverlap {
1769 first: first.name.clone(),
1770 second: second.name.clone(),
1771 });
1772 }
1773 }
1774
1775 Ok(sections)
1776}
1777
1778fn parse_tensors(
1779 value: Option<&Value>,
1780 sections: &[SectionEntry],
1781 section_index: &BTreeMap<String, usize>,
1782) -> Result<Vec<TensorEntry>, FttsqError> {
1783 let array = value
1784 .and_then(Value::as_array)
1785 .ok_or_else(|| FttsqError::Field {
1786 path: "tensors".to_owned(),
1787 expected: "an array".to_owned(),
1788 })?;
1789 if array.len() > MAX_TENSORS {
1790 return Err(FttsqError::LimitExceeded {
1791 what: "tensor".to_owned(),
1792 found: array.len() as u64,
1793 limit: MAX_TENSORS as u64,
1794 });
1795 }
1796
1797 let mut tensors = Vec::with_capacity(array.len());
1798 let mut seen: BTreeMap<String, ()> = BTreeMap::new();
1799 for (index, entry) in array.iter().enumerate() {
1800 let path = |field: &str| format!("tensors[{index}].{field}");
1801 let name = required_str(entry.get("name"), &path("name"))?.to_owned();
1802 if seen.insert(name.clone(), ()).is_some() {
1803 return Err(FttsqError::DuplicateName {
1804 what: "tensor".to_owned(),
1805 name,
1806 });
1807 }
1808 let section = required_str(entry.get("section"), &path("section"))?.to_owned();
1809 let dtype_text = required_str(entry.get("dtype"), &path("dtype"))?;
1810 let dtype = StoredDtype::parse(dtype_text).ok_or_else(|| FttsqError::UnknownValue {
1811 path: path("dtype"),
1812 found: dtype_text.to_owned(),
1813 })?;
1814
1815 let shape_array = entry
1816 .get("shape")
1817 .and_then(Value::as_array)
1818 .ok_or_else(|| FttsqError::Field {
1819 path: path("shape"),
1820 expected: "an array".to_owned(),
1821 })?;
1822 if shape_array.len() > MAX_RANK {
1823 return Err(FttsqError::LimitExceeded {
1824 what: format!("tensor `{name}` rank"),
1825 found: shape_array.len() as u64,
1826 limit: MAX_RANK as u64,
1827 });
1828 }
1829 let mut shape = Vec::with_capacity(shape_array.len());
1830 for (axis, dim) in shape_array.iter().enumerate() {
1831 let dim = dim.as_u64().ok_or_else(|| FttsqError::Field {
1832 path: format!("{}[{axis}]", path("shape")),
1833 expected: "a non-negative integer".to_owned(),
1834 })?;
1835 if dim > MAX_DIM {
1836 return Err(FttsqError::LimitExceeded {
1837 what: format!("tensor `{name}` dimension {axis}"),
1838 found: dim,
1839 limit: MAX_DIM,
1840 });
1841 }
1842 shape.push(dim);
1843 }
1844
1845 let offset = required_u64(entry.get("offset"), &path("offset"))?;
1846 let length = required_u64(entry.get("length"), &path("length"))?;
1847 let scales = entry
1848 .get("scales")
1849 .and_then(Value::as_str)
1850 .map(str::to_owned);
1851
1852 let tensor = TensorEntry {
1853 name,
1854 section,
1855 dtype,
1856 shape,
1857 offset,
1858 length,
1859 scales,
1860 };
1861
1862 let elements = tensor.elements().ok_or_else(|| FttsqError::LimitExceeded {
1866 what: format!("tensor `{}` element count", tensor.name),
1867 found: u64::MAX,
1868 limit: MAX_DIM,
1869 })?;
1870 let implied = dtype
1871 .storage_bytes(elements)
1872 .ok_or_else(|| FttsqError::LimitExceeded {
1873 what: format!("tensor `{}` storage size", tensor.name),
1874 found: u64::MAX,
1875 limit: MAX_DIM,
1876 })?;
1877 if implied != tensor.length {
1878 return Err(FttsqError::LengthMismatch {
1879 tensor: tensor.name.clone(),
1880 declared: tensor.length,
1881 implied,
1882 });
1883 }
1884
1885 let owner = section_index
1886 .get(&tensor.section)
1887 .and_then(|&index| sections.get(index))
1888 .ok_or_else(|| FttsqError::UnknownSection {
1889 tensor: tensor.name.clone(),
1890 section: tensor.section.clone(),
1891 })?;
1892 let end = tensor.offset.checked_add(tensor.length).ok_or_else(|| {
1893 FttsqError::RangeOutOfBounds {
1894 what: format!("tensor `{}`", tensor.name),
1895 offset: tensor.offset,
1896 length: tensor.length,
1897 bound: owner.length,
1898 }
1899 })?;
1900 if end > owner.length {
1901 return Err(FttsqError::RangeOutOfBounds {
1902 what: format!("tensor `{}`", tensor.name),
1903 offset: tensor.offset,
1904 length: tensor.length,
1905 bound: owner.length,
1906 });
1907 }
1908
1909 tensors.push(tensor);
1910 }
1911
1912 let mut by_section: BTreeMap<&str, Vec<&TensorEntry>> = BTreeMap::new();
1915 for tensor in &tensors {
1916 by_section
1917 .entry(tensor.section.as_str())
1918 .or_default()
1919 .push(tensor);
1920 }
1921 for group in by_section.values_mut() {
1922 group.sort_by_key(|tensor| tensor.offset);
1923 for pair in group.windows(2) {
1924 let (first, second) = (pair[0], pair[1]);
1925 let first_end = first.offset.saturating_add(first.length);
1926 if first_end > second.offset {
1927 return Err(FttsqError::TensorOverlap {
1928 first: first.name.clone(),
1929 second: second.name.clone(),
1930 });
1931 }
1932 }
1933 }
1934
1935 Ok(tensors)
1936}
1937
1938#[derive(Debug)]
1946pub struct FttsqStreamPlan {
1947 model_family: String,
1948 source_sha256: String,
1949 license_notice: String,
1950 model_config: Value,
1951 quantization_manifest: Value,
1952 sections: Vec<(String, AccessClass, u64)>,
1953 tensors: Vec<TensorEntry>,
1954}
1955
1956impl FttsqStreamPlan {
1957 #[must_use]
1959 pub fn new(model_family: impl Into<String>, source_sha256: impl Into<String>) -> Self {
1960 Self {
1961 model_family: model_family.into(),
1962 source_sha256: source_sha256.into(),
1963 license_notice: String::new(),
1964 model_config: Value::Null,
1965 quantization_manifest: Value::Null,
1966 sections: Vec::new(),
1967 tensors: Vec::new(),
1968 }
1969 }
1970
1971 #[must_use]
1973 pub fn license_notice(mut self, notice: impl Into<String>) -> Self {
1974 self.license_notice = notice.into();
1975 self
1976 }
1977
1978 #[must_use]
1980 pub fn model_config(mut self, config: Value) -> Self {
1981 self.model_config = config;
1982 self
1983 }
1984
1985 #[must_use]
1987 pub fn quantization_manifest(mut self, manifest: Value) -> Self {
1988 self.quantization_manifest = manifest;
1989 self
1990 }
1991
1992 #[must_use]
1994 pub fn section(
1995 mut self,
1996 name: impl Into<String>,
1997 access_class: AccessClass,
1998 length: u64,
1999 ) -> Self {
2000 self.sections.push((name.into(), access_class, length));
2001 self
2002 }
2003
2004 #[must_use]
2006 pub fn tensor(mut self, tensor: TensorEntry) -> Self {
2007 self.tensors.push(tensor);
2008 self
2009 }
2010
2011 pub fn begin<W: std::io::Write + std::io::Seek>(
2023 self,
2024 mut writer: W,
2025 ) -> Result<FttsqStreamingWriter<W>, FttsqError> {
2026 if self.license_notice.trim().is_empty() {
2027 return Err(FttsqError::LicenseNoticeMissing);
2028 }
2029
2030 let mut sections: Vec<SectionEntry> = self
2034 .sections
2035 .into_iter()
2036 .map(|(name, access_class, length)| SectionEntry {
2037 name,
2038 access_class,
2039 offset: 0,
2040 length,
2041 sha256: "0".repeat(64),
2042 })
2043 .collect();
2044 let mut probe_sections = sections.clone();
2045 for section in &mut probe_sections {
2046 section.offset = u64::MAX;
2049 }
2050 let probe = stream_directory_json(
2051 &self.model_family,
2052 &self.source_sha256,
2053 &self.license_notice,
2054 &self.model_config,
2055 &self.quantization_manifest,
2056 &probe_sections,
2057 &self.tensors,
2058 );
2059 let directory_len = serde_json::to_vec(&probe)
2060 .map_err(|error| FttsqError::DirectoryMalformed {
2061 detail: error.to_string(),
2062 })?
2063 .len() as u64;
2064 if directory_len > MAX_DIRECTORY_BYTES {
2065 return Err(FttsqError::DirectoryLength {
2066 declared: directory_len,
2067 limit: MAX_DIRECTORY_BYTES,
2068 });
2069 }
2070 let payload_start =
2071 HEADER_PREFIX_BYTES
2072 .checked_add(directory_len)
2073 .ok_or(FttsqError::DirectoryLength {
2074 declared: directory_len,
2075 limit: u64::MAX,
2076 })?;
2077 let final_file_len = layout_stream_sections(&mut sections, payload_start)?;
2078
2079 let directory = stream_directory_json(
2080 &self.model_family,
2081 &self.source_sha256,
2082 &self.license_notice,
2083 &self.model_config,
2084 &self.quantization_manifest,
2085 §ions,
2086 &self.tensors,
2087 );
2088 let mut directory_bytes =
2089 serde_json::to_vec(&directory).map_err(|error| FttsqError::DirectoryMalformed {
2090 detail: error.to_string(),
2091 })?;
2092 if directory_bytes.len() as u64 > directory_len {
2093 return Err(FttsqError::DirectoryLength {
2094 declared: directory_bytes.len() as u64,
2095 limit: directory_len,
2096 });
2097 }
2098 directory_bytes.resize(directory_len as usize, b' ');
2099
2100 let mut header_and_directory = Vec::with_capacity(
2101 (HEADER_PREFIX_BYTES as usize).saturating_add(directory_bytes.len()),
2102 );
2103 header_and_directory.extend_from_slice(MAGIC);
2104 header_and_directory.extend_from_slice(&FORMAT_VERSION.to_le_bytes());
2105 header_and_directory.extend_from_slice(&directory_len.to_le_bytes());
2106 header_and_directory.extend_from_slice(&directory_bytes);
2107
2108 FttsqReader::parse_directory_for_file_len(&header_and_directory, final_file_len)?;
2111 writer
2112 .write_all(&header_and_directory)
2113 .map_err(|error| stream_io_error("write header and directory", &error))?;
2114
2115 let mut streaming = FttsqStreamingWriter {
2116 writer,
2117 model_family: self.model_family,
2118 source_sha256: self.source_sha256,
2119 license_notice: self.license_notice,
2120 model_config: self.model_config,
2121 quantization_manifest: self.quantization_manifest,
2122 sections,
2123 tensors: self.tensors,
2124 directory_len,
2125 current_section: 0,
2126 section_written: 0,
2127 section_hasher: Sha256::new(),
2128 };
2129 streaming.finalize_empty_sections();
2130 Ok(streaming)
2131 }
2132}
2133
2134#[derive(Debug)]
2141pub struct FttsqStreamingWriter<W> {
2142 writer: W,
2143 model_family: String,
2144 source_sha256: String,
2145 license_notice: String,
2146 model_config: Value,
2147 quantization_manifest: Value,
2148 sections: Vec<SectionEntry>,
2149 tensors: Vec<TensorEntry>,
2150 directory_len: u64,
2151 current_section: usize,
2152 section_written: u64,
2153 section_hasher: Sha256,
2154}
2155
2156impl<W: std::io::Write + std::io::Seek> FttsqStreamingWriter<W> {
2157 pub fn write_section(&mut self, section: &str, bytes: &[u8]) -> Result<(), FttsqError> {
2168 let Some(entry) = self.sections.get(self.current_section) else {
2169 return Err(FttsqError::SectionWriteOutOfOrder {
2170 expected: None,
2171 actual: section.to_owned(),
2172 });
2173 };
2174 let expected = entry.name.clone();
2175 let declared = entry.length;
2176 if expected != section {
2177 return Err(FttsqError::SectionWriteOutOfOrder {
2178 expected: Some(expected),
2179 actual: section.to_owned(),
2180 });
2181 }
2182 let bytes_len = bytes.len() as u64;
2183 let attempted = self.section_written.checked_add(bytes_len).ok_or_else(|| {
2184 FttsqError::SectionLengthExceeded {
2185 section: expected.clone(),
2186 declared,
2187 attempted: u64::MAX,
2188 }
2189 })?;
2190 if attempted > declared {
2191 return Err(FttsqError::SectionLengthExceeded {
2192 section: expected,
2193 declared,
2194 attempted,
2195 });
2196 }
2197
2198 self.writer
2199 .write_all(bytes)
2200 .map_err(|error| stream_io_error("write section", &error))?;
2201 self.section_hasher.update(bytes);
2202 self.section_written = attempted;
2203 self.finalize_empty_sections();
2204 Ok(())
2205 }
2206
2207 pub fn finish(mut self) -> Result<W, FttsqError> {
2218 if let Some(section) = self.sections.get(self.current_section) {
2219 return Err(FttsqError::SectionIncomplete {
2220 section: section.name.clone(),
2221 declared: section.length,
2222 written: self.section_written,
2223 });
2224 }
2225
2226 let directory = stream_directory_json(
2227 &self.model_family,
2228 &self.source_sha256,
2229 &self.license_notice,
2230 &self.model_config,
2231 &self.quantization_manifest,
2232 &self.sections,
2233 &self.tensors,
2234 );
2235 let directory_bytes =
2236 serde_json::to_vec(&directory).map_err(|error| FttsqError::DirectoryMalformed {
2237 detail: error.to_string(),
2238 })?;
2239 if directory_bytes.len() as u64 > self.directory_len {
2240 return Err(FttsqError::DirectoryLength {
2241 declared: directory_bytes.len() as u64,
2242 limit: self.directory_len,
2243 });
2244 }
2245
2246 self.writer
2247 .seek(std::io::SeekFrom::Start(HEADER_PREFIX_BYTES))
2248 .map_err(|error| stream_io_error("seek to directory", &error))?;
2249 self.writer
2250 .write_all(&directory_bytes)
2251 .map_err(|error| stream_io_error("finalize directory", &error))?;
2252 write_space_padding(
2253 &mut self.writer,
2254 self.directory_len - directory_bytes.len() as u64,
2255 )?;
2256 self.writer
2257 .seek(std::io::SeekFrom::End(0))
2258 .map_err(|error| stream_io_error("seek to artifact end", &error))?;
2259 self.writer
2260 .flush()
2261 .map_err(|error| stream_io_error("flush finalized artifact", &error))?;
2262 Ok(self.writer)
2263 }
2264
2265 fn finalize_empty_sections(&mut self) {
2266 while let Some(section) = self.sections.get_mut(self.current_section) {
2267 if self.section_written != section.length {
2268 break;
2269 }
2270 section.sha256 = to_hex(&std::mem::take(&mut self.section_hasher).finish());
2271 self.current_section += 1;
2272 self.section_written = 0;
2273 }
2274 }
2275}
2276
2277fn layout_stream_sections(
2278 sections: &mut [SectionEntry],
2279 payload_start: u64,
2280) -> Result<u64, FttsqError> {
2281 let mut cursor = payload_start;
2282 for section in sections {
2283 section.offset = cursor;
2284 cursor =
2285 cursor
2286 .checked_add(section.length)
2287 .ok_or_else(|| FttsqError::RangeOutOfBounds {
2288 what: format!("section `{}`", section.name),
2289 offset: section.offset,
2290 length: section.length,
2291 bound: u64::MAX,
2292 })?;
2293 }
2294 Ok(cursor)
2295}
2296
2297fn stream_directory_json(
2298 model_family: &str,
2299 source_sha256: &str,
2300 license_notice: &str,
2301 model_config: &Value,
2302 quantization_manifest: &Value,
2303 sections: &[SectionEntry],
2304 tensors: &[TensorEntry],
2305) -> Value {
2306 let sections: Vec<Value> = sections
2307 .iter()
2308 .map(|section| {
2309 json!({
2310 "name": section.name,
2311 "access_class": section.access_class.as_str(),
2312 "offset": section.offset,
2313 "length": section.length,
2314 "sha256": section.sha256,
2315 })
2316 })
2317 .collect();
2318 let tensors: Vec<Value> = tensors
2319 .iter()
2320 .map(|tensor| {
2321 json!({
2322 "name": tensor.name,
2323 "section": tensor.section,
2324 "dtype": tensor.dtype.as_str(),
2325 "shape": tensor.shape,
2326 "offset": tensor.offset,
2327 "length": tensor.length,
2328 "scales": tensor.scales,
2329 })
2330 })
2331 .collect();
2332 json!({
2333 "format_version": FORMAT_VERSION,
2334 "model_family": model_family,
2335 "source_sha256": source_sha256,
2336 "license_notice": license_notice,
2337 "model_config": model_config,
2338 "quantization_manifest": quantization_manifest,
2339 "sections": sections,
2340 "tensors": tensors,
2341 })
2342}
2343
2344fn stream_io_error(operation: &str, error: &std::io::Error) -> FttsqError {
2345 FttsqError::Io {
2346 operation: operation.to_owned(),
2347 path: "<fttsq stream>".to_owned(),
2348 detail: error.to_string(),
2349 }
2350}
2351
2352fn write_space_padding<W: std::io::Write>(
2353 writer: &mut W,
2354 mut remaining: u64,
2355) -> Result<(), FttsqError> {
2356 const SPACES: [u8; 4096] = [b' '; 4096];
2357 while remaining > 0 {
2358 let count = remaining.min(SPACES.len() as u64) as usize;
2359 writer
2360 .write_all(&SPACES[..count])
2361 .map_err(|error| stream_io_error("pad finalized directory", &error))?;
2362 remaining -= count as u64;
2363 }
2364 Ok(())
2365}
2366
2367#[derive(Debug, Default)]
2372pub struct FttsqWriter {
2373 model_family: String,
2374 source_sha256: String,
2375 license_notice: String,
2376 model_config: Value,
2377 quantization_manifest: Value,
2378 sections: Vec<(SectionEntry, Vec<u8>)>,
2379 tensors: Vec<TensorEntry>,
2380}
2381
2382impl FttsqWriter {
2383 #[must_use]
2385 pub fn new(model_family: impl Into<String>, source_sha256: impl Into<String>) -> Self {
2386 Self {
2387 model_family: model_family.into(),
2388 source_sha256: source_sha256.into(),
2389 license_notice: String::new(),
2390 model_config: Value::Null,
2391 quantization_manifest: Value::Null,
2392 sections: Vec::new(),
2393 tensors: Vec::new(),
2394 }
2395 }
2396
2397 #[must_use]
2399 pub fn license_notice(mut self, notice: impl Into<String>) -> Self {
2400 self.license_notice = notice.into();
2401 self
2402 }
2403
2404 #[must_use]
2406 pub fn model_config(mut self, config: Value) -> Self {
2407 self.model_config = config;
2408 self
2409 }
2410
2411 #[must_use]
2413 pub fn quantization_manifest(mut self, manifest: Value) -> Self {
2414 self.quantization_manifest = manifest;
2415 self
2416 }
2417
2418 #[must_use]
2420 pub fn section(
2421 mut self,
2422 name: impl Into<String>,
2423 access_class: AccessClass,
2424 payload: Vec<u8>,
2425 ) -> Self {
2426 let entry = SectionEntry {
2427 name: name.into(),
2428 access_class,
2429 offset: 0,
2430 length: payload.len() as u64,
2431 sha256: String::new(),
2432 };
2433 self.sections.push((entry, payload));
2434 self
2435 }
2436
2437 #[must_use]
2439 pub fn tensor(mut self, tensor: TensorEntry) -> Self {
2440 self.tensors.push(tensor);
2441 self
2442 }
2443
2444 pub fn finish(mut self) -> Result<Vec<u8>, FttsqError> {
2454 if self.license_notice.trim().is_empty() {
2455 return Err(FttsqError::LicenseNoticeMissing);
2456 }
2457
2458 for (entry, payload) in &mut self.sections {
2459 entry.length = payload.len() as u64;
2460 entry.sha256 = hex_digest(payload);
2461 }
2462
2463 let probe = self.directory_json(u64::MAX);
2468 let probe_len = serde_json::to_vec(&probe)
2469 .map_err(|error| FttsqError::DirectoryMalformed {
2470 detail: error.to_string(),
2471 })?
2472 .len() as u64;
2473
2474 let payload_start = HEADER_PREFIX_BYTES + probe_len;
2475 let directory = self.directory_json(payload_start);
2476 let mut directory_bytes =
2477 serde_json::to_vec(&directory).map_err(|error| FttsqError::DirectoryMalformed {
2478 detail: error.to_string(),
2479 })?;
2480 while (directory_bytes.len() as u64) < probe_len {
2483 directory_bytes.push(b' ');
2484 }
2485
2486 let mut out = Vec::with_capacity(payload_start as usize);
2487 out.extend_from_slice(MAGIC);
2488 out.extend_from_slice(&FORMAT_VERSION.to_le_bytes());
2489 out.extend_from_slice(&(directory_bytes.len() as u64).to_le_bytes());
2490 out.extend_from_slice(&directory_bytes);
2491 for (_, payload) in &self.sections {
2492 out.extend_from_slice(payload);
2493 }
2494
2495 FttsqReader::open(&out)?;
2497 Ok(out)
2498 }
2499
2500 pub fn write_to_path(self, path: &std::path::Path) -> Result<(), FttsqError> {
2516 use std::io::Write as _;
2517
2518 let bytes = self.finish()?;
2519
2520 let parent = path.parent().unwrap_or_else(|| std::path::Path::new("."));
2521 let file_name = path.file_name().map_or_else(
2523 || std::ffi::OsString::from("artifact.fttsq"),
2524 std::ffi::OsStr::to_os_string,
2525 );
2526 let mut temp_name = file_name;
2527 temp_name.push(format!(".tmp.{}", std::process::id()));
2528 let temp_path = parent.join(temp_name);
2529
2530 let io =
2531 |operation: &str, target: &std::path::Path, error: &std::io::Error| FttsqError::Io {
2532 operation: operation.to_owned(),
2533 path: target.display().to_string(),
2534 detail: error.to_string(),
2535 };
2536
2537 let result = (|| -> Result<(), FttsqError> {
2539 let mut file = std::fs::File::create(&temp_path)
2540 .map_err(|error| io("create", &temp_path, &error))?;
2541 file.write_all(&bytes)
2542 .map_err(|error| io("write", &temp_path, &error))?;
2543 file.sync_all()
2546 .map_err(|error| io("fsync", &temp_path, &error))?;
2547 drop(file);
2548 std::fs::rename(&temp_path, path).map_err(|error| io("rename", path, &error))
2549 })();
2550
2551 if result.is_err() {
2552 let _ = std::fs::remove_file(&temp_path);
2553 }
2554 result
2555 }
2556
2557 fn directory_json(&self, payload_start: u64) -> Value {
2558 let mut cursor = payload_start;
2559 let sections: Vec<Value> = self
2560 .sections
2561 .iter()
2562 .map(|(entry, _)| {
2563 let offset = cursor;
2564 cursor = cursor.saturating_add(entry.length);
2569 json!({
2570 "name": entry.name,
2571 "access_class": entry.access_class.as_str(),
2572 "offset": offset,
2573 "length": entry.length,
2574 "sha256": entry.sha256,
2575 })
2576 })
2577 .collect();
2578
2579 let tensors: Vec<Value> = self
2580 .tensors
2581 .iter()
2582 .map(|tensor| {
2583 json!({
2584 "name": tensor.name,
2585 "section": tensor.section,
2586 "dtype": tensor.dtype.as_str(),
2587 "shape": tensor.shape,
2588 "offset": tensor.offset,
2589 "length": tensor.length,
2590 "scales": tensor.scales,
2591 })
2592 })
2593 .collect();
2594
2595 json!({
2596 "format_version": FORMAT_VERSION,
2597 "model_family": self.model_family,
2598 "source_sha256": self.source_sha256,
2599 "license_notice": self.license_notice,
2600 "model_config": self.model_config,
2601 "quantization_manifest": self.quantization_manifest,
2602 "sections": sections,
2603 "tensors": tensors,
2604 })
2605 }
2606}
2607
2608#[cfg(test)]
2609mod tests {
2610 use super::*;
2611 use std::io::Cursor;
2612
2613 const NOTICE: &str = "Copyright 2026 Alibaba Cloud\nApache-2.0\nCHANGES: requantized to .fttsq";
2615
2616 fn artifact() -> Vec<u8> {
2617 FttsqWriter::new("qwen3-tts-12hz-0.6b-base", "a".repeat(64))
2618 .license_notice(NOTICE)
2619 .model_config(json!({ "hidden_size": 1024 }))
2620 .quantization_manifest(json!({ "talker": "q8" }))
2621 .section(
2622 "microdecoder",
2623 AccessClass::HotRecurrentMicrodecoder,
2624 vec![7_u8; 64],
2625 )
2626 .section(
2627 "text_embedding",
2628 AccessClass::ColdTextEmbedding,
2629 vec![9_u8; 32],
2630 )
2631 .tensor(TensorEntry {
2632 name: "microdecoder.body".to_owned(),
2633 section: "microdecoder".to_owned(),
2634 dtype: StoredDtype::Q8,
2635 shape: vec![8, 8],
2636 offset: 0,
2637 length: 64,
2638 scales: Some("microdecoder.body.scales".to_owned()),
2639 })
2640 .tensor(TensorEntry {
2641 name: "text_embedding.weight".to_owned(),
2642 section: "text_embedding".to_owned(),
2643 dtype: StoredDtype::Bf16,
2644 shape: vec![4, 4],
2645 offset: 0,
2646 length: 32,
2647 scales: None,
2648 })
2649 .finish()
2650 .expect("the fixture artifact is writable")
2651 }
2652
2653 fn stream_plan() -> FttsqStreamPlan {
2654 FttsqStreamPlan::new("qwen3-tts-12hz-0.6b-base", "a".repeat(64))
2655 .license_notice(NOTICE)
2656 .model_config(json!({ "hidden_size": 1024 }))
2657 .quantization_manifest(json!({ "talker": "q8" }))
2658 .section("microdecoder", AccessClass::HotRecurrentMicrodecoder, 64)
2659 .section("text_embedding", AccessClass::ColdTextEmbedding, 32)
2660 .tensor(TensorEntry {
2661 name: "microdecoder.body".to_owned(),
2662 section: "microdecoder".to_owned(),
2663 dtype: StoredDtype::Q8,
2664 shape: vec![8, 8],
2665 offset: 0,
2666 length: 64,
2667 scales: Some("microdecoder.body.scales".to_owned()),
2668 })
2669 .tensor(TensorEntry {
2670 name: "text_embedding.weight".to_owned(),
2671 section: "text_embedding".to_owned(),
2672 dtype: StoredDtype::Bf16,
2673 shape: vec![4, 4],
2674 offset: 0,
2675 length: 32,
2676 scales: None,
2677 })
2678 }
2679
2680 fn streamed_artifact() -> Vec<u8> {
2681 let mut writer = stream_plan()
2682 .begin(Cursor::new(Vec::new()))
2683 .expect("the stream plan is structurally valid");
2684 writer
2685 .write_section("microdecoder", &[7_u8; 64])
2686 .expect("first section streams");
2687 writer
2688 .write_section("text_embedding", &[9_u8; 32])
2689 .expect("second section streams");
2690 writer
2691 .finish()
2692 .expect("complete stream finalizes")
2693 .into_inner()
2694 }
2695
2696 #[test]
2697 fn streaming_writer_is_canonical_and_never_retains_section_payloads() {
2698 let bytes = streamed_artifact();
2702 assert_eq!(bytes, artifact());
2703 let reader = FttsqReader::open(&bytes).expect("finalized stream verifies");
2704 assert_eq!(
2705 reader
2706 .tensor_bytes("microdecoder.body", &bytes)
2707 .expect("streamed tensor resolves"),
2708 &[7_u8; 64]
2709 );
2710 }
2711
2712 #[test]
2713 fn streaming_writer_refuses_out_of_order_or_incomplete_sections() {
2714 let mut writer = stream_plan()
2715 .begin(Cursor::new(Vec::new()))
2716 .expect("the stream plan is structurally valid");
2717 assert_eq!(
2718 writer
2719 .write_section("text_embedding", &[9_u8; 32])
2720 .expect_err("later sections cannot be buffered"),
2721 FttsqError::SectionWriteOutOfOrder {
2722 expected: Some("microdecoder".to_owned()),
2723 actual: "text_embedding".to_owned(),
2724 }
2725 );
2726 writer
2727 .write_section("microdecoder", &[7_u8; 63])
2728 .expect("a bounded partial chunk is accepted");
2729 assert_eq!(
2730 writer
2731 .finish()
2732 .expect_err("a partial section cannot acquire a digest"),
2733 FttsqError::SectionIncomplete {
2734 section: "microdecoder".to_owned(),
2735 declared: 64,
2736 written: 63,
2737 }
2738 );
2739 }
2740
2741 #[test]
2742 fn round_trips_through_write_and_read() {
2743 let bytes = artifact();
2744 let reader =
2745 FttsqReader::open(&bytes).expect("the artifact we just wrote must be readable");
2746
2747 assert_eq!(reader.format_version(), FORMAT_VERSION);
2748 assert_eq!(reader.model_family(), "qwen3-tts-12hz-0.6b-base");
2749 assert!(reader.license_notice().contains("Alibaba Cloud"));
2750 assert_eq!(reader.model_config()["hidden_size"], 1024);
2751 assert_eq!(reader.sections().len(), 2);
2752 assert_eq!(reader.tensors().len(), 2);
2753
2754 assert_eq!(
2756 reader
2757 .tensor_bytes("microdecoder.body", &bytes)
2758 .expect("tensor resolves"),
2759 &vec![7_u8; 64][..]
2760 );
2761 assert_eq!(
2762 reader
2763 .tensor_bytes("text_embedding.weight", &bytes)
2764 .expect("tensor resolves"),
2765 &vec![9_u8; 32][..]
2766 );
2767 }
2768
2769 #[test]
2770 fn bf16_payload_is_byte_identical_across_the_round_trip() {
2771 let payload: Vec<u8> = (0..=255_u8).cycle().take(4096).collect();
2774 let bytes = FttsqWriter::new("qwen3-tts-12hz-0.6b-base", "b".repeat(64))
2775 .license_notice(NOTICE)
2776 .section("talker", AccessClass::HotRecurrentTalker, payload.clone())
2777 .tensor(TensorEntry {
2778 name: "talker.weight".to_owned(),
2779 section: "talker".to_owned(),
2780 dtype: StoredDtype::Bf16,
2781 shape: vec![64, 32],
2782 offset: 0,
2783 length: 4096,
2784 scales: None,
2785 })
2786 .finish()
2787 .expect("writable");
2788 let reader = FttsqReader::open(&bytes).expect("readable");
2789 assert_eq!(
2790 reader
2791 .tensor_bytes("talker.weight", &bytes)
2792 .expect("resolves"),
2793 &payload[..]
2794 );
2795 }
2796
2797 #[test]
2798 fn access_classes_drive_the_page_in_policy() {
2799 let bytes = artifact();
2800 let reader = FttsqReader::open(&bytes).expect("readable");
2801
2802 let hot = reader.sections_in_class(AccessClass::HotRecurrentMicrodecoder);
2803 assert_eq!(hot.len(), 1);
2804 assert!(hot[0].access_class.is_hot());
2805 assert!(!hot[0].access_class.is_row_granular());
2806
2807 let cold = reader.sections_in_class(AccessClass::ColdTextEmbedding);
2808 assert_eq!(cold.len(), 1);
2809 assert!(
2810 !cold[0].access_class.is_hot(),
2811 "the 622 MB embedding must never be advised resident"
2812 );
2813 assert!(
2814 cold[0].access_class.is_row_granular(),
2815 "the cold embedding is accessed a row at a time, never as a unit"
2816 );
2817 }
2818
2819 #[test]
2820 fn a_newer_format_version_is_refused_rather_than_guessed_at() {
2821 let mut bytes = artifact();
2822 bytes[8..12].copy_from_slice(&(FORMAT_VERSION + 1).to_le_bytes());
2823 let error = FttsqReader::parse_directory(&bytes).expect_err("must refuse");
2824 assert_eq!(
2825 error,
2826 FttsqError::UnsupportedVersion {
2827 found: FORMAT_VERSION + 1,
2828 supported: FORMAT_VERSION,
2829 }
2830 );
2831 }
2832
2833 #[test]
2834 fn bad_magic_and_truncation_are_named_refusals() {
2835 assert!(matches!(
2836 FttsqReader::parse_directory(&[]),
2837 Err(FttsqError::TooShort { .. })
2838 ));
2839 let mut bytes = artifact();
2840 bytes[0] = b'X';
2841 assert!(matches!(
2842 FttsqReader::parse_directory(&bytes),
2843 Err(FttsqError::BadMagic { .. })
2844 ));
2845 }
2846
2847 #[test]
2848 fn a_truncated_file_never_yields_a_partial_load() {
2849 let full = artifact();
2850 for cut in [full.len() - 1, full.len() - 40, full.len() - 90] {
2852 let error = FttsqReader::open(&full[..cut]).expect_err("truncation must be refused");
2853 assert!(
2854 matches!(
2855 error,
2856 FttsqError::RangeOutOfBounds { .. } | FttsqError::DirectoryLength { .. }
2857 ),
2858 "unexpected error for cut at {cut}: {error}"
2859 );
2860 }
2861 }
2862
2863 #[test]
2864 fn a_single_flipped_payload_bit_fails_digest_verification() {
2865 let mut bytes = artifact();
2866 let last = bytes.len() - 1;
2867 bytes[last] ^= 0x01;
2868 let error = FttsqReader::open(&bytes).expect_err("a bit flip must be caught");
2869 assert!(
2870 matches!(
2871 &error,
2872 FttsqError::DigestMismatch { section, .. } if section == "text_embedding"
2873 ),
2874 "expected a digest mismatch for text_embedding, got {error}"
2875 );
2876 assert!(FttsqReader::parse_directory(&bytes).is_ok());
2878 }
2879
2880 #[test]
2881 fn a_hostile_directory_length_cannot_provoke_a_huge_read() {
2882 let mut bytes = artifact();
2883 bytes[12..20].copy_from_slice(&u64::MAX.to_le_bytes());
2884 let error = FttsqReader::parse_directory(&bytes).expect_err("must refuse");
2885 assert!(matches!(error, FttsqError::DirectoryLength { .. }));
2886 }
2887
2888 #[test]
2890 fn structural_violations_are_each_refused_by_name() {
2891 type StructuralCase = (&'static str, Value, fn(&FttsqError) -> bool);
2892 let cases: Vec<StructuralCase> = vec![
2893 (
2894 "overlapping sections",
2895 json!([
2896 {"name": "a", "access_class": "METADATA", "offset": 100, "length": 50, "sha256": "x"},
2897 {"name": "b", "access_class": "METADATA", "offset": 120, "length": 10, "sha256": "x"},
2898 ]),
2899 |e| matches!(e, FttsqError::SectionOverlap { .. }),
2900 ),
2901 (
2902 "a section running past the file",
2903 json!([
2904 {"name": "a", "access_class": "METADATA", "offset": 100, "length": u64::MAX, "sha256": "x"},
2905 ]),
2906 |e| matches!(e, FttsqError::RangeOutOfBounds { .. }),
2907 ),
2908 (
2909 "a duplicate section name",
2910 json!([
2911 {"name": "a", "access_class": "METADATA", "offset": 100, "length": 10, "sha256": "x"},
2912 {"name": "a", "access_class": "METADATA", "offset": 200, "length": 10, "sha256": "x"},
2913 ]),
2914 |e| matches!(e, FttsqError::DuplicateName { .. }),
2915 ),
2916 (
2917 "an unknown access class",
2918 json!([
2919 {"name": "a", "access_class": "PROBABLY_HOT", "offset": 100, "length": 10, "sha256": "x"},
2920 ]),
2921 |e| matches!(e, FttsqError::UnknownValue { .. }),
2922 ),
2923 ];
2924
2925 for (description, sections, matches_expected) in cases {
2926 let error = parse_sections(Some(§ions), 4096)
2927 .expect_err(&format!("`{description}` must be refused"));
2928 assert!(
2929 matches_expected(&error),
2930 "`{description}` produced the wrong error: {error}"
2931 );
2932 }
2933 }
2934
2935 #[test]
2936 fn a_tensor_whose_length_disagrees_with_its_shape_is_refused() {
2937 let sections = vec![SectionEntry {
2938 name: "s".to_owned(),
2939 access_class: AccessClass::Metadata,
2940 offset: 0,
2941 length: 4096,
2942 sha256: String::new(),
2943 }];
2944 let index: BTreeMap<String, usize> = [("s".to_owned(), 0)].into_iter().collect();
2945
2946 let tensors = json!([
2948 {"name": "t", "section": "s", "dtype": "bf16", "shape": [8, 8], "offset": 0, "length": 64},
2949 ]);
2950 let error = parse_tensors(Some(&tensors), §ions, &index).expect_err("must refuse");
2951 assert_eq!(
2952 error,
2953 FttsqError::LengthMismatch {
2954 tensor: "t".to_owned(),
2955 declared: 64,
2956 implied: 128,
2957 }
2958 );
2959 }
2960
2961 #[test]
2962 fn tensors_may_not_overlap_within_a_section() {
2963 let sections = vec![SectionEntry {
2964 name: "s".to_owned(),
2965 access_class: AccessClass::Metadata,
2966 offset: 0,
2967 length: 4096,
2968 sha256: String::new(),
2969 }];
2970 let index: BTreeMap<String, usize> = [("s".to_owned(), 0)].into_iter().collect();
2971 let tensors = json!([
2972 {"name": "a", "section": "s", "dtype": "q8", "shape": [64], "offset": 0, "length": 64},
2973 {"name": "b", "section": "s", "dtype": "q8", "shape": [64], "offset": 32, "length": 64},
2974 ]);
2975 let error = parse_tensors(Some(&tensors), §ions, &index).expect_err("must refuse");
2976 assert!(matches!(error, FttsqError::TensorOverlap { .. }), "{error}");
2977 }
2978
2979 #[test]
2980 fn a_tensor_leaving_its_section_is_refused() {
2981 let sections = vec![SectionEntry {
2982 name: "s".to_owned(),
2983 access_class: AccessClass::Metadata,
2984 offset: 0,
2985 length: 64,
2986 sha256: String::new(),
2987 }];
2988 let index: BTreeMap<String, usize> = [("s".to_owned(), 0)].into_iter().collect();
2989 let tensors = json!([
2990 {"name": "a", "section": "s", "dtype": "q8", "shape": [64], "offset": 32, "length": 64},
2991 ]);
2992 let error = parse_tensors(Some(&tensors), §ions, &index).expect_err("must refuse");
2993 assert!(
2994 matches!(error, FttsqError::RangeOutOfBounds { .. }),
2995 "{error}"
2996 );
2997 }
2998
2999 #[test]
3000 fn an_artifact_without_a_license_notice_cannot_be_written_or_read() {
3001 let error = FttsqWriter::new("qwen3-tts-12hz-0.6b-base", "c".repeat(64))
3003 .section("m", AccessClass::Metadata, vec![1, 2, 3])
3004 .finish()
3005 .expect_err("Apache-2.0 §4 makes the notice mandatory");
3006 assert_eq!(error, FttsqError::LicenseNoticeMissing);
3007
3008 let mut bytes = artifact();
3010 let directory_len = u64::from_le_bytes(bytes[12..20].try_into().expect("header length"));
3011 let directory_start = HEADER_PREFIX_BYTES as usize;
3012 let directory_end = directory_start + directory_len as usize;
3013 let mut directory: Value = serde_json::from_slice(&bytes[directory_start..directory_end])
3014 .expect("fixture directory");
3015 directory["license_notice"] = Value::String(String::new());
3016 let mut replacement = serde_json::to_vec(&directory).expect("serializes directory");
3017 assert!(
3018 replacement.len() <= directory_len as usize,
3019 "removing a notice cannot grow it"
3020 );
3021 replacement.resize(directory_len as usize, b' ');
3022 bytes[directory_start..directory_end].copy_from_slice(&replacement);
3023 assert_eq!(
3024 FttsqReader::open(&bytes).expect_err("must refuse a missing notice"),
3025 FttsqError::LicenseNoticeMissing
3026 );
3027 }
3028
3029 #[test]
3030 fn write_to_path_lands_a_complete_readable_artifact_and_leaves_no_temporary() {
3031 let dir = std::env::temp_dir().join(format!("ftts-fttsq-write-{}", std::process::id()));
3032 std::fs::create_dir_all(&dir).expect("scratch dir");
3033 let path = dir.join("model.fttsq");
3034
3035 FttsqWriter::new("qwen3-tts-12hz-0.6b-base", "d".repeat(64))
3036 .license_notice(NOTICE)
3037 .section("m", AccessClass::HotRecurrentMicrodecoder, vec![3_u8; 128])
3038 .section(
3039 "embedding",
3040 AccessClass::ColdTextEmbedding,
3041 vec![9_u8; 8192],
3042 )
3043 .tensor(TensorEntry {
3044 name: "m.w".to_owned(),
3045 section: "m".to_owned(),
3046 dtype: StoredDtype::Q8,
3047 shape: vec![128],
3048 offset: 0,
3049 length: 128,
3050 scales: None,
3051 })
3052 .tensor(TensorEntry {
3053 name: "embedding.one_row".to_owned(),
3054 section: "embedding".to_owned(),
3055 dtype: StoredDtype::Q8,
3056 shape: vec![32],
3057 offset: 4096,
3058 length: 32,
3059 scales: None,
3060 })
3061 .write_to_path(&path)
3062 .expect("artifact is writable");
3063
3064 let bytes = std::fs::read(&path).expect("artifact is readable");
3065 let reader = FttsqReader::open(&bytes).expect("what landed on disk must verify");
3066 assert_eq!(
3067 reader.tensor_bytes("m.w", &bytes).expect("resolves"),
3068 &vec![3_u8; 128][..]
3069 );
3070
3071 let mapped = MappedFttsq::open(&path).expect("mapped artifact validates");
3072 assert_eq!(mapped.len(), bytes.len());
3073 assert_eq!(
3074 mapped
3075 .tensor_bytes("embedding.one_row")
3076 .expect("row range resolves without copying the section"),
3077 &vec![9_u8; 32][..]
3078 );
3079
3080 let micro = mapped
3081 .page_advice()
3082 .iter()
3083 .find(|application| application.section == "m")
3084 .expect("microdecoder application is recorded");
3085 assert_eq!(micro.policy, PagePolicy::Resident);
3086 assert_eq!(micro.requested, Some(MemoryAdvice::WillNeed));
3087 assert!(
3088 !matches!(micro.outcome, PageAdviceOutcome::Failed(_)),
3089 "a valid mapped microdecoder section must receive a usable advice result: {micro:?}"
3090 );
3091
3092 let embedding = mapped
3093 .page_advice()
3094 .iter()
3095 .find(|application| application.section == "embedding")
3096 .expect("embedding application is recorded");
3097 assert_eq!(embedding.policy, PagePolicy::LazyRowGranular);
3098 assert_eq!(embedding.requested, Some(MemoryAdvice::Random));
3099 assert!(
3100 !embedding.policy.may_prefetch(),
3101 "the cold embedding policy must make wholesale prefetch impossible"
3102 );
3103 for observation in [&embedding.residency_before, &embedding.residency_after] {
3104 match observation {
3105 PageResidencyOutcome::Measured {
3106 resident_pages,
3107 total_pages,
3108 } => assert!(
3109 resident_pages <= total_pages,
3110 "the OQ-18 residency measurement exceeded the section's page span"
3111 ),
3112 PageResidencyOutcome::Unsupported => {}
3113 PageResidencyOutcome::Failed(detail) => {
3114 panic!("the cold embedding residency measurement failed: {detail}");
3115 }
3116 }
3117 }
3118 assert!(
3119 mapped.page_advice().iter().all(|application| {
3120 application.policy.may_prefetch()
3121 || application.requested != Some(MemoryAdvice::WillNeed)
3122 }),
3123 "a non-prefetch section was routed to MADV_WILLNEED"
3124 );
3125
3126 let strays: Vec<_> = std::fs::read_dir(&dir)
3128 .expect("dir is listable")
3129 .filter_map(Result::ok)
3130 .map(|entry| entry.file_name().to_string_lossy().into_owned())
3131 .filter(|name| name.contains(".tmp."))
3132 .collect();
3133 assert!(strays.is_empty(), "temporary files left behind: {strays:?}");
3134
3135 std::fs::remove_file(&path).expect("cleanup");
3136 }
3137
3138 #[test]
3139 fn write_to_path_refuses_before_touching_the_filesystem_when_the_notice_is_missing() {
3140 let dir = std::env::temp_dir().join(format!("ftts-fttsq-refuse-{}", std::process::id()));
3141 std::fs::create_dir_all(&dir).expect("scratch dir");
3142 let path = dir.join("model.fttsq");
3143
3144 let error = FttsqWriter::new("qwen3-tts-12hz-0.6b-base", "e".repeat(64))
3145 .section("m", AccessClass::Metadata, vec![1, 2, 3])
3146 .write_to_path(&path)
3147 .expect_err("a notice-less artifact must never reach disk");
3148 assert_eq!(error, FttsqError::LicenseNoticeMissing);
3149 assert!(
3150 !path.exists(),
3151 "a refused artifact must not leave a file behind"
3152 );
3153 }
3154
3155 #[test]
3157 fn the_cold_text_embedding_is_never_prefetched_and_hot_classes_always_are() {
3158 assert_eq!(
3159 AccessClass::ColdTextEmbedding.page_policy(),
3160 PagePolicy::LazyRowGranular
3161 );
3162 assert!(
3163 !AccessClass::ColdTextEmbedding.page_policy().may_prefetch(),
3164 "MADV_WILLNEED over the ~622 MB embedding would evict the microdecoder pack"
3165 );
3166
3167 for hot in [
3168 AccessClass::HotRecurrentMicrodecoder,
3169 AccessClass::HotRecurrentTalker,
3170 AccessClass::HotCodecDecoder,
3171 ] {
3172 assert_eq!(hot.page_policy(), PagePolicy::Resident);
3173 assert!(hot.page_policy().may_prefetch());
3174 }
3175 for cold in [
3176 AccessClass::EnrollmentSpeakerEncoder,
3177 AccessClass::EnrollmentCodecEncoder,
3178 AccessClass::Metadata,
3179 ] {
3180 assert_eq!(cold.page_policy(), PagePolicy::OnDemand);
3181 assert!(!cold.page_policy().may_prefetch());
3182 }
3183
3184 for class in [
3186 AccessClass::HotRecurrentMicrodecoder,
3187 AccessClass::HotRecurrentTalker,
3188 AccessClass::HotCodecDecoder,
3189 AccessClass::ColdTextEmbedding,
3190 AccessClass::EnrollmentSpeakerEncoder,
3191 AccessClass::EnrollmentCodecEncoder,
3192 AccessClass::Metadata,
3193 ] {
3194 assert_eq!(
3195 class.is_hot(),
3196 class.page_policy().may_prefetch(),
3197 "is_hot() and page_policy() disagree for {class}"
3198 );
3199 assert_eq!(
3200 class.is_row_granular(),
3201 class.page_policy() == PagePolicy::LazyRowGranular,
3202 "is_row_granular() and page_policy() disagree for {class}"
3203 );
3204 }
3205 }
3206
3207 #[test]
3208 fn the_page_in_plan_prefetches_the_microdecoder_before_the_larger_talker() {
3209 let bytes = FttsqWriter::new("qwen3-tts-12hz-0.6b-base", "f".repeat(64))
3211 .license_notice(NOTICE)
3212 .section("talker", AccessClass::HotRecurrentTalker, vec![1_u8; 400])
3213 .section("embedding", AccessClass::ColdTextEmbedding, vec![2_u8; 900])
3214 .section(
3215 "micro",
3216 AccessClass::HotRecurrentMicrodecoder,
3217 vec![3_u8; 100],
3218 )
3219 .section("meta", AccessClass::Metadata, vec![4_u8; 8])
3220 .finish()
3221 .expect("writable");
3222 let reader = FttsqReader::open(&bytes).expect("readable");
3223
3224 let plan = reader.page_in_plan();
3225 let order: Vec<&str> = plan
3226 .iter()
3227 .map(|(section, _)| section.name.as_str())
3228 .collect();
3229 assert_eq!(
3230 order,
3231 vec!["micro", "talker", "embedding", "meta"],
3232 "resident sections first, smallest first, so the 15x-reread pack wins the cache race"
3233 );
3234 assert_eq!(plan[0].1, PagePolicy::Resident);
3235 assert_eq!(plan[2].1, PagePolicy::LazyRowGranular);
3236 assert_eq!(plan[3].1, PagePolicy::OnDemand);
3237
3238 for (section, policy) in &plan {
3240 assert_eq!(
3241 policy.may_prefetch(),
3242 section.access_class.is_hot(),
3243 "section `{}` would be prefetched against policy",
3244 section.name
3245 );
3246 }
3247 }
3248
3249 fn census_fixture() -> (Vec<u8>, ArtifactManifest) {
3250 let bytes = FttsqWriter::new("qwen3-tts-12hz-0.6b-base", "g".repeat(64))
3251 .license_notice(NOTICE)
3252 .section(
3253 "micro",
3254 AccessClass::HotRecurrentMicrodecoder,
3255 vec![1_u8; 64],
3256 )
3257 .section("embedding", AccessClass::ColdTextEmbedding, vec![2_u8; 32])
3258 .tensor(TensorEntry {
3259 name: "micro.body".to_owned(),
3260 section: "micro".to_owned(),
3261 dtype: StoredDtype::Q8,
3262 shape: vec![8, 8],
3263 offset: 0,
3264 length: 64,
3265 scales: None,
3266 })
3267 .tensor(TensorEntry {
3268 name: "text_embedding.weight".to_owned(),
3269 section: "embedding".to_owned(),
3270 dtype: StoredDtype::Bf16,
3271 shape: vec![4, 4],
3272 offset: 0,
3273 length: 32,
3274 scales: None,
3275 })
3276 .finish()
3277 .expect("writable");
3278
3279 let manifest = ArtifactManifest::new("qwen3-tts pinned")
3280 .expect(ExpectedArtifactTensor {
3281 name: "micro.body".to_owned(),
3282 shape: vec![8, 8],
3283 dtype: StoredDtype::Q8,
3284 access_class: AccessClass::HotRecurrentMicrodecoder,
3285 })
3286 .expect(ExpectedArtifactTensor {
3287 name: "text_embedding.weight".to_owned(),
3288 shape: vec![4, 4],
3289 dtype: StoredDtype::Bf16,
3290 access_class: AccessClass::ColdTextEmbedding,
3291 });
3292 (bytes, manifest)
3293 }
3294
3295 #[test]
3296 fn a_matching_artifact_passes_its_census() {
3297 let (bytes, manifest) = census_fixture();
3298 let reader = FttsqReader::open(&bytes).expect("readable");
3299 let report = manifest.audit(&reader);
3300 assert!(report.is_green(), "{}", report.render());
3301 assert!(reader.verify_census(&manifest).is_ok());
3302 }
3303
3304 #[test]
3306 fn the_census_names_every_divergence_class_in_one_pass() {
3307 let (bytes, _) = census_fixture();
3308 let reader = FttsqReader::open(&bytes).expect("readable");
3309
3310 let manifest = ArtifactManifest::new("deliberately wrong")
3311 .expect(ExpectedArtifactTensor {
3313 name: "micro.body".to_owned(),
3314 shape: vec![16, 4],
3315 dtype: StoredDtype::Q4,
3316 access_class: AccessClass::HotRecurrentMicrodecoder,
3317 })
3318 .expect(ExpectedArtifactTensor {
3320 name: "text_embedding.weight".to_owned(),
3321 shape: vec![4, 4],
3322 dtype: StoredDtype::Bf16,
3323 access_class: AccessClass::HotRecurrentTalker,
3324 })
3325 .expect(ExpectedArtifactTensor {
3327 name: "codec.decoder.weight".to_owned(),
3328 shape: vec![2],
3329 dtype: StoredDtype::Q8,
3330 access_class: AccessClass::HotCodecDecoder,
3331 });
3332
3333 let report = manifest.audit(&reader);
3334 assert!(!report.is_green());
3335 assert_eq!(report.count_of("shape_mismatch"), 1, "{}", report.render());
3336 assert_eq!(report.count_of("dtype_mismatch"), 1, "{}", report.render());
3337 assert_eq!(
3338 report.count_of("wrong_access_class"),
3339 1,
3340 "a tensor in the wrong access class still produces correct audio while destroying \
3341 residency — the census is the only thing that catches it:\n{}",
3342 report.render()
3343 );
3344 assert_eq!(report.count_of("missing"), 1, "{}", report.render());
3345
3346 let rendered = report.render();
3347 for expected in [
3348 "micro.body",
3349 "text_embedding.weight",
3350 "codec.decoder.weight",
3351 "ACCESS_CLASS",
3352 "SHAPE",
3353 "DTYPE",
3354 "MISSING",
3355 ] {
3356 assert!(
3357 rendered.contains(expected),
3358 "census report is missing `{expected}`:\n{rendered}"
3359 );
3360 }
3361
3362 assert!(reader.verify_census(&manifest).is_err());
3363 }
3364
3365 #[test]
3367 fn unexpected_tensors_are_reported_as_extra() {
3368 let (bytes, _) = census_fixture();
3369 let reader = FttsqReader::open(&bytes).expect("readable");
3370 let manifest = ArtifactManifest::new("partial").expect(ExpectedArtifactTensor {
3371 name: "micro.body".to_owned(),
3372 shape: vec![8, 8],
3373 dtype: StoredDtype::Q8,
3374 access_class: AccessClass::HotRecurrentMicrodecoder,
3375 });
3376 let report = manifest.audit(&reader);
3377 assert_eq!(report.count_of("extra"), 1, "{}", report.render());
3378 assert!(report.render().contains("text_embedding.weight"));
3379 }
3380
3381 #[test]
3382 fn quantized_dtype_sizes_are_exact_including_the_odd_q4_tail() {
3383 assert_eq!(StoredDtype::Bf16.storage_bytes(10), Some(20));
3384 assert_eq!(StoredDtype::F32.storage_bytes(10), Some(40));
3385 assert_eq!(StoredDtype::Q8.storage_bytes(10), Some(10));
3386 assert_eq!(StoredDtype::Q4.storage_bytes(10), Some(5));
3388 assert_eq!(StoredDtype::Q4.storage_bytes(11), Some(6));
3389 assert_eq!(StoredDtype::F32.storage_bytes(u64::MAX), None);
3391 }
3392
3393 #[test]
3394 fn wire_strings_round_trip_for_every_enum_value() {
3395 for class in [
3396 AccessClass::HotRecurrentMicrodecoder,
3397 AccessClass::HotRecurrentTalker,
3398 AccessClass::HotCodecDecoder,
3399 AccessClass::ColdTextEmbedding,
3400 AccessClass::EnrollmentSpeakerEncoder,
3401 AccessClass::EnrollmentCodecEncoder,
3402 AccessClass::Metadata,
3403 ] {
3404 assert_eq!(AccessClass::parse(class.as_str()), Some(class));
3405 }
3406 for dtype in [
3407 StoredDtype::Bf16,
3408 StoredDtype::F32,
3409 StoredDtype::Q8,
3410 StoredDtype::Q4,
3411 ] {
3412 assert_eq!(StoredDtype::parse(dtype.as_str()), Some(dtype));
3413 }
3414 assert_eq!(AccessClass::parse("HOT_SOMETHING"), None);
3415 assert_eq!(StoredDtype::parse("f16"), None);
3416 }
3417}