// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
pub(open) trait Reader {
// API for internal implementation only, do not use
_get_internal_buffer(Self) -> ReaderBuffer
// API for internal implementation only, do not use
async _direct_read(Self, FixedArray[Byte], offset~ : Int, max_len~ : Int) -> Int
async read(Self, FixedArray[Byte], offset? : Int, max_len? : Int) -> Int = _
async drop(Self, Int) -> Int = _
async read_exactly(Self, len : Int) -> Bytes = _
async read_some(Self, max_len? : Int) -> Bytes? = _
async read_all(Self) -> &Data = _
async read_until(Self, StringView) -> String? = _
}
///|
pub(all) suberror ReaderClosed derive(Show, ToJson)
///|
/// `reader.read(dst, offset~, max_len~)` read at most `max_len` bytes from the reader,
/// storing the result in `dst[offset:]`.
/// The number of bytes actually read is returned.
/// Note that the return value may be smaller than `max_len`
/// if not enough data is *immediately* available.
/// If EOF is reached and no more data is available, zero will be returned.
impl Reader with read(self, dst, offset? = 0, max_len? = dst.length() - offset) {
let buf = self._get_internal_buffer()
if buf.len > 0 {
let n = @cmp.minimum(max_len, buf.len)
buf.buf.blit_to(dst, src_offset=buf.start, dst_offset=offset, len=n)
buf.0.drop(n)
n
} else {
self._direct_read(dst, offset~, max_len~)
}
}
///|
/// `reader.drop(len)` drops `len` number of bytes from the reader.
/// The number of bytes actually dropped is returned.
/// `drop` will wait until enough number of bytes is available,
/// so the return value would be smaller than `len` only when EOF is reached.
impl Reader with drop(self, len) {
let buf = self._get_internal_buffer()
if buf.len >= len {
buf.0.drop(len)
len
} else {
let buffered = buf.len
buf.0.drop(buffered)
buf.0.enlarge_to(1)
for dropped = buffered; dropped < len; {
buf.len = self._direct_read(buf.buf, offset=0, max_len=buf.buf.length())
if buf.len == 0 {
return dropped
}
continue dropped + buf.len
} nobreak {
buf.start = buf.len - (dropped - len)
buf.len = dropped - len
len
}
}
}
///|
/// `reader.read_exactly(len)` read exactly `len` number of bytes from the reader.
/// It will wait until enough number of bytes is available.
/// If EOF is reached before enough data is read, `ReaderClosed` is raised.
impl Reader with read_exactly(self, len) {
let buf = FixedArray::make(len, b'0')
for received = 0; received < len; {
let new_received = self.read(buf, offset=received, max_len=len - received)
if new_received == 0 {
raise ReaderClosed
}
continue received + new_received
}
buf.unsafe_reinterpret_as_bytes()
}
///|
/// `reader.read_some(max_len?)` fetch and return a chunk of data from the reader.
/// It will return as fast as possible when new data become available.
/// If EOF is reached, `None` is returned.
///
/// If `max_len` is specified, the returned chunk will not be larger than `max_len`.
/// Since `read_some` will return immediately when new data become available,
/// the returned chunk may be smaller than `max_len`,
/// even if there are still data available in the reader.
impl Reader with read_some(self, max_len?) {
let buf = self._get_internal_buffer()
if buf.len > 0 {
let buf_bytes = buf.buf.unsafe_reinterpret_as_bytes()
if max_len is Some(max_len) && max_len < buf.len {
let data = buf_bytes[buf.start:buf.start + max_len].to_bytes()
buf.start += max_len
buf.len -= max_len
return Some(data)
} else {
let data = buf_bytes[buf.start:buf.start + buf.len].to_bytes()
buf.start = 0
buf.len = 0
return Some(data)
}
}
buf.0.enlarge_to(if max_len is Some(max_len) { max_len } else { 1 })
let n = self._direct_read(buf.buf, offset=0, max_len=buf.buf.length())
if n == 0 {
return None
}
if max_len is Some(max_len) && max_len < n {
buf.start = max_len
buf.len = n - max_len
Some(buf.buf.unsafe_reinterpret_as_bytes()[:max_len].to_bytes())
} else {
buf.start = 0
buf.len = 0
Some(buf.buf.unsafe_reinterpret_as_bytes()[:n].to_bytes())
}
}
///|
/// Read all remaining content from the reader.
/// The return value is a `&io.Data` object, which can be converted to different formats
/// using `.binary()`, `.text()` or `.json()`.
impl Reader with read_all(self) {
let buffer_list = []
let mut buffer = FixedArray::make(1024, b'0')
let mut offset = 0
while self.read(buffer, offset~, max_len=buffer.length() - offset) is n &&
n > 0 {
offset += n
if offset == buffer.length() {
buffer_list.push(buffer)
buffer = FixedArray::make(1024, b'0')
offset = 0
}
}
let total_size = buffer_list.length() * 1024 + offset
let result = FixedArray::make(total_size, b'0')
for i, buf in buffer_list {
result.unsafe_blit(i * 1024, buf, 0, 1024)
}
result.unsafe_blit(buffer_list.length() * 1024, buffer, 0, offset)
result.unsafe_reinterpret_as_bytes()
}
///|
/// `reader.read_until(sep)` read UTF-8 encoded text from the input stream
/// until `sep` or EOF is reached.
/// The segment of input from start of reader to start of `sep` or EOF is returned.
/// `sep` will be conusmed from the input, but is not included in the return value.
impl Reader with read_until(self, sep) {
let sep = @utf8.encode(sep)
let buf = self._get_internal_buffer()
match buf.find_opt(sep, reader=self) {
None if buf.len > 0 => {
let buf_bytes = buf.buf.unsafe_reinterpret_as_bytes()
let remaining = @utf8.decode(buf_bytes[buf.start:buf.start + buf.len])
buf.0.drop(buf.len)
Some(remaining)
}
None => None
Some(index) => {
let buf_bytes = buf.buf.unsafe_reinterpret_as_bytes()
let result = @utf8.decode(buf_bytes[buf.start:buf.start + index])
buf.0.drop(index + sep.length())
Some(result)
}
}
}