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