//@NO-IMPLICIT-PRELUDE //! Functions and types for working with `Read`ers. let io @ { IO, wrap, flat_map, default_buf_len } = import! std.io.prim let { show } = import! std.show let { Option, Bool } = import! std.types let { Disposable, dispose, is_disposed } = import! std.disposable let { (>), (<=), (>=), min } = import! std.cmp let { assert } = import! std.assert let { not } = import! std.bool let { ? } = import! std.int let { Result } = import! std.result let { Reference, ref, load, (<-) } = import! std.reference let array = import! std.array let string = import! std.string /// Allows reading bytes from `a`. Generally, reading from a reader /// advances its internal cursor. This means that multiple `read` calls /// with the same arguments usually __do not__ return the same value. #[implicit] type Read a = { /// Tries to read `num_bytes` byte from the reader. If the reader /// has reached end of file or `num_bytes` was zero, `None` /// will be returned. Otherwise, an array containing as many bytes as could be /// read will be returned. The length of the array is guaranteed to be in the /// range `[1, num_bytes]`. /// /// Reaching end of file means that this reader will most likely not produce /// more bytes. It _may_ produce more in the future. read : a -> Int -> IO (Option (Array Byte)), /// Read all bytes until end of file is encountered. read_to_end : a -> IO (Array Byte) } let read ?read : [Read a] -> a -> Int -> IO (Option (Array Byte)) = read.read let read_to_end ?read : [Read a] -> a -> IO (Array Byte) = read.read_to_end /// Read all bytes until end of file is encountered into a `String`. Returns `None` /// if the read bytes are not valid UTF-8. let read_to_string reader : [Read a] -> a -> IO (Option String) = do bytes = read_to_end reader match string.from_utf8 bytes with | Ok str -> wrap (Some str) | Err _ -> wrap None /// Constructs a `read_to_end` function from a `read` function. If it is more efficient, /// implementors of `Read` should provide their own, specialized version. let default_read_to_end read : forall a . (a -> Int -> IO (Option (Array Byte))) -> a -> IO (Array Byte) = let read_to_end_rec buf reader = do result = read reader default_buf_len match result with | Some new_buf -> read_to_end_rec (array.append buf new_buf) reader | None -> wrap buf read_to_end_rec [] /// Wraps a reader to provide buffering. Buffering is more efficient when /// performing many small reads, because it avoids many costly reads from /// the underlying reader. /// /// If you are reading all data at once, buffering is not necessary. type Buffered r = { reader : r, buf : Reference (Array Byte), capacity : Int } /// Wraps `reader` in a `Buffered` reader to provide buffering with the specified /// buffer capacity. let buffered_with_capacity capacity reader : [Read a] -> Int -> a -> IO (Buffered a) = let _ = assert (capacity > 0) do buf = ref [] wrap { reader, buf, capacity, } /// Wraps `reader` in a `Buffered` reader to provide buffering. let buffered reader : [Read a] -> a -> IO (Buffered a) = buffered_with_capacity default_buf_len reader let read_buffered : [Read r] -> Read (Buffered r) = let read_from_buf buf_reader num_bytes = do buf = load buf_reader.buf let num_bytes = min (array.len buf) num_bytes let read_buf = array.slice buf 0 num_bytes let rest_buf = array.slice buf num_bytes (array.len buf) buf_reader.buf <- rest_buf wrap (Some read_buf) let buffered_read buf_reader num_bytes = do buf = load buf_reader.buf if num_bytes <= 0 then wrap (Some []) else if not (array.is_empty buf) then read_from_buf buf_reader num_bytes else if num_bytes >= buf_reader.capacity then read buf_reader.reader num_bytes else do new_buf = read buf_reader.reader buf_reader.capacity match new_buf with | Some new_buf -> buf_reader.buf <- new_buf read_from_buf buf_reader num_bytes | None -> wrap None let buffered_read_to_end buf_reader = do rest = read_to_end buf_reader.reader do buf = load buf_reader.buf buf_reader.buf <- [] wrap (array.append buf rest) { read = buffered_read, read_to_end = buffered_read_to_end, } let disposable_buffered : [Disposable r] -> Disposable (Buffered r) = { dispose = (\buf_reader -> buf_reader.buf <- [] dispose buf_reader.reader), is_disposed = \buf_reader -> is_disposed buf_reader.reader, } { Read, Buffered, read, read_to_end, read_to_string, default_read_to_end, buffered, buffered_with_capacity, read_buffered, disposable_buffered, }