use crate::bridge;
use crate::{ApplyResult, IdentityAdapter, ModuleAdapter, ModuleSnapshot, ModuleStore, PathFilter};
#[cfg(feature = "std")]
use crate::{KeyRemapper, map_indices_contiguous_except};
use alloc::boxed::Box;
use alloc::collections::BTreeMap;
use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use burn_core::tensor::{BoolStore, DType, Shape};
use burn_pack::{Error as PackError, Tensor as PackTensor};
use core::fmt;
use core::ops::Deref;
use hashbrown::HashMap;
#[cfg(not(target_has_atomic = "ptr"))]
use alloc::rc::Rc as Arc;
#[cfg(target_has_atomic = "ptr")]
use alloc::sync::Arc;
#[derive(Debug)]
pub enum SafetensorsStoreError {
Safetensors(safetensors::SafeTensorError),
#[cfg(feature = "std")]
Io(std::io::Error),
TensorNotFound(String),
ValidationFailed(String),
Other(String),
}
impl fmt::Display for SafetensorsStoreError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Safetensors(e) => write!(f, "SafeTensors error: {}", e),
#[cfg(feature = "std")]
Self::Io(e) => write!(f, "I/O error: {}", e),
Self::TensorNotFound(name) => write!(f, "Tensor not found: {}", name),
Self::ValidationFailed(msg) => write!(f, "Validation failed: {}", msg),
Self::Other(msg) => write!(f, "{}", msg),
}
}
}
impl core::error::Error for SafetensorsStoreError {}
impl From<safetensors::SafeTensorError> for SafetensorsStoreError {
fn from(e: safetensors::SafeTensorError) -> Self {
SafetensorsStoreError::Safetensors(e)
}
}
#[cfg(feature = "std")]
impl From<std::io::Error> for SafetensorsStoreError {
fn from(e: std::io::Error) -> Self {
SafetensorsStoreError::Io(e)
}
}
pub enum SafetensorsStore {
#[cfg(feature = "std")]
File(FileStore),
Memory(MemoryStore),
}
impl Default for SafetensorsStore {
fn default() -> Self {
Self::from_bytes(None)
}
}
impl SafetensorsStore {
pub fn default_metadata() -> HashMap<String, String> {
let mut metadata = HashMap::new();
metadata.insert("format".to_string(), "safetensors".to_string());
metadata.insert("producer".to_string(), "burn".to_string());
metadata.insert("version".to_string(), env!("CARGO_PKG_VERSION").to_string());
metadata
}
#[cfg(feature = "std")]
pub fn from_file(path: impl Into<std::path::PathBuf>) -> Self {
Self::File(FileStore {
path: path.into(),
filter: PathFilter::new(),
remapper: KeyRemapper::new(),
metadata: Self::default_metadata(),
validate: true,
allow_partial: false,
overwrite: false,
skip_enum_variants: false,
map_indices_contiguous: false,
keep_indices: PathFilter::new(),
from_adapter: Box::new(IdentityAdapter),
to_adapter: Box::new(IdentityAdapter),
tensors_cache: None,
})
}
pub fn from_bytes(bytes: Option<Vec<u8>>) -> Self {
Self::Memory(MemoryStore {
data: bytes.map(Arc::new),
filter: PathFilter::new(),
#[cfg(feature = "std")]
remapper: KeyRemapper::new(),
metadata: Self::default_metadata(),
validate: true,
allow_partial: false,
skip_enum_variants: false,
#[cfg(feature = "std")]
map_indices_contiguous: false,
#[cfg(feature = "std")]
keep_indices: PathFilter::new(),
from_adapter: Box::new(IdentityAdapter),
to_adapter: Box::new(IdentityAdapter),
tensors_cache: None,
})
}
pub fn filter(mut self, filter: PathFilter) -> Self {
match &mut self {
#[cfg(feature = "std")]
Self::File(p) => p.filter = filter,
Self::Memory(p) => p.filter = filter,
}
self
}
#[cfg(feature = "std")]
pub fn with_regex<S: AsRef<str>>(mut self, pattern: S) -> Self {
match &mut self {
#[cfg(feature = "std")]
Self::File(p) => p.filter = p.filter.clone().with_regex(pattern),
Self::Memory(p) => p.filter = p.filter.clone().with_regex(pattern),
}
self
}
#[cfg(feature = "std")]
pub fn with_regexes<I, S>(mut self, patterns: I) -> Self
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
match &mut self {
#[cfg(feature = "std")]
Self::File(p) => p.filter = p.filter.clone().with_regexes(patterns),
Self::Memory(p) => p.filter = p.filter.clone().with_regexes(patterns),
}
self
}
pub fn with_full_path<S: Into<String>>(mut self, path: S) -> Self {
match &mut self {
#[cfg(feature = "std")]
Self::File(p) => p.filter = p.filter.clone().with_full_path(path),
Self::Memory(p) => p.filter = p.filter.clone().with_full_path(path),
}
self
}
pub fn with_full_paths<I, S>(mut self, paths: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
match &mut self {
#[cfg(feature = "std")]
Self::File(p) => p.filter = p.filter.clone().with_full_paths(paths),
Self::Memory(p) => p.filter = p.filter.clone().with_full_paths(paths),
}
self
}
pub fn with_predicate(mut self, predicate: fn(&str, &str) -> bool) -> Self {
match &mut self {
#[cfg(feature = "std")]
Self::File(p) => p.filter = p.filter.clone().with_predicate(predicate),
Self::Memory(p) => p.filter = p.filter.clone().with_predicate(predicate),
}
self
}
pub fn with_predicates<I>(mut self, predicates: I) -> Self
where
I: IntoIterator<Item = fn(&str, &str) -> bool>,
{
match &mut self {
#[cfg(feature = "std")]
Self::File(p) => p.filter = p.filter.clone().with_predicates(predicates),
Self::Memory(p) => p.filter = p.filter.clone().with_predicates(predicates),
}
self
}
pub fn match_all(mut self) -> Self {
match &mut self {
#[cfg(feature = "std")]
Self::File(p) => p.filter = p.filter.clone().match_all(),
Self::Memory(p) => p.filter = p.filter.clone().match_all(),
}
self
}
#[cfg(feature = "std")]
pub fn remap(mut self, remapper: KeyRemapper) -> Self {
match &mut self {
Self::File(p) => p.remapper = remapper,
Self::Memory(p) => p.remapper = remapper,
}
self.clear_tensors_cache();
self
}
#[cfg(feature = "std")]
pub fn with_key_remapping(
mut self,
from_pattern: impl AsRef<str>,
to_pattern: impl Into<String>,
) -> Self {
match &mut self {
Self::File(p) => {
p.remapper = p
.remapper
.clone()
.add_pattern(from_pattern, to_pattern)
.expect("Invalid regex pattern");
}
Self::Memory(p) => {
p.remapper = p
.remapper
.clone()
.add_pattern(from_pattern, to_pattern)
.expect("Invalid regex pattern");
}
}
self.clear_tensors_cache();
self
}
pub fn metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
let key = key.into();
let value = value.into();
match &mut self {
#[cfg(feature = "std")]
Self::File(p) => {
p.metadata.insert(key, value);
}
Self::Memory(p) => {
p.metadata.insert(key, value);
}
}
self
}
pub fn clear_metadata(mut self) -> Self {
match &mut self {
#[cfg(feature = "std")]
Self::File(p) => {
p.metadata.clear();
}
Self::Memory(p) => {
p.metadata.clear();
}
}
self
}
pub fn validate(mut self, validate: bool) -> Self {
match &mut self {
#[cfg(feature = "std")]
Self::File(p) => p.validate = validate,
Self::Memory(p) => p.validate = validate,
}
self
}
pub fn allow_partial(mut self, allow: bool) -> Self {
match &mut self {
#[cfg(feature = "std")]
Self::File(p) => p.allow_partial = allow,
Self::Memory(p) => p.allow_partial = allow,
}
self
}
pub fn skip_enum_variants(mut self, skip: bool) -> Self {
match &mut self {
#[cfg(feature = "std")]
Self::File(p) => p.skip_enum_variants = skip,
Self::Memory(p) => p.skip_enum_variants = skip,
}
self
}
#[cfg(feature = "std")]
pub fn map_indices_contiguous(mut self, map: bool) -> Self {
match &mut self {
Self::File(p) => p.map_indices_contiguous = map,
Self::Memory(p) => p.map_indices_contiguous = map,
}
self.clear_tensors_cache();
self
}
#[cfg(feature = "std")]
pub fn map_indices_contiguous_except<S: AsRef<str>>(mut self, pattern: S) -> Self {
match &mut self {
Self::File(p) => p.keep_indices = p.keep_indices.clone().with_regex(pattern),
Self::Memory(p) => p.keep_indices = p.keep_indices.clone().with_regex(pattern),
}
self.clear_tensors_cache();
self
}
#[cfg(feature = "std")]
pub fn overwrite(mut self, overwrite: bool) -> Self {
match &mut self {
Self::File(p) => p.overwrite = overwrite,
Self::Memory(_) => {
}
}
self
}
pub fn with_from_adapter(mut self, adapter: impl ModuleAdapter + 'static) -> Self {
match &mut self {
#[cfg(feature = "std")]
Self::File(p) => p.from_adapter = Box::new(adapter),
Self::Memory(p) => p.from_adapter = Box::new(adapter),
}
self
}
pub fn with_to_adapter(mut self, adapter: impl ModuleAdapter + 'static) -> Self {
match &mut self {
#[cfg(feature = "std")]
Self::File(p) => p.to_adapter = Box::new(adapter),
Self::Memory(p) => p.to_adapter = Box::new(adapter),
}
self
}
pub fn get_bytes(&self) -> Result<Vec<u8>, SafetensorsStoreError> {
match self {
#[cfg(feature = "std")]
Self::File(_) => Err(SafetensorsStoreError::Other(
"Cannot get bytes from file-based store".to_string(),
)),
Self::Memory(p) => p
.data()
.map(|arc| arc.as_ref().clone())
.ok_or_else(|| SafetensorsStoreError::Other("No data available".to_string())),
}
}
}
#[cfg(feature = "std")]
pub struct FileStore {
path: std::path::PathBuf,
filter: PathFilter,
remapper: KeyRemapper,
metadata: HashMap<String, String>,
validate: bool,
allow_partial: bool,
overwrite: bool,
skip_enum_variants: bool,
map_indices_contiguous: bool,
keep_indices: PathFilter,
from_adapter: Box<dyn ModuleAdapter>,
to_adapter: Box<dyn ModuleAdapter>,
tensors_cache: Option<BTreeMap<String, PackTensor>>,
}
pub struct MemoryStore {
data: Option<Arc<Vec<u8>>>,
filter: PathFilter,
#[cfg(feature = "std")]
remapper: KeyRemapper,
metadata: HashMap<String, String>,
validate: bool,
allow_partial: bool,
skip_enum_variants: bool,
#[cfg(feature = "std")]
map_indices_contiguous: bool,
#[cfg(feature = "std")]
keep_indices: PathFilter,
from_adapter: Box<dyn ModuleAdapter>,
to_adapter: Box<dyn ModuleAdapter>,
tensors_cache: Option<BTreeMap<String, PackTensor>>,
}
impl Default for MemoryStore {
fn default() -> Self {
Self {
data: None,
filter: PathFilter::new(),
#[cfg(feature = "std")]
remapper: KeyRemapper::new(),
metadata: HashMap::new(),
validate: true,
allow_partial: false,
skip_enum_variants: false,
#[cfg(feature = "std")]
map_indices_contiguous: false,
#[cfg(feature = "std")]
keep_indices: PathFilter::new(),
from_adapter: Box::new(IdentityAdapter),
to_adapter: Box::new(IdentityAdapter),
tensors_cache: None,
}
}
}
impl MemoryStore {
#[cfg(test)]
pub(crate) fn data(&self) -> Option<Arc<Vec<u8>>> {
self.data.clone()
}
#[cfg(not(test))]
fn data(&self) -> Option<Arc<Vec<u8>>> {
self.data.clone()
}
#[cfg(test)]
pub(crate) fn set_data(&mut self, data: Vec<u8>) {
self.data = Some(Arc::new(data));
}
}
#[derive(Debug)]
struct PackTensorView(PackTensor);
impl safetensors::View for PackTensorView {
fn dtype(&self) -> safetensors::Dtype {
dtype_to_safetensors(self.0.dtype).unwrap_or(safetensors::Dtype::F32)
}
fn shape(&self) -> &[usize] {
&self.0.shape
}
fn data(&self) -> alloc::borrow::Cow<'_, [u8]> {
let bytes = self
.0
.to_bytes()
.unwrap_or_else(|e| panic!("Failed to get data for tensor '{}': {:?}", self.0.name, e));
alloc::borrow::Cow::Owned(bytes.deref().to_vec())
}
fn data_len(&self) -> usize {
self.0.byte_len()
}
}
impl ModuleStore for SafetensorsStore {
type Error = SafetensorsStoreError;
fn collect_from<M: ModuleSnapshot>(&mut self, module: &M) -> Result<(), Self::Error> {
self.clear_tensors_cache();
let to_adapter = match self {
#[cfg(feature = "std")]
Self::File(p) => p.to_adapter.clone(),
Self::Memory(p) => p.to_adapter.clone(),
};
let mut tensors = module.collect(None, Some(to_adapter), self.get_skip_enum_variants());
tensors = apply_filter(tensors, self.get_filter());
#[cfg(feature = "std")]
{
tensors = apply_remapping(tensors, self.get_remapper());
}
let metadata = self.get_metadata().clone();
#[cfg(feature = "std")]
let std_metadata: std::collections::HashMap<String, String> = metadata
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect();
match self {
#[cfg(feature = "std")]
Self::File(p) => {
if !p.overwrite && std::fs::symlink_metadata(&p.path).is_ok() {
return Err(pack(burn_pack::Error::AlreadyExists(
p.path.display().to_string(),
)));
}
let tensors = to_safetensors_views(tensors)?;
let (scratch, reserved) = burn_pack::AtomicFile::create(&p.path).map_err(pack)?;
drop(reserved);
let scratch_path = scratch.path().to_path_buf();
caught(move || {
safetensors::serialize_to_file(tensors, Some(std_metadata), &scratch_path)
})??;
if p.overwrite {
scratch.commit().map_err(pack)?;
} else {
scratch.commit_new().map_err(pack)?;
}
Ok(())
}
Self::Memory(p) => {
let tensors = to_safetensors_views(tensors)?;
#[cfg(feature = "std")]
let data = caught(move || safetensors::serialize(tensors, Some(std_metadata)))??;
#[cfg(not(feature = "std"))]
let data = safetensors::serialize(tensors, Some(metadata))?;
p.data = Some(Arc::new(data));
Ok(())
}
}
}
fn apply_to<M: ModuleSnapshot>(&mut self, module: &mut M) -> Result<ApplyResult, Self::Error> {
let tensors: Vec<PackTensor> = self.get_all_tensors()?.values().cloned().collect();
let adapter: Box<dyn ModuleAdapter> = match self {
#[cfg(feature = "std")]
Self::File(p) => p.from_adapter.clone(),
Self::Memory(p) => p.from_adapter.clone(),
};
let filter = self.get_filter();
let filter_opt = if filter.is_empty() {
None
} else {
Some(filter.clone())
};
let result = module.apply(
tensors,
filter_opt,
Some(adapter),
self.get_skip_enum_variants(),
);
if self.get_validate() && !result.errors.is_empty() {
return Err(SafetensorsStoreError::ValidationFailed(format!(
"Import errors: {:?}",
result.errors
)));
}
if !self.get_allow_partial() && !result.missing.is_empty() {
return Err(SafetensorsStoreError::TensorNotFound(format!(
"\n{}\n\nHint: Use `.allow_partial(true)` on the store to get an `ApplyResult` \
with structured missing/error info instead of a hard failure.",
result
)));
}
Ok(result)
}
fn get_tensor(&mut self, name: &str) -> Result<Option<&PackTensor>, Self::Error> {
self.ensure_tensors_cache()?;
let cache = match self {
#[cfg(feature = "std")]
Self::File(p) => p.tensors_cache.as_ref().unwrap(),
Self::Memory(p) => p.tensors_cache.as_ref().unwrap(),
};
Ok(cache.get(name))
}
fn get_all_tensors(&mut self) -> Result<&BTreeMap<String, PackTensor>, Self::Error> {
self.ensure_tensors_cache()?;
let cache = match self {
#[cfg(feature = "std")]
Self::File(p) => p.tensors_cache.as_ref().unwrap(),
Self::Memory(p) => p.tensors_cache.as_ref().unwrap(),
};
Ok(cache)
}
fn keys(&mut self) -> Result<Vec<String>, Self::Error> {
Ok(self.get_all_tensors()?.keys().cloned().collect())
}
}
impl SafetensorsStore {
fn get_filter(&self) -> &PathFilter {
match self {
#[cfg(feature = "std")]
Self::File(p) => &p.filter,
Self::Memory(p) => &p.filter,
}
}
#[cfg(feature = "std")]
fn get_remapper(&self) -> &KeyRemapper {
match self {
Self::File(p) => &p.remapper,
Self::Memory(p) => &p.remapper,
}
}
fn get_metadata(&self) -> &HashMap<String, String> {
match self {
#[cfg(feature = "std")]
Self::File(p) => &p.metadata,
Self::Memory(p) => &p.metadata,
}
}
fn get_validate(&self) -> bool {
match self {
#[cfg(feature = "std")]
Self::File(p) => p.validate,
Self::Memory(p) => p.validate,
}
}
fn get_allow_partial(&self) -> bool {
match self {
#[cfg(feature = "std")]
Self::File(p) => p.allow_partial,
Self::Memory(p) => p.allow_partial,
}
}
fn get_skip_enum_variants(&self) -> bool {
match self {
#[cfg(feature = "std")]
Self::File(p) => p.skip_enum_variants,
Self::Memory(p) => p.skip_enum_variants,
}
}
#[cfg(feature = "std")]
fn get_map_indices_contiguous(&self) -> bool {
match self {
Self::File(p) => p.map_indices_contiguous,
Self::Memory(p) => p.map_indices_contiguous,
}
}
#[cfg(feature = "std")]
fn get_keep_indices(&self) -> &PathFilter {
match self {
Self::File(p) => &p.keep_indices,
Self::Memory(p) => &p.keep_indices,
}
}
fn clear_tensors_cache(&mut self) {
match self {
#[cfg(feature = "std")]
Self::File(p) => p.tensors_cache = None,
Self::Memory(p) => p.tensors_cache = None,
}
}
fn ensure_tensors_cache(&mut self) -> Result<(), SafetensorsStoreError> {
let has_cache = match self {
#[cfg(feature = "std")]
Self::File(p) => p.tensors_cache.is_some(),
Self::Memory(p) => p.tensors_cache.is_some(),
};
if has_cache {
return Ok(());
}
#[allow(unused_mut)]
let mut tensors = match self {
#[cfg(feature = "std")]
Self::File(p) => lazy_tensors_from_file(&p.path)?,
Self::Memory(p) => {
let data_arc = p
.data
.clone()
.ok_or_else(|| SafetensorsStoreError::Other("No data loaded".to_string()))?;
lazy_tensors(data_arc)?
}
};
#[cfg(feature = "std")]
{
tensors = match self {
Self::File(p) => apply_remapping(tensors, &p.remapper),
Self::Memory(p) => apply_remapping(tensors, &p.remapper),
};
}
#[cfg(feature = "std")]
if self.get_map_indices_contiguous() {
let keep = self.get_keep_indices();
let (mapped, _) = map_indices_contiguous_except(tensors, |prefix| keep.matches(prefix));
tensors = mapped;
}
let cache: BTreeMap<String, PackTensor> =
tensors.into_iter().map(|t| (t.name.clone(), t)).collect();
match self {
#[cfg(feature = "std")]
Self::File(p) => p.tensors_cache = Some(cache),
Self::Memory(p) => p.tensors_cache = Some(cache),
}
Ok(())
}
}
#[cfg(feature = "std")]
fn pack(e: burn_pack::Error) -> SafetensorsStoreError {
SafetensorsStoreError::Other(e.to_string())
}
#[cfg(feature = "std")]
fn caught<T>(f: impl FnOnce() -> T) -> Result<T, SafetensorsStoreError> {
std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)).map_err(|payload| {
let cause = payload
.downcast_ref::<&str>()
.map(|s| (*s).to_string())
.or_else(|| payload.downcast_ref::<String>().cloned())
.unwrap_or_else(|| "unknown panic payload".to_string());
SafetensorsStoreError::Other(cause)
})
}
fn apply_filter(mut tensors: Vec<PackTensor>, filter: &PathFilter) -> Vec<PackTensor> {
if filter.is_empty() {
return tensors;
}
tensors.retain(|tensor| filter.matches(&tensor.name));
tensors
}
#[cfg(feature = "std")]
fn apply_remapping(tensors: Vec<PackTensor>, remapper: &KeyRemapper) -> Vec<PackTensor> {
if remapper.is_empty() {
return tensors;
}
let (remapped, _) = remapper.remap(tensors);
remapped
}
fn to_safetensors_views(
tensors: Vec<PackTensor>,
) -> Result<Vec<(String, PackTensorView)>, SafetensorsStoreError> {
tensors
.into_iter()
.map(|tensor| {
dtype_to_safetensors(tensor.dtype).map_err(|e| {
SafetensorsStoreError::Other(format!("tensor '{}': {e}", tensor.name))
})?;
Ok((tensor.name.clone(), PackTensorView(tensor)))
})
.collect()
}
fn lazy_tensors<S>(source: Arc<S>) -> Result<Vec<PackTensor>, SafetensorsStoreError>
where
S: Deref<Target = [u8]> + Send + Sync + 'static,
{
let tensors = safetensors::SafeTensors::deserialize(&source)?;
let mut loaded = Vec::new();
for (name, view) in tensors.tensors() {
let dtype = safetensor_dtype_to_burn(view.dtype())?;
let shape: Shape = view.shape().into();
let source = source.clone();
let tensor_name = name.to_string();
let tensor_shape = shape.clone();
loaded.push(bridge::deferred(
name.to_string(),
dtype,
shape,
None,
move || {
let tensors = safetensors::SafeTensors::deserialize(&source).map_err(|e| {
PackError::ValidationError(format!("Failed to deserialize safetensors: {}", e))
})?;
let tensor = tensors.tensor(&tensor_name).map_err(|e| {
PackError::ValidationError(format!("Tensor '{}' not found: {}", tensor_name, e))
})?;
let bytes = burn_core::tensor::Bytes::from_bytes_vec(tensor.data().to_vec());
bridge::tensor_data(bytes, tensor_shape.clone(), dtype)
},
));
}
Ok(loaded)
}
#[cfg(feature = "std")]
fn lazy_tensors_from_file(
path: &std::path::Path,
) -> Result<Vec<PackTensor>, SafetensorsStoreError> {
use memmap2::MmapOptions;
let file = std::fs::File::open(path)?;
let mmap = unsafe { MmapOptions::new().map(&file)? };
lazy_tensors(Arc::new(mmap))
}
fn safetensor_dtype_to_burn(dtype: safetensors::Dtype) -> Result<DType, SafetensorsStoreError> {
use safetensors::Dtype;
match dtype {
Dtype::F64 => Ok(DType::F64),
Dtype::F32 => Ok(DType::F32),
Dtype::F16 => Ok(DType::F16),
Dtype::BF16 => Ok(DType::BF16),
Dtype::I64 => Ok(DType::I64),
Dtype::I32 => Ok(DType::I32),
Dtype::I16 => Ok(DType::I16),
Dtype::I8 => Ok(DType::I8),
Dtype::U64 => Ok(DType::U64),
Dtype::U32 => Ok(DType::U32),
Dtype::U8 => Ok(DType::U8),
Dtype::BOOL => Ok(DType::Bool(BoolStore::Native)),
_ => Err(SafetensorsStoreError::Other(format!(
"Unsupported dtype: {:?}",
dtype
))),
}
}
fn dtype_to_safetensors(dtype: DType) -> Result<safetensors::Dtype, SafetensorsStoreError> {
use safetensors::Dtype;
match dtype {
DType::F64 => Ok(Dtype::F64),
DType::F32 | DType::Flex32 => Ok(Dtype::F32), DType::F16 => Ok(Dtype::F16),
DType::BF16 => Ok(Dtype::BF16),
DType::I64 => Ok(Dtype::I64),
DType::I32 => Ok(Dtype::I32),
DType::I16 => Ok(Dtype::I16),
DType::I8 => Ok(Dtype::I8),
DType::U64 => Ok(Dtype::U64),
DType::U32 => Ok(Dtype::U32),
DType::U16 => Err(SafetensorsStoreError::Other(
"U16 dtype not yet supported in safetensors".to_string(),
)),
DType::U8 => Ok(Dtype::U8),
DType::Bool(BoolStore::Native) => Ok(Dtype::BOOL),
DType::Bool(BoolStore::U32) => Ok(Dtype::U32),
DType::Bool(BoolStore::U8) => Ok(Dtype::U8),
DType::QFloat(_) => Err(SafetensorsStoreError::Other(
"Quantized tensors not yet supported in safetensors".to_string(),
)),
}
}