///|
/// Counts prepared records or emits them into an exactly sized buffer. The
/// preparation pass enforces the output ceiling before allocating that buffer.
priv struct Output {
limit : Int?
mut buf : FixedArray[Byte]?
mut size : Int
}
///|
fn Output::counting(limit : Int?) -> Output {
{ limit, buf: None, size: 0, }
}
///|
fn Output::writing(limit : Int?, size_hint? : Int = 0) -> Output {
{ limit, buf: Some(FixedArray::make(size_hint, b'\x00')), size: 0, }
}
///|
fn Output::is_counting(self : Output) -> Bool {
self.buf is None
}
///|
fn Output::grow(self : Output, additional : Int) -> Unit raise ZipError {
guard additional >= 0 else {
raise ZipError(UnsupportedFeature, "zip: negative output size")
}
if self.limit is Some(limit) {
guard additional <= limit - self.size else {
raise ZipError(
LimitExceeded(
OutputBytes,
limit,
self.size + additional.min(0x7fff_ffff - self.size),
),
"zip: output exceeds its byte limit",
)
}
}
guard additional <= 0x7fff_ffff - self.size else {
raise ZipError(UnsupportedFeature, "zip: output size exceeds Int range")
}
let required = self.size + additional
if self.buf is Some(buf) && required > buf.length() {
let capacity = required.max(
buf.length() + buf.length().min(0x7fff_ffff - buf.length()),
)
let grown = FixedArray::make(capacity, b'\x00')
buf.blit_to(grown, len=self.size)
self.buf = Some(grown)
}
self.size = required
}
///|
fn Output::write_bytes(self : Output, bytes : BytesView) -> Unit raise ZipError {
self.grow(bytes.length())
if self.buf is Some(buf) {
buf.blit_from_bytesview(self.size - bytes.length(), bytes)
}
}
///|
/// Advance the sizing pass without copying payload bytes.
fn Output::count(self : Output, n : Int) -> Unit raise ZipError {
self.grow(n)
}
///|
// grow checks both the output limit and capacity before unchecked field writes.
fn Output::write_u16(self : Output, value : Int) -> Unit raise ZipError {
if value < 0 || value > 0xffff {
raise ZipError(UnsupportedFeature, "zip: u16 overflow")
}
self.grow(2)
if self.buf is Some(buf) {
buf.unsafe_write_uint16_le(self.size - 2, value.to_uint16())
}
}
///|
fn Output::write_u32(self : Output, value : UInt) -> Unit raise ZipError {
self.grow(4)
if self.buf is Some(buf) {
buf.unsafe_write_uint32_le(self.size - 4, value)
}
}
///|
fn Output::write_u64(self : Output, value : UInt64) -> Unit raise ZipError {
self.grow(8)
if self.buf is Some(buf) {
buf.unsafe_write_uint64_le(self.size - 8, value)
}
}
///|
/// Consumes the mutable output and returns its immutable contents.
fn Output::finish(self : Output) -> Bytes {
guard self.buf is Some(buf) else { return b"" }
// Consume the output: no mutable alias may survive the ownership transfer.
self.buf = None
let size = self.size
self.size = 0
if size == buf.length() {
owned_zip_buffer_to_bytes(buf)
} else {
let exact = FixedArray::make(size, b'\x00')
buf.blit_to(exact, len=size)
owned_zip_buffer_to_bytes(exact)
}
}
///|
fn write_u16_buf(buf : Buffer, value : Int) -> Unit raise ZipError {
let raw = value.to_uint_checked()
buf.write_uint16_le(raw.to_uint16())
}
///|
fn build_zip64_extra(
uncomp~ : Int?,
comp~ : Int?,
offset~ : Int?,
) -> Bytes raise ZipError {
let mut count = 0
if uncomp is Some(_) {
count = count + 1
}
if comp is Some(_) {
count = count + 1
}
if offset is Some(_) {
count = count + 1
}
if count == 0 {
return b""
}
let buf = Buffer()
write_u16_buf(buf, ZIP64_EXTRA_ID)
write_u16_buf(buf, 8 * count)
if uncomp is Some(value) {
buf.write_uint64_le(value.to_uint64_checked())
}
if comp is Some(value) {
buf.write_uint64_le(value.to_uint64_checked())
}
if offset is Some(value) {
buf.write_uint64_le(value.to_uint64_checked())
}
buf.to_bytes()
}
///|
/// A prepared payload; stored data remains owned by its entry.
priv struct PreparedPayload {
compressed_size : Int
compressed : Bytes?
}
///|
/// Compress an entry once using the archive's reusable workspace.
fn prepare_payload(
entry : Entry,
compressor : @flate.Compressor,
) -> PreparedPayload {
match entry.compression {
Store => { compressed_size: entry.data.length(), compressed: None, }
Deflate => {
let compressed = compressor.compress(entry.data)
{ compressed_size: compressed.length(), compressed: Some(compressed), }
}
}
}
///|
fn method_code(entry : Entry) -> Int {
match entry.compression {
Store => 0
Deflate => 8
}
}
///|
fn entry_name_bytes(entry : Entry) -> Bytes raise ZipError {
let name = @encoding/utf8.encode(entry.name)
if name.length() > 0xffff {
raise ZipError(UnsupportedFeature, "zip: entry name too long")
}
name
}
///|
fn write_local_record(
out : Output,
entry : Entry,
name_bytes : Bytes,
payload : PreparedPayload,
use_descriptor : Bool,
force_zip64 : Bool,
) -> Unit raise ZipError {
let method_id = method_code(entry)
let data_len = entry.data.length()
let compressed_size = payload.compressed_size
let crc = entry.crc32
let needs_zip64_sizes = force_zip64 ||
data_len.to_uint_checked() > MAX_U32 ||
compressed_size.to_uint_checked() > MAX_U32
if out.is_counting() {
let extra_size = if needs_zip64_sizes { 20 } else { 0 }
out.count(30 + name_bytes.length() + extra_size)
out.count(compressed_size)
if use_descriptor {
out.count(if needs_zip64_sizes { 24 } else { 16 })
}
return
}
let version = if needs_zip64_sizes { 45 } else { 20 }
let flags = FLAG_UTF8 |
(if use_descriptor { FLAG_DATA_DESCRIPTOR } else { 0 })
let local_extra = if needs_zip64_sizes {
build_zip64_extra(
uncomp=Some(data_len),
comp=Some(compressed_size),
offset=None,
)
} else {
b""
}
guard name_bytes.length() <= 0xffff else {
raise ZipError(UnsupportedFeature, "zip: u16 overflow")
}
let comp_field = if needs_zip64_sizes {
MAX_U32
} else if use_descriptor {
0
} else {
compressed_size.to_uint_checked()
}
let uncomp_field = if needs_zip64_sizes {
MAX_U32
} else if use_descriptor {
0
} else {
data_len.to_uint_checked()
}
let start = out.size
out.grow(30)
if out.buf is Some(buf) {
// grow reserves the entire fixed header before any unchecked store.
// All u16 fields are constants, bounded enum codes, or validated lengths.
buf.unsafe_write_uint32_le(start, LOCAL_HEADER_SIG)
buf.unsafe_write_uint16_le(start + 4, version.to_uint16())
buf.unsafe_write_uint16_le(start + 6, flags.to_uint16())
buf.unsafe_write_uint16_le(start + 8, method_id.to_uint16())
buf.unsafe_write_uint32_le(start + 10, 0)
buf.unsafe_write_uint32_le(start + 14, if use_descriptor { 0 } else { crc })
buf.unsafe_write_uint32_le(start + 18, comp_field)
buf.unsafe_write_uint32_le(start + 22, uncomp_field)
buf.unsafe_write_uint16_le(start + 26, name_bytes.length().to_uint16())
buf.unsafe_write_uint16_le(start + 28, local_extra.length().to_uint16())
}
out.write_bytes(name_bytes)
out.write_bytes(local_extra)
// Both stored and compressed payloads may be followed by a descriptor.
if payload.compressed is Some(compressed) {
out.write_bytes(compressed)
} else {
out.write_bytes(entry.data)
}
if use_descriptor {
out.write_u32(DATA_DESCRIPTOR_SIG)
out.write_u32(crc)
if needs_zip64_sizes {
out.write_u64(compressed_size.to_uint64_checked())
out.write_u64(data_len.to_uint64_checked())
} else {
out.write_u32(compressed_size.to_uint_checked())
out.write_u32(data_len.to_uint_checked())
}
}
}
///|
fn write_central_record(
out : Output,
entry : Entry,
name_bytes : Bytes,
local_offset : Int,
compressed_size : Int,
use_descriptor : Bool,
force_zip64 : Bool,
) -> Unit raise ZipError {
let method_id = method_code(entry)
let data_len = entry.data.length()
let needs_zip64_sizes = force_zip64 ||
data_len.to_uint_checked() > MAX_U32 ||
compressed_size.to_uint_checked() > MAX_U32
let needs_zip64_offset = force_zip64 ||
local_offset.to_uint_checked() > MAX_U32
if out.is_counting() {
let extra_size = (if needs_zip64_sizes { 16 } else { 0 }) +
(if needs_zip64_offset { 8 } else { 0 })
out.count(
46 +
name_bytes.length() +
(if extra_size > 0 { 4 + extra_size } else { 0 }),
)
return
}
let needs_zip64_entry = needs_zip64_sizes || needs_zip64_offset
let version = if needs_zip64_entry { 45 } else { 20 }
let flags = FLAG_UTF8 |
(if use_descriptor { FLAG_DATA_DESCRIPTOR } else { 0 })
let central_extra = build_zip64_extra(
uncomp=if needs_zip64_sizes { Some(data_len) } else { None },
comp=if needs_zip64_sizes { Some(compressed_size) } else { None },
offset=if needs_zip64_offset { Some(local_offset) } else { None },
)
guard name_bytes.length() <= 0xffff else {
raise ZipError(UnsupportedFeature, "zip: u16 overflow")
}
let comp_field = if needs_zip64_sizes {
MAX_U32
} else {
compressed_size.to_uint_checked()
}
let uncomp_field = if needs_zip64_sizes {
MAX_U32
} else {
data_len.to_uint_checked()
}
let offset_field = if needs_zip64_offset {
MAX_U32
} else {
local_offset.to_uint_checked()
}
let start = out.size
out.grow(46)
if out.buf is Some(buf) {
// grow reserves all 46 bytes; the generated extra is at most 28 bytes.
buf.unsafe_write_uint32_le(start, CENTRAL_HEADER_SIG)
buf.unsafe_write_uint16_le(start + 4, version.to_uint16())
buf.unsafe_write_uint16_le(start + 6, version.to_uint16())
buf.unsafe_write_uint16_le(start + 8, flags.to_uint16())
buf.unsafe_write_uint16_le(start + 10, method_id.to_uint16())
buf.unsafe_write_uint32_le(start + 12, 0)
buf.unsafe_write_uint32_le(start + 16, entry.crc32)
buf.unsafe_write_uint32_le(start + 20, comp_field)
buf.unsafe_write_uint32_le(start + 24, uncomp_field)
buf.unsafe_write_uint16_le(start + 28, name_bytes.length().to_uint16())
buf.unsafe_write_uint16_le(start + 30, central_extra.length().to_uint16())
buf.unsafe_write_uint32_le(start + 32, 0)
buf.unsafe_write_uint16_le(start + 36, 0)
buf.unsafe_write_uint32_le(start + 38, 0)
buf.unsafe_write_uint32_le(start + 42, offset_field)
}
out.write_bytes(name_bytes)
out.write_bytes(central_extra)
}
///|
fn write_preserved_central_record(
out : Output,
source : SourceRecord,
local_offset : Int,
) -> Unit raise ZipError {
let template = source.central_record
guard template.length() >= 46 &&
read_u32_le_at_raw(template, 0) == CENTRAL_HEADER_SIG else {
raise ZipError(
UnsupportedFeature,
"zip: preserved central record is malformed",
)
}
guard source.central_zip64_offset_position is Some(position) else {
out.write_bytes(template[0:42])
out.write_u32(local_offset.to_uint_checked())
out.write_bytes(template[46:])
return
}
out.write_bytes(template[0:position])
out.write_u64(local_offset.to_uint64_checked())
out.write_bytes(template[position + 8:])
}
///|
fn write_generated_classic_end_record(
out : Output,
zip64 : Bool,
total_entries : Int,
central_size : Int,
central_offset : Int,
comment : Bytes,
) -> Unit raise ZipError {
out.write_u32(END_OF_CENTRAL_SIG)
out.write_u16(0)
out.write_u16(0)
if zip64 {
out.write_u16(0xffff)
out.write_u16(0xffff)
out.write_u32(MAX_U32)
out.write_u32(MAX_U32)
} else {
if total_entries > 0xffff {
raise ZipError(
UnsupportedFeature,
"zip: too many entries for a classic archive",
)
}
out.write_u16(total_entries)
out.write_u16(total_entries)
out.write_u32(central_size.to_uint_checked())
out.write_u32(central_offset.to_uint_checked())
}
if comment.length() > 0xffff {
raise ZipError(UnsupportedFeature, "zip: archive comment too long")
}
out.write_u16(comment.length())
out.write_bytes(comment)
}
///|
fn write_generated_zip64_trailer(
out : Output,
total_entries : Int,
central_size : Int,
central_offset : Int,
comment : Bytes,
) -> Unit raise ZipError {
let zip64_offset = out.size
out.write_u32(ZIP64_EOCD_SIG)
out.write_u64((44).to_uint64_checked())
out.write_u16(45)
out.write_u16(45)
out.write_u32(0)
out.write_u32(0)
let entry_count = total_entries.to_uint64_checked()
out.write_u64(entry_count)
out.write_u64(entry_count)
out.write_u64(central_size.to_uint64_checked())
out.write_u64(central_offset.to_uint64_checked())
out.write_u32(ZIP64_LOCATOR_SIG)
out.write_u32(0)
out.write_u64(zip64_offset.to_uint64_checked())
out.write_u32(1)
write_generated_classic_end_record(
out, true, total_entries, central_size, central_offset, comment,
)
}
///|
fn write_preserved_trailer(
out : Output,
trailer : TrailerTemplate,
total_entries : Int,
central_size : Int,
central_offset : Int,
) -> Unit raise ZipError {
let classic = trailer.classic_end_record
guard classic.length() >= 22 &&
read_u32_le_at_raw(classic, 0) == END_OF_CENTRAL_SIG else {
raise ZipError(UnsupportedFeature, "zip: preserved end record is malformed")
}
guard trailer.zip64_end_record is Some(end_record) else {
write_preserved_classic_end_record(
out, classic, total_entries, central_size, central_offset,
)
return
}
guard end_record.length() >= 56 &&
read_u32_le_at_raw(end_record, 0) == ZIP64_EOCD_SIG else {
raise ZipError(
UnsupportedFeature,
"zip: preserved zip64 end record is malformed",
)
}
let zip64_offset = out.size
// Patch the two entry-count fields, central size, and central offset
// (offsets 24/32/40/48 within the record) over the verbatim template.
out.write_bytes(end_record[0:24])
out.write_u64(total_entries.to_uint64_checked())
out.write_u64(total_entries.to_uint64_checked())
out.write_u64(central_size.to_uint64_checked())
out.write_u64(central_offset.to_uint64_checked())
out.write_bytes(end_record[56:])
guard trailer.zip64_locator is Some(locator) else {
raise ZipError(
UnsupportedFeature,
"zip: preserved zip64 locator is missing",
)
}
guard locator.length() == 20 &&
read_u32_le_at_raw(locator, 0) == ZIP64_LOCATOR_SIG else {
raise ZipError(
UnsupportedFeature,
"zip: preserved zip64 locator is malformed",
)
}
out.write_bytes(locator[0:8])
out.write_u64(zip64_offset.to_uint64_checked())
out.write_bytes(locator[16:])
// The classic record of a preserved ZIP64 archive carries sentinels;
// re-emit it verbatim.
out.write_bytes(classic)
}
///|
/// Streams a classic end record while patching both entry-count fields
/// (offsets 8, 10) and the central size/offset (offsets 12, 16). Fields
/// carrying ZIP64 sentinels are left untouched.
fn write_preserved_classic_end_record(
out : Output,
classic : Bytes,
total_entries : Int,
central_size : Int,
central_offset : Int,
) -> Unit raise ZipError {
if classic.length() < 22 ||
read_u32_le_at_raw(classic, 0) != END_OF_CENTRAL_SIG {
raise ZipError(UnsupportedFeature, "zip: preserved end record is malformed")
}
let count = total_entries.min(0xffff)
let mut cursor = 0
let entries_on_disk = read_u16_le_at(classic, 8)
if entries_on_disk != 0xffff {
out.write_bytes(classic[cursor:8])
out.write_u16(count)
cursor = 10
}
let total_on_disk = read_u16_le_at(classic, 10)
if total_on_disk != 0xffff {
out.write_bytes(classic[cursor:10])
out.write_u16(count)
cursor = 12
}
let size_field = read_u32_le_at_raw(classic, 12)
if size_field != MAX_U32 {
out.write_bytes(classic[cursor:12])
out.write_u32(central_size.to_uint_checked())
cursor = 16
}
let offset_field = read_u32_le_at_raw(classic, 16)
if offset_field != MAX_U32 {
out.write_bytes(classic[cursor:16])
out.write_u32(central_offset.to_uint_checked())
cursor = 20
}
out.write_bytes(classic[cursor:])
}
///|
/// Each fresh entry is encoded once during preparation. Emission consumes the
/// retained payload so it can be released as soon as it has been copied.
priv struct EntryPlan {
local_offset : Int
compressed_size : Int
name_bytes : Bytes
mut payload : PreparedPayload
}
///|
/// The order in which local records are written: entries that originated in a
/// read archive first, in their original local-record order, then entries
/// created in memory. A `replace` keeps the replaced entry's original position
/// (its source records are dropped for re-encoding, but its ordering hint
/// survives), so a byte-preserving rewrite never silently moves an edited part
/// to the end of the archive.
fn archive_local_order(archive : Archive) -> FixedArray[Int] {
let order = FixedArray::makei(archive.entries.length(), index => index)
// Most archives are already in local-record order, including all newly built
// archives. Avoid sorting (and its comparisons) on this common path.
let mut previous = -1
let mut fresh_seen = false
let mut ordered = true
for entry in archive.entries {
if entry.origin_local_offset is Some(offset) {
if fresh_seen || offset < previous {
ordered = false
break
}
previous = offset
} else {
fresh_seen = true
}
}
guard !ordered else { return order }
order.sort_by((left, right) => {
match
(
archive.entries[left].origin_local_offset,
archive.entries[right].origin_local_offset,
) {
(Some(a), Some(b)) => a.compare(b)
(Some(_), None) => -1
(None, Some(_)) => 1
(None, None) => left.compare(right)
}
})
order
}
///|
/// Prepare local records once, measuring exact offsets without allocating the
/// final ZIP buffer. Preserved and stored payloads continue to reference their
/// existing bytes. A compressor is allocated only when a fresh Deflate entry
/// is encountered.
fn prepare_archive(
out : Output,
archive : Archive,
order : FixedArray[Int],
preserve : Bool,
level : Int,
fast_store : Bool,
) -> Array[EntryPlan] raise ZipError {
let plans : Array[EntryPlan] = []
let mut compressor : @flate.Compressor? = None
for index in order {
let entry = archive.entries[index]
let local_offset = out.size
if preserve && entry.source is Some(source) {
out.count(source.local_record.length())
plans.push({
local_offset,
compressed_size: entry.compressed_size,
name_bytes: b"",
payload: { compressed_size: entry.compressed_size, compressed: None, },
})
continue
}
let name_bytes = entry_name_bytes(entry)
// Reject an impossible header before doing compression work.
if out.limit is Some(limit) {
guard 30 + name_bytes.length() <= limit - out.size else {
raise ZipError(
LimitExceeded(OutputBytes, limit, out.size + 30 + name_bytes.length()),
"zip: output exceeds its byte limit",
)
}
}
let payload = match entry.compression {
Store => { compressed_size: entry.data.length(), compressed: None, }
Deflate => {
let codec = compressor.unwrap_or_else(() => {
let codec = @flate.Compressor(level~, fast_store~)
compressor = Some(codec)
codec
})
prepare_payload(entry, codec)
}
}
write_local_record(
out,
entry,
name_bytes,
payload,
entry.data_descriptor,
false,
)
plans.push({
local_offset,
compressed_size: payload.compressed_size,
name_bytes,
payload,
})
}
plans
}
///|
fn write_directory(
out : Output,
archive : Archive,
order : FixedArray[Int],
plans : Array[EntryPlan],
preserve : Bool,
) -> Unit raise ZipError {
for position in 0.. Unit raise ZipError {
let needs_zip64 = force_zip64 ||
total_entries > 0xffff ||
central_size.to_uint_checked() > MAX_U32 ||
central_offset.to_uint_checked() > MAX_U32
match (preserve, archive.trailer) {
(true, Some(trailer)) if !needs_zip64 || trailer.zip64_end_record is Some(_) =>
write_preserved_trailer(
out, trailer, total_entries, central_size, central_offset,
)
_ =>
if needs_zip64 {
write_generated_zip64_trailer(
out,
total_entries,
central_size,
central_offset,
archive.comment,
)
} else {
write_generated_classic_end_record(
out,
false,
total_entries,
central_size,
central_offset,
archive.comment,
)
}
}
}
///|
/// A pristine rewrite needs only record lengths and patched offsets. Avoid
/// allocating a payload/name plan for every entry when no encoding is needed.
fn write_pristine_archive(
archive : Archive,
order : FixedArray[Int],
max_output : Int?,
) -> Bytes? raise ZipError {
for entry in archive.entries {
guard entry.source is Some(_) else { return None }
}
let counting = Output::counting(max_output)
for index in order {
if archive.entries[index].source is Some(source) {
counting.count(source.local_record.length())
}
}
let central_offset = counting.size
for index in order {
if archive.entries[index].source is Some(source) {
counting.count(source.central_record.length())
}
}
let central_size = counting.size - central_offset
write_trailer(
counting,
archive,
archive.entries.length(),
central_size,
central_offset,
true,
false,
)
let out = Output::writing(None, size_hint=counting.size)
for index in order {
if archive.entries[index].source is Some(source) {
out.write_bytes(source.local_record)
}
}
let mut local_offset = 0
for index in order {
if archive.entries[index].source is Some(source) {
write_preserved_central_record(out, source, local_offset)
local_offset += source.local_record.length()
}
}
write_trailer(
out,
archive,
archive.entries.length(),
central_size,
central_offset,
true,
false,
)
Some(out.finish())
}
///|
/// Prepare once, then serialize into an exactly sized buffer. The output limit
/// is checked before allocating the final archive; no entry is recompressed.
fn write_impl(
archive : Archive,
preserve : Bool,
level : Int,
max_output : Int?,
fast_store : Bool,
) -> Bytes raise ZipError {
let order = archive_local_order(archive)
if preserve &&
write_pristine_archive(archive, order, max_output) is Some(bytes) {
return bytes
}
let counting = Output::counting(max_output)
let plans = prepare_archive(
counting, archive, order, preserve, level, fast_store,
)
let central_offset = counting.size
write_directory(counting, archive, order, plans, preserve)
let central_size = counting.size - central_offset
write_trailer(
counting,
archive,
archive.entries.length(),
central_size,
central_offset,
preserve,
false,
)
let out = Output::writing(None, size_hint=counting.size)
for position in 0.. Bytes raise ZipError {
write_impl(archive, false, level, None, fast_store)
}
///|
/// Serialize `archive`, re-emitting pristine entries (those read from a
/// previous archive and never replaced) byte-for-byte from their retained
/// source records, so unchanged packages round-trip without loss. Entries
/// created or replaced in memory are encoded fresh.
/// `fast_store` defaults to false. Fresh DEFLATE payloads may use heuristic
/// stored blocks when enabled; ZIP methods and preserved records stay intact.
pub fn write_preserving(
archive : Archive,
level? : Int = 6,
fast_store? : Bool = false,
) -> Bytes raise ZipError {
write_impl(archive, true, level, None, fast_store)
}
///|
/// Serialize `archive` like `write` while enforcing a hard output ceiling. The
/// output is sized first, so an over-limit archive raises
/// `LimitExceeded(OutputBytes, ...)` without materializing a candidate buffer.
/// `fast_store` defaults to false. Fresh DEFLATE payloads may use heuristic
/// stored blocks when enabled; ZIP methods and preserved records stay intact.
pub fn write_limited(
archive : Archive,
max_output_bytes~ : Int,
level? : Int = 6,
fast_store? : Bool = false,
) -> Bytes raise ZipError {
guard max_output_bytes >= 0 else {
raise ZipError(UnsupportedFeature, "zip: output limit must be non-negative")
}
write_impl(archive, false, level, Some(max_output_bytes), fast_store)
}
///|
/// Serialize `archive` like `write_preserving` while enforcing a hard output
/// ceiling (sized first, so an over-limit archive raises without materializing
/// a candidate buffer).
/// `fast_store` defaults to false. Fresh DEFLATE payloads may use heuristic
/// stored blocks when enabled; ZIP methods and preserved records stay intact.
pub fn write_preserving_limited(
archive : Archive,
max_output_bytes~ : Int,
level? : Int = 6,
fast_store? : Bool = false,
) -> Bytes raise ZipError {
guard max_output_bytes >= 0 else {
raise ZipError(UnsupportedFeature, "zip: output limit must be non-negative")
}
write_impl(archive, true, level, Some(max_output_bytes), fast_store)
}