///|
// Strategy: base32-byte-encoding
priv struct Base32Encoder {
wrap : Int32
pending : FixedArray[Byte]
mut count : Int32
mut column : Int32
}
///|
fn Base32Encoder::new(wrap : Int32) -> Base32Encoder {
{ wrap, pending: FixedArray::make(5, b'\x00'), count: 0, column: 0, }
}
///|
fn Base32Encoder::wrapped(
self : Base32Encoder,
raw : FixedArray[Byte],
ending~ : Bool = false,
) -> Bytes {
if self.wrap == 0 {
return raw.unsafe_reinterpret_as_bytes()
}
let total = self.column.to_int64() + raw.length().to_int64()
let breaks = total / self.wrap.to_int64()
let tail = if ending && total % self.wrap.to_int64() != 0 { 1 } else { 0 }
let output = FixedArray::make(raw.length() + breaks.to_int() + tail, b'\n')
let mut source = 0
let mut destination = 0
while source < raw.length() {
let count = (self.wrap - self.column).min(raw.length() - source)
raw.blit_to(output, src_offset=source, dst_offset=destination, len=count)
source += count
destination += count
self.column += count
if self.column == self.wrap {
self.column = 0
destination += 1
}
}
if ending {
self.column = 0
}
output.unsafe_reinterpret_as_bytes()
}
///|
// The reader bounds each call. Only the unfinished quantum survives it.
fn Base32Encoder::encode(self : Base32Encoder, input : BytesView) -> Bytes {
let output = FixedArray::make((input.length() + self.count) / 5 * 8, b'=')
let mut index = 0
let mut target = 0
if self.count != 0 {
while self.count < 5 && index < input.length() {
self.pending[self.count] = input[index]
self.count += 1
index += 1
}
if self.count == 5 {
encode_groups(self.pending.unsafe_reinterpret_as_bytes()[:], output, 0)
self.count = 0
target = 8
}
}
let complete = (input.length() - index) / 5 * 5
encode_groups(input[index:index + complete], output, target)
index += complete
while index < input.length() {
self.pending[self.count] = input[index]
self.count += 1
index += 1
}
self.wrapped(output)
}
///|
fn Base32Encoder::finish(self : Base32Encoder) -> Bytes {
let output = FixedArray::make(if self.count == 0 { 0 } else { 8 }, b'=')
if self.count != 0 {
for index in self.count..<5 {
self.pending[index] = b'\x00'
}
encode_groups(self.pending.unsafe_reinterpret_as_bytes()[:], output, 0)
let symbols = (self.count * 8 + 4) / 5
for index in symbols..<8 {
output[index] = b'='
}
}
self.count = 0
self.wrapped(output, ending=true)
}
///|
async fn encode_stream(
reader : &@io.Reader,
wrap : Int32,
writer : &@io.Writer,
) -> Unit {
let encoder = Base32Encoder::new(wrap)
while reader.read_some(max_len=65536) is Some(chunk) {
let output = encoder.encode(chunk)
if !output.is_empty() {
writer.write(output)
}
}
let output = encoder.finish()
if !output.is_empty() {
writer.write(output)
}
}