pub struct FileWriter<W> { /* private fields */ }Expand description
Writes a Hurray file in a single forward pass without seeks.
The file format is:
[ File header ] 64 bytes
[ Tensor 0 ] descriptor + padding + data buffers
[ Tensor 1 ]
...
[ KV section ] optional, written by finish()
[ Footer index ] written by finish()
[ Trailer ] 40 bytes, written by finish()This is a driver, not an encoder: FileWriterCore holds the container format, the
offsets and the growing index, and this type only writes the bytes the core offers and
reports how many the sink took (ADR-041 § 7).
HAS_KV_METADATA is not set in the header because KV content is supplied to
finish, after the header has already gone out, and this writer
never seeks back. Readers treat a non-zero trailer kv_offset as authoritative.
§Examples
use hurray_core::{
BufferHandle, DeviceTag, ElementType, LayoutDescriptor, Shape, SyncMode,
TensorDescriptor, MIN_BUFFER_ALIGNMENT,
};
use hurray_io::file::{FileWriter, KvValue};
let handle = BufferHandle::new(
16, MIN_BUFFER_ALIGNMENT, DeviceTag::Cpu, SyncMode::ProducerSynced,
)?;
let descriptor = TensorDescriptor::new(
1, 0, ElementType::Float32, Shape::new(vec![2u64, 2])?, 0,
LayoutDescriptor::RowMajor, vec![handle], None, None, None, None,
)?;
let data = [0u8; 16];
let file = tokio::fs::File::create("model.hrry").await?;
let mut writer = FileWriter::new(file).await?;
writer.write_tensor("weight", &descriptor, &[&data]).await?;
writer
.finish(vec![("producer".into(), KvValue::String("hurray".into()))])
.await?;Implementations§
Source§impl<W: AsyncWrite + Unpin> FileWriter<W>
impl<W: AsyncWrite + Unpin> FileWriter<W>
Sourcepub async fn new(inner: W) -> Result<Self>
pub async fn new(inner: W) -> Result<Self>
Creates a writer with default options (4096-byte buffer alignment, unsorted index).
Sourcepub async fn with_options(inner: W, options: FileWriterOptions) -> Result<Self>
pub async fn with_options(inner: W, options: FileWriterOptions) -> Result<Self>
Creates a writer with custom options.
§Errors
Error::InvalidHeader—data_buffer_alignmentis not a power of two in[4096, 2097152]Error::Io— the header could not be written
Sourcepub async fn write_tensor(
&mut self,
name: &str,
desc: &TensorDescriptor,
buffers: &[&[u8]],
) -> Result<()>
pub async fn write_tensor( &mut self, name: &str, desc: &TensorDescriptor, buffers: &[&[u8]], ) -> Result<()>
Encodes and writes one tensor.
§Errors
Error::TensorNameEmpty/Error::TensorNameTooLong— invalid nameError::DuplicateTensorName— name already written to this fileError::MultiBufferLengthMismatch— wrong buffer countError::BufferSizeMismatch— buffer length ≠ handlebyte_sizeError::Core— descriptor encoding failedError::SinkClosed— the sink stopped accepting bytes mid-tensorError::Io— underlying write error
Sourcepub async fn write_composite(
&mut self,
head_name: &str,
head: &TensorDescriptor,
members: &[FileCompositeNode<'_>],
) -> Result<()>
pub async fn write_composite( &mut self, head_name: &str, head: &TensorDescriptor, members: &[FileCompositeNode<'_>], ) -> Result<()>
Writes a composite tensor: its head, then every member’s descriptor and data, contiguously and in order (ADR-027 § Binding).
Every tensor — the head and each member — gets its own footer-index entry, so all
are individually addressable by name via
read_tensor. Membership is recoverable by
read_composite from the head’s
member_count plus file-offset adjacency (the members are the tensors written
immediately after the head). Nested composites are written in the same pass.
The whole group is validated up front — reusing
CompositeValidator for member count and
per-rule constraints (partition exact-cover, overlay ordering) — before any tensor
is written.
§Errors
Error::Core— the head is not a valid composite head, or validation failed- the name/buffer errors of
write_tensorfor the head or any member Error::Io— underlying write error
§Examples
use hurray_core::{
layout::{CompositeLayout, CompositionRule, LayoutDescriptor},
ElementType, Shape, TensorDescriptor,
};
use hurray_io::file::{FileCompositeNode, FileWriter};
let head = TensorDescriptor::new(
1, 0, ElementType::Float32, Shape::new(vec![8u64, 8]).unwrap(), 0,
LayoutDescriptor::Composite(CompositeLayout::new(CompositionRule::Partition, 2).unwrap()),
vec![], None, None, None, None,
)?;
let file = tokio::fs::File::create("model.hrry").await?;
let mut writer = FileWriter::new(file).await?;
writer.write_composite("weight", &head, &members).await?;
writer.finish(vec![]).await?;Sourcepub async fn finish(self, kv: Vec<(String, KvValue)>) -> Result<W>
pub async fn finish(self, kv: Vec<(String, KvValue)>) -> Result<W>
Writes the KV section, footer index, and trailer, then flushes.
kv is a list of (key, value) pairs. Keys must be non-empty, at most
65 535 bytes, and unique within the list.
§Errors
Error::KvKeyEmpty/Error::KvKeyTooLong/Error::DuplicateKvKey— invalid KV keysError::InvalidHeader— a KV array is empty, nested, or not homogeneousError::SinkClosed— the sink stopped accepting bytes mid-footerError::Io— underlying write error