///|
/// A read-only chunk view over an encoded GIF for sockets and file sinks.
pub(all) struct GifChunk {
index : Int
offset : Int
data : Bytes
is_last : Bool
} derive(Debug)
///|
pub fn GifChunk::index(self : GifChunk) -> Int {
self.index
}
///|
pub fn GifChunk::offset(self : GifChunk) -> Int {
self.offset
}
///|
pub fn GifChunk::data(self : GifChunk) -> Bytes {
self.data
}
///|
pub fn GifChunk::is_final(self : GifChunk) -> Bool {
self.is_last
}
///|
pub(all) struct GifByteStream {
bytes : Bytes
chunk_size : Int
} derive(Debug)
///|
pub fn GifByteStream::new(bytes : Bytes, chunk_size : Int) -> GifByteStream {
{ bytes, chunk_size: if chunk_size < 1 { 1 } else { chunk_size } }
}
///|
pub fn GifByteStream::length(self : GifByteStream) -> Int {
self.bytes.length()
}
///|
pub fn GifByteStream::bytes(self : GifByteStream) -> Bytes {
self.bytes
}
///|
pub fn GifByteStream::chunk_size(self : GifByteStream) -> Int {
self.chunk_size
}
///|
pub fn GifByteStream::chunk_count(self : GifByteStream) -> Int {
if self.bytes.length() == 0 {
0
} else {
(self.bytes.length() + self.chunk_size - 1) / self.chunk_size
}
}
///|
pub fn GifByteStream::chunk(self : GifByteStream, index : Int) -> GifChunk? {
if index < 0 || index >= self.chunk_count() {
None
} else {
let offset = index * self.chunk_size
let remaining = self.bytes.length() - offset
let length = if remaining > self.chunk_size {
self.chunk_size
} else {
remaining
}
let payload : Array[Byte] = []
for i in 0.. Array[GifChunk] {
let result : Array[GifChunk] = []
for index in 0.. ()
Some(chunk) => result.push(chunk)
}
}
result
}
///|
/// Encode an animation and expose the result as fixed-size chunks.
pub fn encode_stream(
animation : Animation,
options? : EncodeOptions = EncodeOptions::default(),
chunk_size? : Int = 4096,
) -> GifByteStream raise GifError {
GifByteStream::new(encode_with_options(animation, options), chunk_size)
}
///|
/// A collector for asynchronous or network-delivered GIF chunks.
pub(all) struct GifStreamCollector {
parts : Array[Bytes]
total_bytes : Int
} derive(Debug)
///|
pub fn GifStreamCollector::new() -> GifStreamCollector {
{ parts: [], total_bytes: 0 }
}
///|
pub fn GifStreamCollector::push(
self : GifStreamCollector,
chunk : Bytes,
) -> GifStreamCollector {
self.parts.push(chunk)
{ ..self, total_bytes: self.total_bytes + chunk.length() }
}
///|
pub fn GifStreamCollector::part_count(self : GifStreamCollector) -> Int {
self.parts.length()
}
///|
pub fn GifStreamCollector::total_bytes(self : GifStreamCollector) -> Int {
self.total_bytes
}
///|
pub fn GifStreamCollector::finish(self : GifStreamCollector) -> Bytes {
let result : Array[Byte] = []
for part in self.parts {
for byte in part {
result.push(byte)
}
}
Bytes::from_array(result[:])
}
///|
/// Decode a collected stream after checking its byte-level structure.
pub fn GifStreamCollector::decode(
self : GifStreamCollector,
) -> Animation raise GifError {
decode(self.finish())
}
///|
/// Feed chunks in arbitrary order and reassemble by their declared offset.
pub fn collect_chunks(chunks : ArrayView[GifChunk]) -> Bytes {
let mut total = 0
for chunk in chunks {
let end = chunk.offset() + chunk.data().length()
if end > total {
total = end
}
}
let result = Array::make(total, b'\x00')
for chunk in chunks {
let data = chunk.data()
for i in 0.. Bool {
let mut expected = 0
let mut index = 0
for chunk in chunks {
if chunk.index() != index || chunk.offset() != expected {
return false
}
expected += chunk.data().length()
index += 1
}
chunks.length() == 0 || chunks[chunks.length() - 1].is_final()
}
///|
/// Return a stream digest suitable for resumable transfer logs.
pub fn GifByteStream::checksum(self : GifByteStream) -> UInt {
let mut hash : UInt = 2166136261
for byte in self.bytes {
hash = (hash ^ byte.to_int().reinterpret_as_uint()) * 16777619
}
hash
}
///|
/// Find a safe chunk size for a transport with a maximum packet budget.
pub fn recommended_chunk_size(packet_budget : Int) -> Int {
if packet_budget < 64 {
64
} else if packet_budget > 65536 {
65536
} else {
packet_budget
}
}
///|
/// Split an encoded animation into chunks without duplicating the source buffer.
pub fn encode_chunks(
animation : Animation,
options? : EncodeOptions = EncodeOptions::default(),
chunk_size? : Int = 4096,
) -> Array[GifChunk] raise GifError {
encode_stream(animation, options~, chunk_size~).chunks()
}