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}
703
704impl MappedFttsq {
705 pub fn open(path: impl AsRef<std::path::Path>) -> Result<Self, FttsqError> {
717 let path = path.as_ref();
718 let mapping = MappedFile::open(path).map_err(|error| FttsqError::Io {
719 operation: "memory-map artifact".to_owned(),
720 path: path.display().to_string(),
721 detail: error.to_string(),
722 })?;
723 let reader = FttsqReader::parse_directory(mapping.as_slice())?;
726 let page_advice = apply_page_in_plan(&mapping, &reader);
727 reader.verify_digests(mapping.as_slice())?;
728 Ok(Self {
729 mapping,
730 reader,
731 page_advice,
732 })
733 }
734
735 #[must_use]
737 pub const fn reader(&self) -> &FttsqReader {
738 &self.reader
739 }
740
741 #[must_use]
743 pub fn page_advice(&self) -> &[PageAdviceApplication] {
744 &self.page_advice
745 }
746
747 pub fn tensor_bytes(&self, name: &str) -> Result<&[u8], FttsqError> {
753 self.reader.tensor_bytes(name, self.mapping.as_slice())
754 }
755
756 #[must_use]
758 pub fn len(&self) -> usize {
759 self.mapping.len()
760 }
761
762 #[must_use]
764 pub fn is_empty(&self) -> bool {
765 self.mapping.is_empty()
766 }
767}
768
769fn apply_page_in_plan(mapping: &MappedFile, reader: &FttsqReader) -> Vec<PageAdviceApplication> {
770 reader
771 .page_in_plan()
772 .into_iter()
773 .map(|(section, policy)| {
774 let requested = match policy {
775 PagePolicy::Resident => Some(MemoryAdvice::WillNeed),
776 PagePolicy::LazyRowGranular => Some(MemoryAdvice::Random),
777 PagePolicy::OnDemand => None,
778 };
779
780 assert!(
784 policy.may_prefetch() || requested != Some(MemoryAdvice::WillNeed),
785 "a non-prefetch policy must never issue MADV_WILLNEED"
786 );
787
788 let residency_before = observe_residency(mapping, section.offset, section.length);
789 let outcome = match requested {
790 Some(advice) => match mapping.advise(section.offset, section.length, advice) {
791 Ok(MemoryAdviceOutcome::Applied) => PageAdviceOutcome::Applied,
792 Ok(MemoryAdviceOutcome::SkippedEmpty) => PageAdviceOutcome::SkippedEmpty,
793 Ok(MemoryAdviceOutcome::Unsupported) => PageAdviceOutcome::Unsupported,
794 Err(error) => PageAdviceOutcome::Failed(error.to_string()),
795 },
796 None => PageAdviceOutcome::NotRequested,
797 };
798 let residency_after = observe_residency(mapping, section.offset, section.length);
799
800 PageAdviceApplication {
801 section: section.name.clone(),
802 policy,
803 requested,
804 residency_before,
805 outcome,
806 residency_after,
807 }
808 })
809 .collect()
810}
811
812fn observe_residency(mapping: &MappedFile, offset: u64, length: u64) -> PageResidencyOutcome {
813 match mapping.resident_pages(offset, length) {
814 Ok(MemoryResidency::Measured {
815 resident_pages,
816 total_pages,
817 }) => PageResidencyOutcome::Measured {
818 resident_pages,
819 total_pages,
820 },
821 Ok(MemoryResidency::Unsupported) => PageResidencyOutcome::Unsupported,
822 Err(error) => PageResidencyOutcome::Failed(error.to_string()),
823 }
824}
825
826impl FttsqReader {
827 pub fn open(bytes: &[u8]) -> Result<Self, FttsqError> {
836 let reader = Self::parse_directory(bytes)?;
837 reader.verify_digests(bytes)?;
838 Ok(reader)
839 }
840
841 pub fn parse_directory(bytes: &[u8]) -> Result<Self, FttsqError> {
851 Self::parse_directory_for_file_len(bytes, bytes.len() as u64)
852 }
853
854 fn parse_directory_for_file_len(bytes: &[u8], file_len: u64) -> Result<Self, FttsqError> {
861 let present_len = bytes.len() as u64;
862 if present_len < HEADER_PREFIX_BYTES {
863 return Err(FttsqError::TooShort {
864 length: present_len,
865 });
866 }
867
868 let mut magic = [0_u8; 8];
869 magic.copy_from_slice(&bytes[..8]);
870 if &magic != MAGIC {
871 return Err(FttsqError::BadMagic { found: magic });
872 }
873
874 let format_version = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]);
875 if format_version > FORMAT_VERSION {
876 return Err(FttsqError::UnsupportedVersion {
877 found: format_version,
878 supported: FORMAT_VERSION,
879 });
880 }
881
882 let mut length_bytes = [0_u8; 8];
883 length_bytes.copy_from_slice(&bytes[12..20]);
884 let directory_len = u64::from_le_bytes(length_bytes);
885 if directory_len > MAX_DIRECTORY_BYTES {
886 return Err(FttsqError::DirectoryLength {
887 declared: directory_len,
888 limit: MAX_DIRECTORY_BYTES,
889 });
890 }
891 let directory_end =
892 HEADER_PREFIX_BYTES
893 .checked_add(directory_len)
894 .ok_or(FttsqError::DirectoryLength {
895 declared: directory_len,
896 limit: u64::MAX,
897 })?;
898 if directory_end > present_len || directory_end > file_len {
899 return Err(FttsqError::DirectoryLength {
900 declared: directory_len,
901 limit: present_len.min(file_len),
902 });
903 }
904
905 let directory_bytes = &bytes[HEADER_PREFIX_BYTES as usize..directory_end as usize];
907 let directory: Value = serde_json::from_slice(directory_bytes).map_err(|error| {
908 FttsqError::DirectoryMalformed {
909 detail: error.to_string(),
910 }
911 })?;
912 let object = directory
913 .as_object()
914 .ok_or_else(|| FttsqError::DirectoryMalformed {
915 detail: "top level is not a JSON object".to_owned(),
916 })?;
917
918 let model_family = required_str(object.get("model_family"), "model_family")?.to_owned();
919 let source_sha256 = required_str(object.get("source_sha256"), "source_sha256")?.to_owned();
920
921 let license_notice = object
923 .get("license_notice")
924 .and_then(Value::as_str)
925 .unwrap_or_default()
926 .to_owned();
927 if license_notice.trim().is_empty() {
928 return Err(FttsqError::LicenseNoticeMissing);
929 }
930
931 let model_config = object.get("model_config").cloned().unwrap_or(Value::Null);
932 let quantization_manifest = object
933 .get("quantization_manifest")
934 .cloned()
935 .unwrap_or(Value::Null);
936
937 let sections = parse_sections(object.get("sections"), file_len)?;
938 let section_index: BTreeMap<String, usize> = sections
939 .iter()
940 .enumerate()
941 .map(|(index, section)| (section.name.clone(), index))
942 .collect();
943 let tensors = parse_tensors(object.get("tensors"), §ions, §ion_index)?;
944 let tensor_index: BTreeMap<String, usize> = tensors
945 .iter()
946 .enumerate()
947 .map(|(index, tensor)| (tensor.name.clone(), index))
948 .collect();
949
950 Ok(Self {
951 format_version,
952 model_family,
953 source_sha256,
954 license_notice,
955 model_config,
956 quantization_manifest,
957 sections,
958 tensors,
959 section_index,
960 tensor_index,
961 })
962 }
963
964 pub fn verify_digests(&self, bytes: &[u8]) -> Result<(), FttsqError> {
970 for section in &self.sections {
971 let payload = self.section_bytes(section, bytes)?;
972 let mut hasher = Sha256::new();
973 hasher.update(payload);
974 let actual = to_hex(&hasher.finish());
975 if actual != section.sha256 {
976 return Err(FttsqError::DigestMismatch {
977 section: section.name.clone(),
978 expected: section.sha256.clone(),
979 actual,
980 });
981 }
982 }
983 Ok(())
984 }
985
986 fn section_bytes<'a>(
987 &self,
988 section: &SectionEntry,
989 bytes: &'a [u8],
990 ) -> Result<&'a [u8], FttsqError> {
991 let end = section.end().ok_or_else(|| FttsqError::RangeOutOfBounds {
992 what: format!("section `{}`", section.name),
993 offset: section.offset,
994 length: section.length,
995 bound: bytes.len() as u64,
996 })?;
997 if end > bytes.len() as u64 {
998 return Err(FttsqError::RangeOutOfBounds {
999 what: format!("section `{}`", section.name),
1000 offset: section.offset,
1001 length: section.length,
1002 bound: bytes.len() as u64,
1003 });
1004 }
1005 Ok(&bytes[section.offset as usize..end as usize])
1006 }
1007
1008 #[must_use]
1010 pub const fn format_version(&self) -> u32 {
1011 self.format_version
1012 }
1013
1014 #[must_use]
1016 pub fn model_family(&self) -> &str {
1017 &self.model_family
1018 }
1019
1020 #[must_use]
1022 pub fn source_sha256(&self) -> &str {
1023 &self.source_sha256
1024 }
1025
1026 #[must_use]
1028 pub fn license_notice(&self) -> &str {
1029 &self.license_notice
1030 }
1031
1032 #[must_use]
1034 pub const fn model_config(&self) -> &Value {
1035 &self.model_config
1036 }
1037
1038 #[must_use]
1040 pub const fn quantization_manifest(&self) -> &Value {
1041 &self.quantization_manifest
1042 }
1043
1044 #[must_use]
1046 pub fn sections(&self) -> &[SectionEntry] {
1047 &self.sections
1048 }
1049
1050 #[must_use]
1052 pub fn tensors(&self) -> &[TensorEntry] {
1053 &self.tensors
1054 }
1055
1056 #[must_use]
1058 pub fn section(&self, name: &str) -> Option<&SectionEntry> {
1059 self.section_index
1060 .get(name)
1061 .and_then(|&index| self.sections.get(index))
1062 }
1063
1064 #[must_use]
1066 pub fn tensor(&self, name: &str) -> Option<&TensorEntry> {
1067 self.tensor_index
1068 .get(name)
1069 .and_then(|&index| self.tensors.get(index))
1070 }
1071
1072 #[must_use]
1074 pub fn sections_in_class(&self, class: AccessClass) -> Vec<&SectionEntry> {
1075 self.sections
1076 .iter()
1077 .filter(|section| section.access_class == class)
1078 .collect()
1079 }
1080
1081 pub fn tensor_bytes<'a>(&self, name: &str, bytes: &'a [u8]) -> Result<&'a [u8], FttsqError> {
1088 let tensor = self
1089 .tensor(name)
1090 .ok_or_else(|| FttsqError::UnknownSection {
1091 tensor: name.to_owned(),
1092 section: "<unknown tensor>".to_owned(),
1093 })?;
1094 let section = self
1095 .section(&tensor.section)
1096 .ok_or_else(|| FttsqError::UnknownSection {
1097 tensor: tensor.name.clone(),
1098 section: tensor.section.clone(),
1099 })?;
1100 let payload = self.section_bytes(section, bytes)?;
1101 let end = tensor.offset.checked_add(tensor.length).ok_or_else(|| {
1102 FttsqError::RangeOutOfBounds {
1103 what: format!("tensor `{}`", tensor.name),
1104 offset: tensor.offset,
1105 length: tensor.length,
1106 bound: payload.len() as u64,
1107 }
1108 })?;
1109 if end > payload.len() as u64 {
1110 return Err(FttsqError::RangeOutOfBounds {
1111 what: format!("tensor `{}`", tensor.name),
1112 offset: tensor.offset,
1113 length: tensor.length,
1114 bound: payload.len() as u64,
1115 });
1116 }
1117 Ok(&payload[tensor.offset as usize..end as usize])
1118 }
1119
1120 #[must_use]
1130 pub fn page_in_plan(&self) -> Vec<(&SectionEntry, PagePolicy)> {
1131 let mut plan: Vec<(&SectionEntry, PagePolicy)> = self
1132 .sections
1133 .iter()
1134 .map(|section| (section, section.access_class.page_policy()))
1135 .collect();
1136 plan.sort_by_key(|(section, policy)| {
1137 let rank = match policy {
1138 PagePolicy::Resident => 0_u8,
1139 PagePolicy::LazyRowGranular => 1,
1140 PagePolicy::OnDemand => 2,
1141 };
1142 (rank, section.length)
1143 });
1144 plan
1145 }
1146
1147 pub fn verify_census(&self, manifest: &ArtifactManifest) -> Result<(), Box<ArtifactCensus>> {
1153 let report = manifest.audit(self);
1154 if report.is_green() {
1155 Ok(())
1156 } else {
1157 Err(Box::new(report))
1158 }
1159 }
1160}
1161
1162#[derive(Clone, Debug, PartialEq, Eq)]
1172pub struct ExpectedArtifactTensor {
1173 pub name: String,
1175 pub shape: Vec<u64>,
1177 pub dtype: StoredDtype,
1179 pub access_class: AccessClass,
1181}
1182
1183#[derive(Clone, Debug, PartialEq, Eq)]
1185pub enum ArtifactFinding {
1186 Missing {
1188 name: String,
1190 },
1191 Extra {
1194 name: String,
1196 },
1197 ShapeMismatch {
1199 name: String,
1201 expected: Vec<u64>,
1203 found: Vec<u64>,
1205 },
1206 DtypeMismatch {
1208 name: String,
1210 expected: StoredDtype,
1212 found: StoredDtype,
1214 },
1215 WrongAccessClass {
1219 name: String,
1221 expected: AccessClass,
1223 found: AccessClass,
1225 },
1226 DanglingSection {
1228 name: String,
1230 section: String,
1232 },
1233}
1234
1235impl ArtifactFinding {
1236 #[must_use]
1238 pub fn tensor(&self) -> &str {
1239 match self {
1240 Self::Missing { name }
1241 | Self::Extra { name }
1242 | Self::ShapeMismatch { name, .. }
1243 | Self::DtypeMismatch { name, .. }
1244 | Self::WrongAccessClass { name, .. }
1245 | Self::DanglingSection { name, .. } => name,
1246 }
1247 }
1248
1249 #[must_use]
1251 pub const fn class(&self) -> &'static str {
1252 match self {
1253 Self::Missing { .. } => "missing",
1254 Self::Extra { .. } => "extra",
1255 Self::ShapeMismatch { .. } => "shape_mismatch",
1256 Self::DtypeMismatch { .. } => "dtype_mismatch",
1257 Self::WrongAccessClass { .. } => "wrong_access_class",
1258 Self::DanglingSection { .. } => "dangling_section",
1259 }
1260 }
1261}
1262
1263impl fmt::Display for ArtifactFinding {
1264 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1265 match self {
1266 Self::Missing { name } => write!(f, "MISSING {name}"),
1267 Self::Extra { name } => write!(f, "EXTRA {name}"),
1268 Self::ShapeMismatch {
1269 name,
1270 expected,
1271 found,
1272 } => write!(
1273 f,
1274 "SHAPE {name}: expected {expected:?}, found {found:?}"
1275 ),
1276 Self::DtypeMismatch {
1277 name,
1278 expected,
1279 found,
1280 } => write!(
1281 f,
1282 "DTYPE {name}: expected {expected}, found {found}"
1283 ),
1284 Self::WrongAccessClass {
1285 name,
1286 expected,
1287 found,
1288 } => write!(
1289 f,
1290 "ACCESS_CLASS {name}: expected {expected}, found {found}"
1291 ),
1292 Self::DanglingSection { name, section } => {
1293 write!(
1294 f,
1295 "DANGLING {name}: names undeclared section `{section}`"
1296 )
1297 }
1298 }
1299 }
1300}
1301
1302#[derive(Clone, Debug, Default)]
1308pub struct ArtifactManifest {
1309 label: String,
1310 expected: Vec<ExpectedArtifactTensor>,
1311}
1312
1313impl ArtifactManifest {
1314 #[must_use]
1316 pub fn new(label: impl Into<String>) -> Self {
1317 Self {
1318 label: label.into(),
1319 expected: Vec::new(),
1320 }
1321 }
1322
1323 #[must_use]
1325 pub fn expect(mut self, tensor: ExpectedArtifactTensor) -> Self {
1326 self.expected.push(tensor);
1327 self
1328 }
1329
1330 #[must_use]
1332 pub fn label(&self) -> &str {
1333 &self.label
1334 }
1335
1336 #[must_use]
1338 pub fn len(&self) -> usize {
1339 self.expected.len()
1340 }
1341
1342 #[must_use]
1344 pub fn is_empty(&self) -> bool {
1345 self.expected.is_empty()
1346 }
1347
1348 #[must_use]
1354 pub fn audit(&self, reader: &FttsqReader) -> ArtifactCensus {
1355 let mut findings = Vec::new();
1356 let expected_names: BTreeMap<&str, &ExpectedArtifactTensor> = self
1357 .expected
1358 .iter()
1359 .map(|tensor| (tensor.name.as_str(), tensor))
1360 .collect();
1361
1362 for expectation in &self.expected {
1363 let Some(found) = reader.tensor(&expectation.name) else {
1364 findings.push(ArtifactFinding::Missing {
1365 name: expectation.name.clone(),
1366 });
1367 continue;
1368 };
1369 if found.shape != expectation.shape {
1370 findings.push(ArtifactFinding::ShapeMismatch {
1371 name: expectation.name.clone(),
1372 expected: expectation.shape.clone(),
1373 found: found.shape.clone(),
1374 });
1375 }
1376 if found.dtype != expectation.dtype {
1377 findings.push(ArtifactFinding::DtypeMismatch {
1378 name: expectation.name.clone(),
1379 expected: expectation.dtype,
1380 found: found.dtype,
1381 });
1382 }
1383 match reader.section(&found.section) {
1384 Some(section) if section.access_class != expectation.access_class => {
1385 findings.push(ArtifactFinding::WrongAccessClass {
1386 name: expectation.name.clone(),
1387 expected: expectation.access_class,
1388 found: section.access_class,
1389 });
1390 }
1391 Some(_) => {}
1392 None => findings.push(ArtifactFinding::DanglingSection {
1393 name: expectation.name.clone(),
1394 section: found.section.clone(),
1395 }),
1396 }
1397 }
1398
1399 for tensor in reader.tensors() {
1400 if !expected_names.contains_key(tensor.name.as_str()) {
1401 findings.push(ArtifactFinding::Extra {
1402 name: tensor.name.clone(),
1403 });
1404 }
1405 }
1406
1407 ArtifactCensus {
1408 label: self.label.clone(),
1409 expected: self.expected.len(),
1410 found: reader.tensors().len(),
1411 findings,
1412 }
1413 }
1414}
1415
1416#[derive(Clone, Debug)]
1418pub struct ArtifactCensus {
1419 label: String,
1420 expected: usize,
1421 found: usize,
1422 findings: Vec<ArtifactFinding>,
1423}
1424
1425impl ArtifactCensus {
1426 #[must_use]
1428 pub fn is_green(&self) -> bool {
1429 self.findings.is_empty()
1430 }
1431
1432 #[must_use]
1434 pub fn findings(&self) -> &[ArtifactFinding] {
1435 &self.findings
1436 }
1437
1438 #[must_use]
1440 pub fn count_of(&self, class: &str) -> usize {
1441 self.findings
1442 .iter()
1443 .filter(|finding| finding.class() == class)
1444 .count()
1445 }
1446
1447 #[must_use]
1449 pub fn render(&self) -> String {
1450 let mut out = format!(
1451 "artifact census `{}`: expected {} tensors, artifact declares {} — {}\n",
1452 self.label,
1453 self.expected,
1454 self.found,
1455 if self.is_green() {
1456 "GREEN".to_owned()
1457 } else {
1458 format!("{} FINDINGS", self.findings.len())
1459 }
1460 );
1461 for finding in &self.findings {
1462 out.push_str(&format!(" {finding}\n"));
1463 }
1464 out
1465 }
1466}
1467
1468impl fmt::Display for ArtifactCensus {
1469 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1470 f.write_str(&self.render())
1471 }
1472}
1473
1474impl std::error::Error for ArtifactCensus {}
1475
1476fn required_str<'a>(value: Option<&'a Value>, path: &str) -> Result<&'a str, FttsqError> {
1477 value
1478 .and_then(Value::as_str)
1479 .filter(|text| !text.is_empty())
1480 .ok_or_else(|| FttsqError::Field {
1481 path: path.to_owned(),
1482 expected: "a non-empty string".to_owned(),
1483 })
1484}
1485
1486fn required_u64(value: Option<&Value>, path: &str) -> Result<u64, FttsqError> {
1487 value
1488 .and_then(Value::as_u64)
1489 .ok_or_else(|| FttsqError::Field {
1490 path: path.to_owned(),
1491 expected: "a non-negative integer".to_owned(),
1492 })
1493}
1494
1495fn parse_sections(value: Option<&Value>, file_len: u64) -> Result<Vec<SectionEntry>, FttsqError> {
1496 let array = value
1497 .and_then(Value::as_array)
1498 .ok_or_else(|| FttsqError::Field {
1499 path: "sections".to_owned(),
1500 expected: "an array".to_owned(),
1501 })?;
1502 if array.len() > MAX_SECTIONS {
1503 return Err(FttsqError::LimitExceeded {
1504 what: "section".to_owned(),
1505 found: array.len() as u64,
1506 limit: MAX_SECTIONS as u64,
1507 });
1508 }
1509
1510 let mut sections = Vec::with_capacity(array.len());
1511 let mut seen: BTreeMap<String, ()> = BTreeMap::new();
1512 for (index, entry) in array.iter().enumerate() {
1513 let path = |field: &str| format!("sections[{index}].{field}");
1514 let name = required_str(entry.get("name"), &path("name"))?.to_owned();
1515 if seen.insert(name.clone(), ()).is_some() {
1516 return Err(FttsqError::DuplicateName {
1517 what: "section".to_owned(),
1518 name,
1519 });
1520 }
1521 let class_text = required_str(entry.get("access_class"), &path("access_class"))?;
1522 let access_class =
1523 AccessClass::parse(class_text).ok_or_else(|| FttsqError::UnknownValue {
1524 path: path("access_class"),
1525 found: class_text.to_owned(),
1526 })?;
1527 let offset = required_u64(entry.get("offset"), &path("offset"))?;
1528 let length = required_u64(entry.get("length"), &path("length"))?;
1529 let sha256 = required_str(entry.get("sha256"), &path("sha256"))?.to_owned();
1530
1531 let end = offset
1532 .checked_add(length)
1533 .ok_or_else(|| FttsqError::RangeOutOfBounds {
1534 what: format!("section `{name}`"),
1535 offset,
1536 length,
1537 bound: file_len,
1538 })?;
1539 if end > file_len {
1540 return Err(FttsqError::RangeOutOfBounds {
1541 what: format!("section `{name}`"),
1542 offset,
1543 length,
1544 bound: file_len,
1545 });
1546 }
1547
1548 sections.push(SectionEntry {
1549 name,
1550 access_class,
1551 offset,
1552 length,
1553 sha256,
1554 });
1555 }
1556
1557 let mut ordered: Vec<&SectionEntry> = sections.iter().collect();
1559 ordered.sort_by_key(|section| section.offset);
1560 for pair in ordered.windows(2) {
1561 let (first, second) = (pair[0], pair[1]);
1562 let first_end = first.end().unwrap_or(u64::MAX);
1563 if first_end > second.offset {
1564 return Err(FttsqError::SectionOverlap {
1565 first: first.name.clone(),
1566 second: second.name.clone(),
1567 });
1568 }
1569 }
1570
1571 Ok(sections)
1572}
1573
1574fn parse_tensors(
1575 value: Option<&Value>,
1576 sections: &[SectionEntry],
1577 section_index: &BTreeMap<String, usize>,
1578) -> Result<Vec<TensorEntry>, FttsqError> {
1579 let array = value
1580 .and_then(Value::as_array)
1581 .ok_or_else(|| FttsqError::Field {
1582 path: "tensors".to_owned(),
1583 expected: "an array".to_owned(),
1584 })?;
1585 if array.len() > MAX_TENSORS {
1586 return Err(FttsqError::LimitExceeded {
1587 what: "tensor".to_owned(),
1588 found: array.len() as u64,
1589 limit: MAX_TENSORS as u64,
1590 });
1591 }
1592
1593 let mut tensors = Vec::with_capacity(array.len());
1594 let mut seen: BTreeMap<String, ()> = BTreeMap::new();
1595 for (index, entry) in array.iter().enumerate() {
1596 let path = |field: &str| format!("tensors[{index}].{field}");
1597 let name = required_str(entry.get("name"), &path("name"))?.to_owned();
1598 if seen.insert(name.clone(), ()).is_some() {
1599 return Err(FttsqError::DuplicateName {
1600 what: "tensor".to_owned(),
1601 name,
1602 });
1603 }
1604 let section = required_str(entry.get("section"), &path("section"))?.to_owned();
1605 let dtype_text = required_str(entry.get("dtype"), &path("dtype"))?;
1606 let dtype = StoredDtype::parse(dtype_text).ok_or_else(|| FttsqError::UnknownValue {
1607 path: path("dtype"),
1608 found: dtype_text.to_owned(),
1609 })?;
1610
1611 let shape_array = entry
1612 .get("shape")
1613 .and_then(Value::as_array)
1614 .ok_or_else(|| FttsqError::Field {
1615 path: path("shape"),
1616 expected: "an array".to_owned(),
1617 })?;
1618 if shape_array.len() > MAX_RANK {
1619 return Err(FttsqError::LimitExceeded {
1620 what: format!("tensor `{name}` rank"),
1621 found: shape_array.len() as u64,
1622 limit: MAX_RANK as u64,
1623 });
1624 }
1625 let mut shape = Vec::with_capacity(shape_array.len());
1626 for (axis, dim) in shape_array.iter().enumerate() {
1627 let dim = dim.as_u64().ok_or_else(|| FttsqError::Field {
1628 path: format!("{}[{axis}]", path("shape")),
1629 expected: "a non-negative integer".to_owned(),
1630 })?;
1631 if dim > MAX_DIM {
1632 return Err(FttsqError::LimitExceeded {
1633 what: format!("tensor `{name}` dimension {axis}"),
1634 found: dim,
1635 limit: MAX_DIM,
1636 });
1637 }
1638 shape.push(dim);
1639 }
1640
1641 let offset = required_u64(entry.get("offset"), &path("offset"))?;
1642 let length = required_u64(entry.get("length"), &path("length"))?;
1643 let scales = entry
1644 .get("scales")
1645 .and_then(Value::as_str)
1646 .map(str::to_owned);
1647
1648 let tensor = TensorEntry {
1649 name,
1650 section,
1651 dtype,
1652 shape,
1653 offset,
1654 length,
1655 scales,
1656 };
1657
1658 let elements = tensor.elements().ok_or_else(|| FttsqError::LimitExceeded {
1662 what: format!("tensor `{}` element count", tensor.name),
1663 found: u64::MAX,
1664 limit: MAX_DIM,
1665 })?;
1666 let implied = dtype
1667 .storage_bytes(elements)
1668 .ok_or_else(|| FttsqError::LimitExceeded {
1669 what: format!("tensor `{}` storage size", tensor.name),
1670 found: u64::MAX,
1671 limit: MAX_DIM,
1672 })?;
1673 if implied != tensor.length {
1674 return Err(FttsqError::LengthMismatch {
1675 tensor: tensor.name.clone(),
1676 declared: tensor.length,
1677 implied,
1678 });
1679 }
1680
1681 let owner = section_index
1682 .get(&tensor.section)
1683 .and_then(|&index| sections.get(index))
1684 .ok_or_else(|| FttsqError::UnknownSection {
1685 tensor: tensor.name.clone(),
1686 section: tensor.section.clone(),
1687 })?;
1688 let end = tensor.offset.checked_add(tensor.length).ok_or_else(|| {
1689 FttsqError::RangeOutOfBounds {
1690 what: format!("tensor `{}`", tensor.name),
1691 offset: tensor.offset,
1692 length: tensor.length,
1693 bound: owner.length,
1694 }
1695 })?;
1696 if end > owner.length {
1697 return Err(FttsqError::RangeOutOfBounds {
1698 what: format!("tensor `{}`", tensor.name),
1699 offset: tensor.offset,
1700 length: tensor.length,
1701 bound: owner.length,
1702 });
1703 }
1704
1705 tensors.push(tensor);
1706 }
1707
1708 let mut by_section: BTreeMap<&str, Vec<&TensorEntry>> = BTreeMap::new();
1711 for tensor in &tensors {
1712 by_section
1713 .entry(tensor.section.as_str())
1714 .or_default()
1715 .push(tensor);
1716 }
1717 for group in by_section.values_mut() {
1718 group.sort_by_key(|tensor| tensor.offset);
1719 for pair in group.windows(2) {
1720 let (first, second) = (pair[0], pair[1]);
1721 let first_end = first.offset.saturating_add(first.length);
1722 if first_end > second.offset {
1723 return Err(FttsqError::TensorOverlap {
1724 first: first.name.clone(),
1725 second: second.name.clone(),
1726 });
1727 }
1728 }
1729 }
1730
1731 Ok(tensors)
1732}
1733
1734#[derive(Debug)]
1742pub struct FttsqStreamPlan {
1743 model_family: String,
1744 source_sha256: String,
1745 license_notice: String,
1746 model_config: Value,
1747 quantization_manifest: Value,
1748 sections: Vec<(String, AccessClass, u64)>,
1749 tensors: Vec<TensorEntry>,
1750}
1751
1752impl FttsqStreamPlan {
1753 #[must_use]
1755 pub fn new(model_family: impl Into<String>, source_sha256: impl Into<String>) -> Self {
1756 Self {
1757 model_family: model_family.into(),
1758 source_sha256: source_sha256.into(),
1759 license_notice: String::new(),
1760 model_config: Value::Null,
1761 quantization_manifest: Value::Null,
1762 sections: Vec::new(),
1763 tensors: Vec::new(),
1764 }
1765 }
1766
1767 #[must_use]
1769 pub fn license_notice(mut self, notice: impl Into<String>) -> Self {
1770 self.license_notice = notice.into();
1771 self
1772 }
1773
1774 #[must_use]
1776 pub fn model_config(mut self, config: Value) -> Self {
1777 self.model_config = config;
1778 self
1779 }
1780
1781 #[must_use]
1783 pub fn quantization_manifest(mut self, manifest: Value) -> Self {
1784 self.quantization_manifest = manifest;
1785 self
1786 }
1787
1788 #[must_use]
1790 pub fn section(
1791 mut self,
1792 name: impl Into<String>,
1793 access_class: AccessClass,
1794 length: u64,
1795 ) -> Self {
1796 self.sections.push((name.into(), access_class, length));
1797 self
1798 }
1799
1800 #[must_use]
1802 pub fn tensor(mut self, tensor: TensorEntry) -> Self {
1803 self.tensors.push(tensor);
1804 self
1805 }
1806
1807 pub fn begin<W: std::io::Write + std::io::Seek>(
1819 self,
1820 mut writer: W,
1821 ) -> Result<FttsqStreamingWriter<W>, FttsqError> {
1822 if self.license_notice.trim().is_empty() {
1823 return Err(FttsqError::LicenseNoticeMissing);
1824 }
1825
1826 let mut sections: Vec<SectionEntry> = self
1830 .sections
1831 .into_iter()
1832 .map(|(name, access_class, length)| SectionEntry {
1833 name,
1834 access_class,
1835 offset: 0,
1836 length,
1837 sha256: "0".repeat(64),
1838 })
1839 .collect();
1840 let mut probe_sections = sections.clone();
1841 for section in &mut probe_sections {
1842 section.offset = u64::MAX;
1845 }
1846 let probe = stream_directory_json(
1847 &self.model_family,
1848 &self.source_sha256,
1849 &self.license_notice,
1850 &self.model_config,
1851 &self.quantization_manifest,
1852 &probe_sections,
1853 &self.tensors,
1854 );
1855 let directory_len = serde_json::to_vec(&probe)
1856 .map_err(|error| FttsqError::DirectoryMalformed {
1857 detail: error.to_string(),
1858 })?
1859 .len() as u64;
1860 if directory_len > MAX_DIRECTORY_BYTES {
1861 return Err(FttsqError::DirectoryLength {
1862 declared: directory_len,
1863 limit: MAX_DIRECTORY_BYTES,
1864 });
1865 }
1866 let payload_start =
1867 HEADER_PREFIX_BYTES
1868 .checked_add(directory_len)
1869 .ok_or(FttsqError::DirectoryLength {
1870 declared: directory_len,
1871 limit: u64::MAX,
1872 })?;
1873 let final_file_len = layout_stream_sections(&mut sections, payload_start)?;
1874
1875 let directory = stream_directory_json(
1876 &self.model_family,
1877 &self.source_sha256,
1878 &self.license_notice,
1879 &self.model_config,
1880 &self.quantization_manifest,
1881 §ions,
1882 &self.tensors,
1883 );
1884 let mut directory_bytes =
1885 serde_json::to_vec(&directory).map_err(|error| FttsqError::DirectoryMalformed {
1886 detail: error.to_string(),
1887 })?;
1888 if directory_bytes.len() as u64 > directory_len {
1889 return Err(FttsqError::DirectoryLength {
1890 declared: directory_bytes.len() as u64,
1891 limit: directory_len,
1892 });
1893 }
1894 directory_bytes.resize(directory_len as usize, b' ');
1895
1896 let mut header_and_directory = Vec::with_capacity(
1897 (HEADER_PREFIX_BYTES as usize).saturating_add(directory_bytes.len()),
1898 );
1899 header_and_directory.extend_from_slice(MAGIC);
1900 header_and_directory.extend_from_slice(&FORMAT_VERSION.to_le_bytes());
1901 header_and_directory.extend_from_slice(&directory_len.to_le_bytes());
1902 header_and_directory.extend_from_slice(&directory_bytes);
1903
1904 FttsqReader::parse_directory_for_file_len(&header_and_directory, final_file_len)?;
1907 writer
1908 .write_all(&header_and_directory)
1909 .map_err(|error| stream_io_error("write header and directory", &error))?;
1910
1911 let mut streaming = FttsqStreamingWriter {
1912 writer,
1913 model_family: self.model_family,
1914 source_sha256: self.source_sha256,
1915 license_notice: self.license_notice,
1916 model_config: self.model_config,
1917 quantization_manifest: self.quantization_manifest,
1918 sections,
1919 tensors: self.tensors,
1920 directory_len,
1921 current_section: 0,
1922 section_written: 0,
1923 section_hasher: Sha256::new(),
1924 };
1925 streaming.finalize_empty_sections();
1926 Ok(streaming)
1927 }
1928}
1929
1930#[derive(Debug)]
1937pub struct FttsqStreamingWriter<W> {
1938 writer: W,
1939 model_family: String,
1940 source_sha256: String,
1941 license_notice: String,
1942 model_config: Value,
1943 quantization_manifest: Value,
1944 sections: Vec<SectionEntry>,
1945 tensors: Vec<TensorEntry>,
1946 directory_len: u64,
1947 current_section: usize,
1948 section_written: u64,
1949 section_hasher: Sha256,
1950}
1951
1952impl<W: std::io::Write + std::io::Seek> FttsqStreamingWriter<W> {
1953 pub fn write_section(&mut self, section: &str, bytes: &[u8]) -> Result<(), FttsqError> {
1964 let Some(entry) = self.sections.get(self.current_section) else {
1965 return Err(FttsqError::SectionWriteOutOfOrder {
1966 expected: None,
1967 actual: section.to_owned(),
1968 });
1969 };
1970 let expected = entry.name.clone();
1971 let declared = entry.length;
1972 if expected != section {
1973 return Err(FttsqError::SectionWriteOutOfOrder {
1974 expected: Some(expected),
1975 actual: section.to_owned(),
1976 });
1977 }
1978 let bytes_len = bytes.len() as u64;
1979 let attempted = self.section_written.checked_add(bytes_len).ok_or_else(|| {
1980 FttsqError::SectionLengthExceeded {
1981 section: expected.clone(),
1982 declared,
1983 attempted: u64::MAX,
1984 }
1985 })?;
1986 if attempted > declared {
1987 return Err(FttsqError::SectionLengthExceeded {
1988 section: expected,
1989 declared,
1990 attempted,
1991 });
1992 }
1993
1994 self.writer
1995 .write_all(bytes)
1996 .map_err(|error| stream_io_error("write section", &error))?;
1997 self.section_hasher.update(bytes);
1998 self.section_written = attempted;
1999 self.finalize_empty_sections();
2000 Ok(())
2001 }
2002
2003 pub fn finish(mut self) -> Result<W, FttsqError> {
2014 if let Some(section) = self.sections.get(self.current_section) {
2015 return Err(FttsqError::SectionIncomplete {
2016 section: section.name.clone(),
2017 declared: section.length,
2018 written: self.section_written,
2019 });
2020 }
2021
2022 let directory = stream_directory_json(
2023 &self.model_family,
2024 &self.source_sha256,
2025 &self.license_notice,
2026 &self.model_config,
2027 &self.quantization_manifest,
2028 &self.sections,
2029 &self.tensors,
2030 );
2031 let directory_bytes =
2032 serde_json::to_vec(&directory).map_err(|error| FttsqError::DirectoryMalformed {
2033 detail: error.to_string(),
2034 })?;
2035 if directory_bytes.len() as u64 > self.directory_len {
2036 return Err(FttsqError::DirectoryLength {
2037 declared: directory_bytes.len() as u64,
2038 limit: self.directory_len,
2039 });
2040 }
2041
2042 self.writer
2043 .seek(std::io::SeekFrom::Start(HEADER_PREFIX_BYTES))
2044 .map_err(|error| stream_io_error("seek to directory", &error))?;
2045 self.writer
2046 .write_all(&directory_bytes)
2047 .map_err(|error| stream_io_error("finalize directory", &error))?;
2048 write_space_padding(
2049 &mut self.writer,
2050 self.directory_len - directory_bytes.len() as u64,
2051 )?;
2052 self.writer
2053 .seek(std::io::SeekFrom::End(0))
2054 .map_err(|error| stream_io_error("seek to artifact end", &error))?;
2055 self.writer
2056 .flush()
2057 .map_err(|error| stream_io_error("flush finalized artifact", &error))?;
2058 Ok(self.writer)
2059 }
2060
2061 fn finalize_empty_sections(&mut self) {
2062 while let Some(section) = self.sections.get_mut(self.current_section) {
2063 if self.section_written != section.length {
2064 break;
2065 }
2066 section.sha256 = to_hex(&std::mem::take(&mut self.section_hasher).finish());
2067 self.current_section += 1;
2068 self.section_written = 0;
2069 }
2070 }
2071}
2072
2073fn layout_stream_sections(
2074 sections: &mut [SectionEntry],
2075 payload_start: u64,
2076) -> Result<u64, FttsqError> {
2077 let mut cursor = payload_start;
2078 for section in sections {
2079 section.offset = cursor;
2080 cursor =
2081 cursor
2082 .checked_add(section.length)
2083 .ok_or_else(|| FttsqError::RangeOutOfBounds {
2084 what: format!("section `{}`", section.name),
2085 offset: section.offset,
2086 length: section.length,
2087 bound: u64::MAX,
2088 })?;
2089 }
2090 Ok(cursor)
2091}
2092
2093fn stream_directory_json(
2094 model_family: &str,
2095 source_sha256: &str,
2096 license_notice: &str,
2097 model_config: &Value,
2098 quantization_manifest: &Value,
2099 sections: &[SectionEntry],
2100 tensors: &[TensorEntry],
2101) -> Value {
2102 let sections: Vec<Value> = sections
2103 .iter()
2104 .map(|section| {
2105 json!({
2106 "name": section.name,
2107 "access_class": section.access_class.as_str(),
2108 "offset": section.offset,
2109 "length": section.length,
2110 "sha256": section.sha256,
2111 })
2112 })
2113 .collect();
2114 let tensors: Vec<Value> = tensors
2115 .iter()
2116 .map(|tensor| {
2117 json!({
2118 "name": tensor.name,
2119 "section": tensor.section,
2120 "dtype": tensor.dtype.as_str(),
2121 "shape": tensor.shape,
2122 "offset": tensor.offset,
2123 "length": tensor.length,
2124 "scales": tensor.scales,
2125 })
2126 })
2127 .collect();
2128 json!({
2129 "format_version": FORMAT_VERSION,
2130 "model_family": model_family,
2131 "source_sha256": source_sha256,
2132 "license_notice": license_notice,
2133 "model_config": model_config,
2134 "quantization_manifest": quantization_manifest,
2135 "sections": sections,
2136 "tensors": tensors,
2137 })
2138}
2139
2140fn stream_io_error(operation: &str, error: &std::io::Error) -> FttsqError {
2141 FttsqError::Io {
2142 operation: operation.to_owned(),
2143 path: "<fttsq stream>".to_owned(),
2144 detail: error.to_string(),
2145 }
2146}
2147
2148fn write_space_padding<W: std::io::Write>(
2149 writer: &mut W,
2150 mut remaining: u64,
2151) -> Result<(), FttsqError> {
2152 const SPACES: [u8; 4096] = [b' '; 4096];
2153 while remaining > 0 {
2154 let count = remaining.min(SPACES.len() as u64) as usize;
2155 writer
2156 .write_all(&SPACES[..count])
2157 .map_err(|error| stream_io_error("pad finalized directory", &error))?;
2158 remaining -= count as u64;
2159 }
2160 Ok(())
2161}
2162
2163#[derive(Debug, Default)]
2168pub struct FttsqWriter {
2169 model_family: String,
2170 source_sha256: String,
2171 license_notice: String,
2172 model_config: Value,
2173 quantization_manifest: Value,
2174 sections: Vec<(SectionEntry, Vec<u8>)>,
2175 tensors: Vec<TensorEntry>,
2176}
2177
2178impl FttsqWriter {
2179 #[must_use]
2181 pub fn new(model_family: impl Into<String>, source_sha256: impl Into<String>) -> Self {
2182 Self {
2183 model_family: model_family.into(),
2184 source_sha256: source_sha256.into(),
2185 license_notice: String::new(),
2186 model_config: Value::Null,
2187 quantization_manifest: Value::Null,
2188 sections: Vec::new(),
2189 tensors: Vec::new(),
2190 }
2191 }
2192
2193 #[must_use]
2195 pub fn license_notice(mut self, notice: impl Into<String>) -> Self {
2196 self.license_notice = notice.into();
2197 self
2198 }
2199
2200 #[must_use]
2202 pub fn model_config(mut self, config: Value) -> Self {
2203 self.model_config = config;
2204 self
2205 }
2206
2207 #[must_use]
2209 pub fn quantization_manifest(mut self, manifest: Value) -> Self {
2210 self.quantization_manifest = manifest;
2211 self
2212 }
2213
2214 #[must_use]
2216 pub fn section(
2217 mut self,
2218 name: impl Into<String>,
2219 access_class: AccessClass,
2220 payload: Vec<u8>,
2221 ) -> Self {
2222 let entry = SectionEntry {
2223 name: name.into(),
2224 access_class,
2225 offset: 0,
2226 length: payload.len() as u64,
2227 sha256: String::new(),
2228 };
2229 self.sections.push((entry, payload));
2230 self
2231 }
2232
2233 #[must_use]
2235 pub fn tensor(mut self, tensor: TensorEntry) -> Self {
2236 self.tensors.push(tensor);
2237 self
2238 }
2239
2240 pub fn finish(mut self) -> Result<Vec<u8>, FttsqError> {
2250 if self.license_notice.trim().is_empty() {
2251 return Err(FttsqError::LicenseNoticeMissing);
2252 }
2253
2254 for (entry, payload) in &mut self.sections {
2255 entry.length = payload.len() as u64;
2256 entry.sha256 = hex_digest(payload);
2257 }
2258
2259 let probe = self.directory_json(u64::MAX);
2264 let probe_len = serde_json::to_vec(&probe)
2265 .map_err(|error| FttsqError::DirectoryMalformed {
2266 detail: error.to_string(),
2267 })?
2268 .len() as u64;
2269
2270 let payload_start = HEADER_PREFIX_BYTES + probe_len;
2271 let directory = self.directory_json(payload_start);
2272 let mut directory_bytes =
2273 serde_json::to_vec(&directory).map_err(|error| FttsqError::DirectoryMalformed {
2274 detail: error.to_string(),
2275 })?;
2276 while (directory_bytes.len() as u64) < probe_len {
2279 directory_bytes.push(b' ');
2280 }
2281
2282 let mut out = Vec::with_capacity(payload_start as usize);
2283 out.extend_from_slice(MAGIC);
2284 out.extend_from_slice(&FORMAT_VERSION.to_le_bytes());
2285 out.extend_from_slice(&(directory_bytes.len() as u64).to_le_bytes());
2286 out.extend_from_slice(&directory_bytes);
2287 for (_, payload) in &self.sections {
2288 out.extend_from_slice(payload);
2289 }
2290
2291 FttsqReader::open(&out)?;
2293 Ok(out)
2294 }
2295
2296 pub fn write_to_path(self, path: &std::path::Path) -> Result<(), FttsqError> {
2312 use std::io::Write as _;
2313
2314 let bytes = self.finish()?;
2315
2316 let parent = path.parent().unwrap_or_else(|| std::path::Path::new("."));
2317 let file_name = path.file_name().map_or_else(
2319 || std::ffi::OsString::from("artifact.fttsq"),
2320 std::ffi::OsStr::to_os_string,
2321 );
2322 let mut temp_name = file_name;
2323 temp_name.push(format!(".tmp.{}", std::process::id()));
2324 let temp_path = parent.join(temp_name);
2325
2326 let io =
2327 |operation: &str, target: &std::path::Path, error: &std::io::Error| FttsqError::Io {
2328 operation: operation.to_owned(),
2329 path: target.display().to_string(),
2330 detail: error.to_string(),
2331 };
2332
2333 let result = (|| -> Result<(), FttsqError> {
2335 let mut file = std::fs::File::create(&temp_path)
2336 .map_err(|error| io("create", &temp_path, &error))?;
2337 file.write_all(&bytes)
2338 .map_err(|error| io("write", &temp_path, &error))?;
2339 file.sync_all()
2342 .map_err(|error| io("fsync", &temp_path, &error))?;
2343 drop(file);
2344 std::fs::rename(&temp_path, path).map_err(|error| io("rename", path, &error))
2345 })();
2346
2347 if result.is_err() {
2348 let _ = std::fs::remove_file(&temp_path);
2349 }
2350 result
2351 }
2352
2353 fn directory_json(&self, payload_start: u64) -> Value {
2354 let mut cursor = payload_start;
2355 let sections: Vec<Value> = self
2356 .sections
2357 .iter()
2358 .map(|(entry, _)| {
2359 let offset = cursor;
2360 cursor = cursor.saturating_add(entry.length);
2365 json!({
2366 "name": entry.name,
2367 "access_class": entry.access_class.as_str(),
2368 "offset": offset,
2369 "length": entry.length,
2370 "sha256": entry.sha256,
2371 })
2372 })
2373 .collect();
2374
2375 let tensors: Vec<Value> = self
2376 .tensors
2377 .iter()
2378 .map(|tensor| {
2379 json!({
2380 "name": tensor.name,
2381 "section": tensor.section,
2382 "dtype": tensor.dtype.as_str(),
2383 "shape": tensor.shape,
2384 "offset": tensor.offset,
2385 "length": tensor.length,
2386 "scales": tensor.scales,
2387 })
2388 })
2389 .collect();
2390
2391 json!({
2392 "format_version": FORMAT_VERSION,
2393 "model_family": self.model_family,
2394 "source_sha256": self.source_sha256,
2395 "license_notice": self.license_notice,
2396 "model_config": self.model_config,
2397 "quantization_manifest": self.quantization_manifest,
2398 "sections": sections,
2399 "tensors": tensors,
2400 })
2401 }
2402}
2403
2404#[cfg(test)]
2405mod tests {
2406 use super::*;
2407 use std::io::Cursor;
2408
2409 const NOTICE: &str = "Copyright 2026 Alibaba Cloud\nApache-2.0\nCHANGES: requantized to .fttsq";
2411
2412 fn artifact() -> Vec<u8> {
2413 FttsqWriter::new("qwen3-tts-12hz-0.6b-base", "a".repeat(64))
2414 .license_notice(NOTICE)
2415 .model_config(json!({ "hidden_size": 1024 }))
2416 .quantization_manifest(json!({ "talker": "q8" }))
2417 .section(
2418 "microdecoder",
2419 AccessClass::HotRecurrentMicrodecoder,
2420 vec![7_u8; 64],
2421 )
2422 .section(
2423 "text_embedding",
2424 AccessClass::ColdTextEmbedding,
2425 vec![9_u8; 32],
2426 )
2427 .tensor(TensorEntry {
2428 name: "microdecoder.body".to_owned(),
2429 section: "microdecoder".to_owned(),
2430 dtype: StoredDtype::Q8,
2431 shape: vec![8, 8],
2432 offset: 0,
2433 length: 64,
2434 scales: Some("microdecoder.body.scales".to_owned()),
2435 })
2436 .tensor(TensorEntry {
2437 name: "text_embedding.weight".to_owned(),
2438 section: "text_embedding".to_owned(),
2439 dtype: StoredDtype::Bf16,
2440 shape: vec![4, 4],
2441 offset: 0,
2442 length: 32,
2443 scales: None,
2444 })
2445 .finish()
2446 .expect("the fixture artifact is writable")
2447 }
2448
2449 fn stream_plan() -> FttsqStreamPlan {
2450 FttsqStreamPlan::new("qwen3-tts-12hz-0.6b-base", "a".repeat(64))
2451 .license_notice(NOTICE)
2452 .model_config(json!({ "hidden_size": 1024 }))
2453 .quantization_manifest(json!({ "talker": "q8" }))
2454 .section("microdecoder", AccessClass::HotRecurrentMicrodecoder, 64)
2455 .section("text_embedding", AccessClass::ColdTextEmbedding, 32)
2456 .tensor(TensorEntry {
2457 name: "microdecoder.body".to_owned(),
2458 section: "microdecoder".to_owned(),
2459 dtype: StoredDtype::Q8,
2460 shape: vec![8, 8],
2461 offset: 0,
2462 length: 64,
2463 scales: Some("microdecoder.body.scales".to_owned()),
2464 })
2465 .tensor(TensorEntry {
2466 name: "text_embedding.weight".to_owned(),
2467 section: "text_embedding".to_owned(),
2468 dtype: StoredDtype::Bf16,
2469 shape: vec![4, 4],
2470 offset: 0,
2471 length: 32,
2472 scales: None,
2473 })
2474 }
2475
2476 fn streamed_artifact() -> Vec<u8> {
2477 let mut writer = stream_plan()
2478 .begin(Cursor::new(Vec::new()))
2479 .expect("the stream plan is structurally valid");
2480 writer
2481 .write_section("microdecoder", &[7_u8; 64])
2482 .expect("first section streams");
2483 writer
2484 .write_section("text_embedding", &[9_u8; 32])
2485 .expect("second section streams");
2486 writer
2487 .finish()
2488 .expect("complete stream finalizes")
2489 .into_inner()
2490 }
2491
2492 #[test]
2493 fn streaming_writer_is_canonical_and_never_retains_section_payloads() {
2494 let bytes = streamed_artifact();
2498 assert_eq!(bytes, artifact());
2499 let reader = FttsqReader::open(&bytes).expect("finalized stream verifies");
2500 assert_eq!(
2501 reader
2502 .tensor_bytes("microdecoder.body", &bytes)
2503 .expect("streamed tensor resolves"),
2504 &[7_u8; 64]
2505 );
2506 }
2507
2508 #[test]
2509 fn streaming_writer_refuses_out_of_order_or_incomplete_sections() {
2510 let mut writer = stream_plan()
2511 .begin(Cursor::new(Vec::new()))
2512 .expect("the stream plan is structurally valid");
2513 assert_eq!(
2514 writer
2515 .write_section("text_embedding", &[9_u8; 32])
2516 .expect_err("later sections cannot be buffered"),
2517 FttsqError::SectionWriteOutOfOrder {
2518 expected: Some("microdecoder".to_owned()),
2519 actual: "text_embedding".to_owned(),
2520 }
2521 );
2522 writer
2523 .write_section("microdecoder", &[7_u8; 63])
2524 .expect("a bounded partial chunk is accepted");
2525 assert_eq!(
2526 writer
2527 .finish()
2528 .expect_err("a partial section cannot acquire a digest"),
2529 FttsqError::SectionIncomplete {
2530 section: "microdecoder".to_owned(),
2531 declared: 64,
2532 written: 63,
2533 }
2534 );
2535 }
2536
2537 #[test]
2538 fn round_trips_through_write_and_read() {
2539 let bytes = artifact();
2540 let reader =
2541 FttsqReader::open(&bytes).expect("the artifact we just wrote must be readable");
2542
2543 assert_eq!(reader.format_version(), FORMAT_VERSION);
2544 assert_eq!(reader.model_family(), "qwen3-tts-12hz-0.6b-base");
2545 assert!(reader.license_notice().contains("Alibaba Cloud"));
2546 assert_eq!(reader.model_config()["hidden_size"], 1024);
2547 assert_eq!(reader.sections().len(), 2);
2548 assert_eq!(reader.tensors().len(), 2);
2549
2550 assert_eq!(
2552 reader
2553 .tensor_bytes("microdecoder.body", &bytes)
2554 .expect("tensor resolves"),
2555 &vec![7_u8; 64][..]
2556 );
2557 assert_eq!(
2558 reader
2559 .tensor_bytes("text_embedding.weight", &bytes)
2560 .expect("tensor resolves"),
2561 &vec![9_u8; 32][..]
2562 );
2563 }
2564
2565 #[test]
2566 fn bf16_payload_is_byte_identical_across_the_round_trip() {
2567 let payload: Vec<u8> = (0..=255_u8).cycle().take(4096).collect();
2570 let bytes = FttsqWriter::new("qwen3-tts-12hz-0.6b-base", "b".repeat(64))
2571 .license_notice(NOTICE)
2572 .section("talker", AccessClass::HotRecurrentTalker, payload.clone())
2573 .tensor(TensorEntry {
2574 name: "talker.weight".to_owned(),
2575 section: "talker".to_owned(),
2576 dtype: StoredDtype::Bf16,
2577 shape: vec![64, 32],
2578 offset: 0,
2579 length: 4096,
2580 scales: None,
2581 })
2582 .finish()
2583 .expect("writable");
2584 let reader = FttsqReader::open(&bytes).expect("readable");
2585 assert_eq!(
2586 reader
2587 .tensor_bytes("talker.weight", &bytes)
2588 .expect("resolves"),
2589 &payload[..]
2590 );
2591 }
2592
2593 #[test]
2594 fn access_classes_drive_the_page_in_policy() {
2595 let bytes = artifact();
2596 let reader = FttsqReader::open(&bytes).expect("readable");
2597
2598 let hot = reader.sections_in_class(AccessClass::HotRecurrentMicrodecoder);
2599 assert_eq!(hot.len(), 1);
2600 assert!(hot[0].access_class.is_hot());
2601 assert!(!hot[0].access_class.is_row_granular());
2602
2603 let cold = reader.sections_in_class(AccessClass::ColdTextEmbedding);
2604 assert_eq!(cold.len(), 1);
2605 assert!(
2606 !cold[0].access_class.is_hot(),
2607 "the 622 MB embedding must never be advised resident"
2608 );
2609 assert!(
2610 cold[0].access_class.is_row_granular(),
2611 "the cold embedding is accessed a row at a time, never as a unit"
2612 );
2613 }
2614
2615 #[test]
2616 fn a_newer_format_version_is_refused_rather_than_guessed_at() {
2617 let mut bytes = artifact();
2618 bytes[8..12].copy_from_slice(&(FORMAT_VERSION + 1).to_le_bytes());
2619 let error = FttsqReader::parse_directory(&bytes).expect_err("must refuse");
2620 assert_eq!(
2621 error,
2622 FttsqError::UnsupportedVersion {
2623 found: FORMAT_VERSION + 1,
2624 supported: FORMAT_VERSION,
2625 }
2626 );
2627 }
2628
2629 #[test]
2630 fn bad_magic_and_truncation_are_named_refusals() {
2631 assert!(matches!(
2632 FttsqReader::parse_directory(&[]),
2633 Err(FttsqError::TooShort { .. })
2634 ));
2635 let mut bytes = artifact();
2636 bytes[0] = b'X';
2637 assert!(matches!(
2638 FttsqReader::parse_directory(&bytes),
2639 Err(FttsqError::BadMagic { .. })
2640 ));
2641 }
2642
2643 #[test]
2644 fn a_truncated_file_never_yields_a_partial_load() {
2645 let full = artifact();
2646 for cut in [full.len() - 1, full.len() - 40, full.len() - 90] {
2648 let error = FttsqReader::open(&full[..cut]).expect_err("truncation must be refused");
2649 assert!(
2650 matches!(
2651 error,
2652 FttsqError::RangeOutOfBounds { .. } | FttsqError::DirectoryLength { .. }
2653 ),
2654 "unexpected error for cut at {cut}: {error}"
2655 );
2656 }
2657 }
2658
2659 #[test]
2660 fn a_single_flipped_payload_bit_fails_digest_verification() {
2661 let mut bytes = artifact();
2662 let last = bytes.len() - 1;
2663 bytes[last] ^= 0x01;
2664 let error = FttsqReader::open(&bytes).expect_err("a bit flip must be caught");
2665 assert!(
2666 matches!(
2667 &error,
2668 FttsqError::DigestMismatch { section, .. } if section == "text_embedding"
2669 ),
2670 "expected a digest mismatch for text_embedding, got {error}"
2671 );
2672 assert!(FttsqReader::parse_directory(&bytes).is_ok());
2674 }
2675
2676 #[test]
2677 fn a_hostile_directory_length_cannot_provoke_a_huge_read() {
2678 let mut bytes = artifact();
2679 bytes[12..20].copy_from_slice(&u64::MAX.to_le_bytes());
2680 let error = FttsqReader::parse_directory(&bytes).expect_err("must refuse");
2681 assert!(matches!(error, FttsqError::DirectoryLength { .. }));
2682 }
2683
2684 #[test]
2686 fn structural_violations_are_each_refused_by_name() {
2687 type StructuralCase = (&'static str, Value, fn(&FttsqError) -> bool);
2688 let cases: Vec<StructuralCase> = vec![
2689 (
2690 "overlapping sections",
2691 json!([
2692 {"name": "a", "access_class": "METADATA", "offset": 100, "length": 50, "sha256": "x"},
2693 {"name": "b", "access_class": "METADATA", "offset": 120, "length": 10, "sha256": "x"},
2694 ]),
2695 |e| matches!(e, FttsqError::SectionOverlap { .. }),
2696 ),
2697 (
2698 "a section running past the file",
2699 json!([
2700 {"name": "a", "access_class": "METADATA", "offset": 100, "length": u64::MAX, "sha256": "x"},
2701 ]),
2702 |e| matches!(e, FttsqError::RangeOutOfBounds { .. }),
2703 ),
2704 (
2705 "a duplicate section name",
2706 json!([
2707 {"name": "a", "access_class": "METADATA", "offset": 100, "length": 10, "sha256": "x"},
2708 {"name": "a", "access_class": "METADATA", "offset": 200, "length": 10, "sha256": "x"},
2709 ]),
2710 |e| matches!(e, FttsqError::DuplicateName { .. }),
2711 ),
2712 (
2713 "an unknown access class",
2714 json!([
2715 {"name": "a", "access_class": "PROBABLY_HOT", "offset": 100, "length": 10, "sha256": "x"},
2716 ]),
2717 |e| matches!(e, FttsqError::UnknownValue { .. }),
2718 ),
2719 ];
2720
2721 for (description, sections, matches_expected) in cases {
2722 let error = parse_sections(Some(§ions), 4096)
2723 .expect_err(&format!("`{description}` must be refused"));
2724 assert!(
2725 matches_expected(&error),
2726 "`{description}` produced the wrong error: {error}"
2727 );
2728 }
2729 }
2730
2731 #[test]
2732 fn a_tensor_whose_length_disagrees_with_its_shape_is_refused() {
2733 let sections = vec![SectionEntry {
2734 name: "s".to_owned(),
2735 access_class: AccessClass::Metadata,
2736 offset: 0,
2737 length: 4096,
2738 sha256: String::new(),
2739 }];
2740 let index: BTreeMap<String, usize> = [("s".to_owned(), 0)].into_iter().collect();
2741
2742 let tensors = json!([
2744 {"name": "t", "section": "s", "dtype": "bf16", "shape": [8, 8], "offset": 0, "length": 64},
2745 ]);
2746 let error = parse_tensors(Some(&tensors), §ions, &index).expect_err("must refuse");
2747 assert_eq!(
2748 error,
2749 FttsqError::LengthMismatch {
2750 tensor: "t".to_owned(),
2751 declared: 64,
2752 implied: 128,
2753 }
2754 );
2755 }
2756
2757 #[test]
2758 fn tensors_may_not_overlap_within_a_section() {
2759 let sections = vec![SectionEntry {
2760 name: "s".to_owned(),
2761 access_class: AccessClass::Metadata,
2762 offset: 0,
2763 length: 4096,
2764 sha256: String::new(),
2765 }];
2766 let index: BTreeMap<String, usize> = [("s".to_owned(), 0)].into_iter().collect();
2767 let tensors = json!([
2768 {"name": "a", "section": "s", "dtype": "q8", "shape": [64], "offset": 0, "length": 64},
2769 {"name": "b", "section": "s", "dtype": "q8", "shape": [64], "offset": 32, "length": 64},
2770 ]);
2771 let error = parse_tensors(Some(&tensors), §ions, &index).expect_err("must refuse");
2772 assert!(matches!(error, FttsqError::TensorOverlap { .. }), "{error}");
2773 }
2774
2775 #[test]
2776 fn a_tensor_leaving_its_section_is_refused() {
2777 let sections = vec![SectionEntry {
2778 name: "s".to_owned(),
2779 access_class: AccessClass::Metadata,
2780 offset: 0,
2781 length: 64,
2782 sha256: String::new(),
2783 }];
2784 let index: BTreeMap<String, usize> = [("s".to_owned(), 0)].into_iter().collect();
2785 let tensors = json!([
2786 {"name": "a", "section": "s", "dtype": "q8", "shape": [64], "offset": 32, "length": 64},
2787 ]);
2788 let error = parse_tensors(Some(&tensors), §ions, &index).expect_err("must refuse");
2789 assert!(
2790 matches!(error, FttsqError::RangeOutOfBounds { .. }),
2791 "{error}"
2792 );
2793 }
2794
2795 #[test]
2796 fn an_artifact_without_a_license_notice_cannot_be_written_or_read() {
2797 let error = FttsqWriter::new("qwen3-tts-12hz-0.6b-base", "c".repeat(64))
2799 .section("m", AccessClass::Metadata, vec![1, 2, 3])
2800 .finish()
2801 .expect_err("Apache-2.0 §4 makes the notice mandatory");
2802 assert_eq!(error, FttsqError::LicenseNoticeMissing);
2803
2804 let mut bytes = artifact();
2806 let directory_len = u64::from_le_bytes(bytes[12..20].try_into().expect("header length"));
2807 let directory_start = HEADER_PREFIX_BYTES as usize;
2808 let directory_end = directory_start + directory_len as usize;
2809 let mut directory: Value = serde_json::from_slice(&bytes[directory_start..directory_end])
2810 .expect("fixture directory");
2811 directory["license_notice"] = Value::String(String::new());
2812 let mut replacement = serde_json::to_vec(&directory).expect("serializes directory");
2813 assert!(
2814 replacement.len() <= directory_len as usize,
2815 "removing a notice cannot grow it"
2816 );
2817 replacement.resize(directory_len as usize, b' ');
2818 bytes[directory_start..directory_end].copy_from_slice(&replacement);
2819 assert_eq!(
2820 FttsqReader::open(&bytes).expect_err("must refuse a missing notice"),
2821 FttsqError::LicenseNoticeMissing
2822 );
2823 }
2824
2825 #[test]
2826 fn write_to_path_lands_a_complete_readable_artifact_and_leaves_no_temporary() {
2827 let dir = std::env::temp_dir().join(format!("ftts-fttsq-write-{}", std::process::id()));
2828 std::fs::create_dir_all(&dir).expect("scratch dir");
2829 let path = dir.join("model.fttsq");
2830
2831 FttsqWriter::new("qwen3-tts-12hz-0.6b-base", "d".repeat(64))
2832 .license_notice(NOTICE)
2833 .section("m", AccessClass::HotRecurrentMicrodecoder, vec![3_u8; 128])
2834 .section(
2835 "embedding",
2836 AccessClass::ColdTextEmbedding,
2837 vec![9_u8; 8192],
2838 )
2839 .tensor(TensorEntry {
2840 name: "m.w".to_owned(),
2841 section: "m".to_owned(),
2842 dtype: StoredDtype::Q8,
2843 shape: vec![128],
2844 offset: 0,
2845 length: 128,
2846 scales: None,
2847 })
2848 .tensor(TensorEntry {
2849 name: "embedding.one_row".to_owned(),
2850 section: "embedding".to_owned(),
2851 dtype: StoredDtype::Q8,
2852 shape: vec![32],
2853 offset: 4096,
2854 length: 32,
2855 scales: None,
2856 })
2857 .write_to_path(&path)
2858 .expect("artifact is writable");
2859
2860 let bytes = std::fs::read(&path).expect("artifact is readable");
2861 let reader = FttsqReader::open(&bytes).expect("what landed on disk must verify");
2862 assert_eq!(
2863 reader.tensor_bytes("m.w", &bytes).expect("resolves"),
2864 &vec![3_u8; 128][..]
2865 );
2866
2867 let mapped = MappedFttsq::open(&path).expect("mapped artifact validates");
2868 assert_eq!(mapped.len(), bytes.len());
2869 assert_eq!(
2870 mapped
2871 .tensor_bytes("embedding.one_row")
2872 .expect("row range resolves without copying the section"),
2873 &vec![9_u8; 32][..]
2874 );
2875
2876 let micro = mapped
2877 .page_advice()
2878 .iter()
2879 .find(|application| application.section == "m")
2880 .expect("microdecoder application is recorded");
2881 assert_eq!(micro.policy, PagePolicy::Resident);
2882 assert_eq!(micro.requested, Some(MemoryAdvice::WillNeed));
2883 assert!(
2884 !matches!(micro.outcome, PageAdviceOutcome::Failed(_)),
2885 "a valid mapped microdecoder section must receive a usable advice result: {micro:?}"
2886 );
2887
2888 let embedding = mapped
2889 .page_advice()
2890 .iter()
2891 .find(|application| application.section == "embedding")
2892 .expect("embedding application is recorded");
2893 assert_eq!(embedding.policy, PagePolicy::LazyRowGranular);
2894 assert_eq!(embedding.requested, Some(MemoryAdvice::Random));
2895 assert!(
2896 !embedding.policy.may_prefetch(),
2897 "the cold embedding policy must make wholesale prefetch impossible"
2898 );
2899 for observation in [&embedding.residency_before, &embedding.residency_after] {
2900 match observation {
2901 PageResidencyOutcome::Measured {
2902 resident_pages,
2903 total_pages,
2904 } => assert!(
2905 resident_pages <= total_pages,
2906 "the OQ-18 residency measurement exceeded the section's page span"
2907 ),
2908 PageResidencyOutcome::Unsupported => {}
2909 PageResidencyOutcome::Failed(detail) => {
2910 panic!("the cold embedding residency measurement failed: {detail}");
2911 }
2912 }
2913 }
2914 assert!(
2915 mapped.page_advice().iter().all(|application| {
2916 application.policy.may_prefetch()
2917 || application.requested != Some(MemoryAdvice::WillNeed)
2918 }),
2919 "a non-prefetch section was routed to MADV_WILLNEED"
2920 );
2921
2922 let strays: Vec<_> = std::fs::read_dir(&dir)
2924 .expect("dir is listable")
2925 .filter_map(Result::ok)
2926 .map(|entry| entry.file_name().to_string_lossy().into_owned())
2927 .filter(|name| name.contains(".tmp."))
2928 .collect();
2929 assert!(strays.is_empty(), "temporary files left behind: {strays:?}");
2930
2931 std::fs::remove_file(&path).expect("cleanup");
2932 }
2933
2934 #[test]
2935 fn write_to_path_refuses_before_touching_the_filesystem_when_the_notice_is_missing() {
2936 let dir = std::env::temp_dir().join(format!("ftts-fttsq-refuse-{}", std::process::id()));
2937 std::fs::create_dir_all(&dir).expect("scratch dir");
2938 let path = dir.join("model.fttsq");
2939
2940 let error = FttsqWriter::new("qwen3-tts-12hz-0.6b-base", "e".repeat(64))
2941 .section("m", AccessClass::Metadata, vec![1, 2, 3])
2942 .write_to_path(&path)
2943 .expect_err("a notice-less artifact must never reach disk");
2944 assert_eq!(error, FttsqError::LicenseNoticeMissing);
2945 assert!(
2946 !path.exists(),
2947 "a refused artifact must not leave a file behind"
2948 );
2949 }
2950
2951 #[test]
2953 fn the_cold_text_embedding_is_never_prefetched_and_hot_classes_always_are() {
2954 assert_eq!(
2955 AccessClass::ColdTextEmbedding.page_policy(),
2956 PagePolicy::LazyRowGranular
2957 );
2958 assert!(
2959 !AccessClass::ColdTextEmbedding.page_policy().may_prefetch(),
2960 "MADV_WILLNEED over the ~622 MB embedding would evict the microdecoder pack"
2961 );
2962
2963 for hot in [
2964 AccessClass::HotRecurrentMicrodecoder,
2965 AccessClass::HotRecurrentTalker,
2966 AccessClass::HotCodecDecoder,
2967 ] {
2968 assert_eq!(hot.page_policy(), PagePolicy::Resident);
2969 assert!(hot.page_policy().may_prefetch());
2970 }
2971 for cold in [
2972 AccessClass::EnrollmentSpeakerEncoder,
2973 AccessClass::EnrollmentCodecEncoder,
2974 AccessClass::Metadata,
2975 ] {
2976 assert_eq!(cold.page_policy(), PagePolicy::OnDemand);
2977 assert!(!cold.page_policy().may_prefetch());
2978 }
2979
2980 for class in [
2982 AccessClass::HotRecurrentMicrodecoder,
2983 AccessClass::HotRecurrentTalker,
2984 AccessClass::HotCodecDecoder,
2985 AccessClass::ColdTextEmbedding,
2986 AccessClass::EnrollmentSpeakerEncoder,
2987 AccessClass::EnrollmentCodecEncoder,
2988 AccessClass::Metadata,
2989 ] {
2990 assert_eq!(
2991 class.is_hot(),
2992 class.page_policy().may_prefetch(),
2993 "is_hot() and page_policy() disagree for {class}"
2994 );
2995 assert_eq!(
2996 class.is_row_granular(),
2997 class.page_policy() == PagePolicy::LazyRowGranular,
2998 "is_row_granular() and page_policy() disagree for {class}"
2999 );
3000 }
3001 }
3002
3003 #[test]
3004 fn the_page_in_plan_prefetches_the_microdecoder_before_the_larger_talker() {
3005 let bytes = FttsqWriter::new("qwen3-tts-12hz-0.6b-base", "f".repeat(64))
3007 .license_notice(NOTICE)
3008 .section("talker", AccessClass::HotRecurrentTalker, vec![1_u8; 400])
3009 .section("embedding", AccessClass::ColdTextEmbedding, vec![2_u8; 900])
3010 .section(
3011 "micro",
3012 AccessClass::HotRecurrentMicrodecoder,
3013 vec![3_u8; 100],
3014 )
3015 .section("meta", AccessClass::Metadata, vec![4_u8; 8])
3016 .finish()
3017 .expect("writable");
3018 let reader = FttsqReader::open(&bytes).expect("readable");
3019
3020 let plan = reader.page_in_plan();
3021 let order: Vec<&str> = plan
3022 .iter()
3023 .map(|(section, _)| section.name.as_str())
3024 .collect();
3025 assert_eq!(
3026 order,
3027 vec!["micro", "talker", "embedding", "meta"],
3028 "resident sections first, smallest first, so the 15x-reread pack wins the cache race"
3029 );
3030 assert_eq!(plan[0].1, PagePolicy::Resident);
3031 assert_eq!(plan[2].1, PagePolicy::LazyRowGranular);
3032 assert_eq!(plan[3].1, PagePolicy::OnDemand);
3033
3034 for (section, policy) in &plan {
3036 assert_eq!(
3037 policy.may_prefetch(),
3038 section.access_class.is_hot(),
3039 "section `{}` would be prefetched against policy",
3040 section.name
3041 );
3042 }
3043 }
3044
3045 fn census_fixture() -> (Vec<u8>, ArtifactManifest) {
3046 let bytes = FttsqWriter::new("qwen3-tts-12hz-0.6b-base", "g".repeat(64))
3047 .license_notice(NOTICE)
3048 .section(
3049 "micro",
3050 AccessClass::HotRecurrentMicrodecoder,
3051 vec![1_u8; 64],
3052 )
3053 .section("embedding", AccessClass::ColdTextEmbedding, vec![2_u8; 32])
3054 .tensor(TensorEntry {
3055 name: "micro.body".to_owned(),
3056 section: "micro".to_owned(),
3057 dtype: StoredDtype::Q8,
3058 shape: vec![8, 8],
3059 offset: 0,
3060 length: 64,
3061 scales: None,
3062 })
3063 .tensor(TensorEntry {
3064 name: "text_embedding.weight".to_owned(),
3065 section: "embedding".to_owned(),
3066 dtype: StoredDtype::Bf16,
3067 shape: vec![4, 4],
3068 offset: 0,
3069 length: 32,
3070 scales: None,
3071 })
3072 .finish()
3073 .expect("writable");
3074
3075 let manifest = ArtifactManifest::new("qwen3-tts pinned")
3076 .expect(ExpectedArtifactTensor {
3077 name: "micro.body".to_owned(),
3078 shape: vec![8, 8],
3079 dtype: StoredDtype::Q8,
3080 access_class: AccessClass::HotRecurrentMicrodecoder,
3081 })
3082 .expect(ExpectedArtifactTensor {
3083 name: "text_embedding.weight".to_owned(),
3084 shape: vec![4, 4],
3085 dtype: StoredDtype::Bf16,
3086 access_class: AccessClass::ColdTextEmbedding,
3087 });
3088 (bytes, manifest)
3089 }
3090
3091 #[test]
3092 fn a_matching_artifact_passes_its_census() {
3093 let (bytes, manifest) = census_fixture();
3094 let reader = FttsqReader::open(&bytes).expect("readable");
3095 let report = manifest.audit(&reader);
3096 assert!(report.is_green(), "{}", report.render());
3097 assert!(reader.verify_census(&manifest).is_ok());
3098 }
3099
3100 #[test]
3102 fn the_census_names_every_divergence_class_in_one_pass() {
3103 let (bytes, _) = census_fixture();
3104 let reader = FttsqReader::open(&bytes).expect("readable");
3105
3106 let manifest = ArtifactManifest::new("deliberately wrong")
3107 .expect(ExpectedArtifactTensor {
3109 name: "micro.body".to_owned(),
3110 shape: vec![16, 4],
3111 dtype: StoredDtype::Q4,
3112 access_class: AccessClass::HotRecurrentMicrodecoder,
3113 })
3114 .expect(ExpectedArtifactTensor {
3116 name: "text_embedding.weight".to_owned(),
3117 shape: vec![4, 4],
3118 dtype: StoredDtype::Bf16,
3119 access_class: AccessClass::HotRecurrentTalker,
3120 })
3121 .expect(ExpectedArtifactTensor {
3123 name: "codec.decoder.weight".to_owned(),
3124 shape: vec![2],
3125 dtype: StoredDtype::Q8,
3126 access_class: AccessClass::HotCodecDecoder,
3127 });
3128
3129 let report = manifest.audit(&reader);
3130 assert!(!report.is_green());
3131 assert_eq!(report.count_of("shape_mismatch"), 1, "{}", report.render());
3132 assert_eq!(report.count_of("dtype_mismatch"), 1, "{}", report.render());
3133 assert_eq!(
3134 report.count_of("wrong_access_class"),
3135 1,
3136 "a tensor in the wrong access class still produces correct audio while destroying \
3137 residency — the census is the only thing that catches it:\n{}",
3138 report.render()
3139 );
3140 assert_eq!(report.count_of("missing"), 1, "{}", report.render());
3141
3142 let rendered = report.render();
3143 for expected in [
3144 "micro.body",
3145 "text_embedding.weight",
3146 "codec.decoder.weight",
3147 "ACCESS_CLASS",
3148 "SHAPE",
3149 "DTYPE",
3150 "MISSING",
3151 ] {
3152 assert!(
3153 rendered.contains(expected),
3154 "census report is missing `{expected}`:\n{rendered}"
3155 );
3156 }
3157
3158 assert!(reader.verify_census(&manifest).is_err());
3159 }
3160
3161 #[test]
3163 fn unexpected_tensors_are_reported_as_extra() {
3164 let (bytes, _) = census_fixture();
3165 let reader = FttsqReader::open(&bytes).expect("readable");
3166 let manifest = ArtifactManifest::new("partial").expect(ExpectedArtifactTensor {
3167 name: "micro.body".to_owned(),
3168 shape: vec![8, 8],
3169 dtype: StoredDtype::Q8,
3170 access_class: AccessClass::HotRecurrentMicrodecoder,
3171 });
3172 let report = manifest.audit(&reader);
3173 assert_eq!(report.count_of("extra"), 1, "{}", report.render());
3174 assert!(report.render().contains("text_embedding.weight"));
3175 }
3176
3177 #[test]
3178 fn quantized_dtype_sizes_are_exact_including_the_odd_q4_tail() {
3179 assert_eq!(StoredDtype::Bf16.storage_bytes(10), Some(20));
3180 assert_eq!(StoredDtype::F32.storage_bytes(10), Some(40));
3181 assert_eq!(StoredDtype::Q8.storage_bytes(10), Some(10));
3182 assert_eq!(StoredDtype::Q4.storage_bytes(10), Some(5));
3184 assert_eq!(StoredDtype::Q4.storage_bytes(11), Some(6));
3185 assert_eq!(StoredDtype::F32.storage_bytes(u64::MAX), None);
3187 }
3188
3189 #[test]
3190 fn wire_strings_round_trip_for_every_enum_value() {
3191 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!(AccessClass::parse(class.as_str()), Some(class));
3201 }
3202 for dtype in [
3203 StoredDtype::Bf16,
3204 StoredDtype::F32,
3205 StoredDtype::Q8,
3206 StoredDtype::Q4,
3207 ] {
3208 assert_eq!(StoredDtype::parse(dtype.as_str()), Some(dtype));
3209 }
3210 assert_eq!(AccessClass::parse("HOT_SOMETHING"), None);
3211 assert_eq!(StoredDtype::parse("f16"), None);
3212 }
3213}