summaryrefslogtreecommitdiff
path: root/doc/api/libbio/index.txt
diff options
context:
space:
mode:
Diffstat (limited to 'doc/api/libbio/index.txt')
-rw-r--r--doc/api/libbio/index.txt416
1 files changed, 416 insertions, 0 deletions
diff --git a/doc/api/libbio/index.txt b/doc/api/libbio/index.txt
new file mode 100644
index 0000000..c0099b4
--- /dev/null
+++ b/doc/api/libbio/index.txt
@@ -0,0 +1,416 @@
+{
+ title: libbio
+ description: BIO library description
+}
+
+Myrddin's BIO library is used for buffered input and output. It is a fairly
+simple library that handles reading and writing from file descriptors.
+
+The concepts in libbio should be familiar to anyone that has used a buffered
+IO library in most languages. The usual concepts are supported: Files,
+formatted output, binary and ascii reads, and so on. There are also some
+utility functions to deal with reading integers in a known endianness.
+
+One thing to keep in mind with libbio that it does not attempt to flush
+buffers on program exit, which means that any unwritten, unflushed data
+will be lost. Closing the bio files will prevent this issue.
+
+Summary
+-------
+
+ pkg bio =
+ type mode
+ const Rd : mode
+ const Wr : mode
+ const Rw : mode
+
+ type file = struct
+ ;;
+
+ type lineiter
+
+ type status(@a) = union
+ `Eof
+ `Ok @a
+ `Err ioerr
+ ;;
+
+ type ioerr = union
+ `Ebadfile
+ `Ebadbuf
+ `Ebadfd
+ `Eioerr
+ ;;
+
+ impl iterable lineiter
+
+ /* creation */
+ const mkfile : (fd : std.fd, mode : mode -> file#)
+ const open : (path : byte[:], mode : mode -> std.result(file#, byte[:]))
+ const dial : (srv : byte[:], mode : mode -> std.result(file#, byte[:]))
+ const create : (path : byte[:], mode : mode, perm : int -> std.result(file#, byte[:]))
+ const close : (f : file# -> bool)
+ const free : (f : file# -> void)
+
+ /* basic i/o. Returns sub-buffer when applicable. */
+ const write : (f : file#, src : byte[:] -> status(std.size))
+ const read : (f : file#, dst : byte[:] -> status(byte[:]))
+ const flush : (f : file# -> bool)
+
+ /* seeking */
+ const seek : (f : file#, std.off -> std.result(std.off, ioerr))
+
+ /* single unit operations */
+ const putb : (f : file#, b : byte -> status(std.size))
+ const putc : (f : file#, c : char -> status(std.size))
+ const getb : (f : file# -> status(byte))
+ const getc : (f : file# -> status(char))
+
+ /* peeking */
+ const peekb : (f : file# -> status(byte))
+ const peekc : (f : file# -> status(char))
+
+ /* delimited read; returns freshly allocated buffer. */
+ const readln : (f : file# -> status(byte[:]))
+ const readto : (f : file#, delim : byte[:] -> status(byte[:]))
+ const skipto : (f : file#, delim : byte[:] -> bool)
+ const skipspace : (f : file# -> bool)
+
+ /* iterators */
+ const lineiter : (f : file# -> lineiter)
+
+ /* formatted i/o */
+ const put : (f : file#, fmt : byte[:], args : ... -> status(std.size))
+
+ /* unsigned big endian reads */
+ generic getbe8 : (f : file# -> status(@a::(numeric,integral)))
+ generic getbe16 : (f : file# -> status(@a::(numeric,integral)))
+ generic getbe32 : (f : file# -> status(@a::(numeric,integral)))
+ generic getbe64 : (f : file# -> status(@a::(numeric,integral)))
+
+ /* signed big endian reads */
+ generic getle8 : (f : file# -> status(@a::(numeric,integral)))
+ generic getle16 : (f : file# -> status(@a::(numeric,integral)))
+ generic getle32 : (f : file# -> status(@a::(numeric,integral)))
+ generic getle64 : (f : file# -> status(@a::(numeric,integral)))
+
+ /* unsigned big endian */
+ generic putbe8 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ generic putbe16 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ generic putbe32 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ generic putbe64 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+
+ /* unsigned little endian */
+ generic putle8 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ generic putle16 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ generic putle32 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ generic putle64 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ ;;
+
+The Data Structures
+-------------------
+
+ type file = struct
+ fd : std.fd
+ ;;
+
+The `bio.file` type contains the state required to implement buffered files.
+All of the state is internal, except for the file descriptor, which is exposed
+for the purposes of poll() and friends. Reading from it directly may lead to
+inconsistent buffer state, and is strongly not recommended.
+
+ type ioerr = union
+ `Ebadfile
+ `Ebadbuf
+ `Ebadfd
+ `Eioerr
+ ;;
+
+ type status(@a) = union
+ `Eof
+ `Ok @a
+ `Err ioerr
+ ;;
+
+The `bio.status(@a)` union returns the result of the read operation, which
+is going to either be a result of type '@a', an error-free end of file, or
+an error of type `ioerr`. All of these conditions are transient, and are
+retried on subsequent calls to the IO operations. For example, if reading
+a file returns `Eof, but data is appended to a file, then a subsequent read
+will return data again.
+
+In general, errors will be queued up, so if there is a buffered read or
+write that partly succeeds but is interrupted halfway, the error will be
+reported on the next operation. This way, partial operations are not lost.
+
+File Creation and Opening
+-------------------------
+
+ const Rd : mode
+ const Wr : mode
+ const Rw : mode
+
+When opening a file, one of the above flags must be passed to set the mode
+of the file. `Rd` indicates that the bio file should be read only, `Wr`
+indicates that it should be write only, and `Rw` indicates that it should be
+both readable and writable.
+
+Bio file creation
+-------------
+
+ const mkfile : (fd : std.fd, mode : mode -> file#)
+
+This function creates a bio file from a file descriptor and mode, returning a
+`bio.file#` which will buffer reads and/or writes from the fd. This function
+assumes that you are passing it a correctly initialized fd, and will always
+succeed. If the FD is incorrectly configured, uses of it will error out.
+
+Returns: A buffered file wrapping the file descriptor `fd`
+
+ const open : (path : byte[:], mode : mode -> std.result(file#, byte[:]))
+
+This function attempts to open the path passed in with the mode, returning
+a result which is either a file, or a string representing the error that
+prevented the file from being opened.
+
+Returns: A buffered file representing `path` opened with the requested
+permissions.
+
+ const dial : (srv : byte[:], mode : mode -> std.result(file#, byte[:]))
+
+This function is similar to open, however, this function will open a
+connection via a dialstring (as in `std.dial`), and buffer the fd returned
+from that.
+
+Returns: A buffered file representing `dialstr` opened with the requested
+permissions.
+
+ const create : (path : byte[:], mode : mode, perm : int -> std.result(file#, byte[:]))
+
+This function is similar to open, however, this function will attempt to
+atomically create and open the file descriptor.
+
+Returns: A buffered file representing `path` opened with the requested
+permissions.
+
+ const close : (f : file# -> bool)
+
+Closes the file descriptor that the bio file is wrapping, and frees any
+resources used for buffering it. Any data that has not yet been written is
+flushed.
+
+Returns: `true` if flushing the file succeeded, `false` if it failed.
+
+ const free : (f : file# -> void)
+
+Frees any resources used for the file descriptor, but leaves it open. This is
+useful if the file descriptor has been 'stolen' using bio.mkfile, and is not
+owned by the bio file.
+
+ const flush : (f : file# -> bool)
+
+Clears any data that has not been sent to the backing file descriptor.
+
+Returns: `true` if flushing the file succeeded, `false` if it failed.
+
+Binary I/O
+-----------
+
+ const write : (f : file#, src : byte[:] -> result(std.size, byte[:]))
+
+Writes bytes from the buffer `src` to a bio file, returning the number of
+bytes written in the case of success. This number may be smaller than the size
+of the buffer, but will never be larger.
+
+ const read : (f : file#, dst : byte[:] -> result(byte[:]))
+
+Reads from a bio file, into the buffer 'dst', returning the section of the
+buffer that was read in the result.
+
+ const seek : (f : file#, std.off -> std.result(std.off, ioerr))
+
+Seeks the bio file to the given absolute offset.
+
+ const flush : (f : file# -> bool)
+
+Flush attempts to clear the buffers within `f`, writing everything within
+the buffers and discarding them. If writing fails, `false` is returned.
+
+
+Single Unit Operations
+----------------------
+
+ /* single unit operations */
+ const putb : (f : file#, b : byte -> status(std.size))
+
+Writes a single byte out to the file 'f', returning a status. If
+it is successful, the return value represents the number of bytes written.
+For single byte writes, this value is unsurprisingly always 1.
+
+ const putc : (f : file#, c : char -> status(std.size))
+
+Writes a single unicode character out to the file 'f', returning a status.
+This character is encoded in utf-8. If it is successful, the return value
+represents the number of bytes written, varying between 1 and 4.
+
+ const getb : (f : file# -> status(byte))
+
+Reads a single byte from the file `f`, returning a status. If this read
+succeeds, the next byte in the file is returned.
+
+ const getc : (f : file# -> status(char))
+
+Reads a unicode character from the file `f`, returning a status. If this read
+was successful, the next character is returned from the file. The file is
+assumed to be encoded in utf-8.
+
+ /* peeking */
+ const peekb : (f : file# -> status(byte))
+ const peekc : (f : file# -> status(char))
+
+Both peekb and peekc are similar to getb and getc respectively, although
+they return the value without advancing the position within the buffer.
+
+
+Delimited Operations
+--------------------
+
+ /* delimited read; returns freshly allocated buffer. */
+ const readln : (f : file# -> status(byte[:]))
+
+Readln reads a single line of input from the file 'f', returning the line
+with the line ending characters removed. '\n', '\r', and '\r\n' are all
+accepted as valid line endings, and are treated interchangably when reading.
+
+The buffer is heap allocated, and must be freed with std.slfree
+
+ const readto : (f : file#, delim : byte[:] -> status(byte[:]))
+
+Readto is similar to readln, but instead of reading to a line ending, it will
+read up to the point where it finds the requested delimiter. The delimiter is
+an arbitrary sequence of bytes. If an end of file is reached, then the buffer
+up to the Eof is returned.
+
+The buffer is heap allocated, and must be freed with std.slfree
+
+ const skipto : (f : file#, delim : byte[:] -> bool)
+
+Skipto is identical to readto, but instead of allocating a buffer and reading
+into it, skipto will ignore the values and drop them. It returns true for
+any successful reads, and false if an error was encountered.
+
+ const skipspace : (f : file# -> bool)
+
+Skipspace will consume any space tokens from the start of the input stream, It
+returns true for any successful reads, and false if an error was encountered.
+
+Iteration
+---------
+
+ const lineiter : (f : file# -> lineiter)
+
+Lineiter will return an interable type that will iterate through each line
+in a file. These lines are allocated on the heap, and are automatically freed
+at the end of the iteration.
+
+The returned iterator object is a value type, and does not need to be freed.
+
+Formatted IO
+-------------
+
+ const put : (f : file#, fmt : byte[:], args : ... -> status(std.size))
+
+This formats the output using the std.fmt api, and writes it to the file `f`.
+All custom formatters installed for `std.fmt` are used for this formatting.
+This call returns the number of bytes written on success.
+
+Endian Aware Reads
+------------------
+
+ generic getbe8 : (f : file# -> status(@a::(numeric,integral)))
+ generic getbe16 : (f : file# -> status(@a::(numeric,integral)))
+ generic getbe32 : (f : file# -> status(@a::(numeric,integral)))
+ generic getbe64 : (f : file# -> status(@a::(numeric,integral)))
+
+ generic getle8 : (f : file# -> status(@a::(numeric,integral)))
+ generic getle16 : (f : file# -> status(@a::(numeric,integral)))
+ generic getle32 : (f : file# -> status(@a::(numeric,integral)))
+ generic getle64 : (f : file# -> status(@a::(numeric,integral)))
+
+The functions above all read from a bio file, and return the value read on
+success. The 'be' variants read big endian values, and the `le' variants
+read little endian values. They final result is converted to whatever
+numeric, integral type is desired. If the value does not fit within the bounds
+of the type, it is truncated.
+
+The number of bytes read is determined by the number at the end of the
+function call. For eample, `getbe8()` will read 8 bits, or one byte from the
+stream. `getle64` will get a 64 bit, or 8 byte, value. The number of bytes
+consumed is independent of the size of the type being assigned to.
+
+ generic putbe8 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ generic putbe16 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ generic putbe32 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ generic putbe64 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+
+ generic putle8 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ generic putle16 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ generic putle32 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ generic putle64 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+
+The functions above all write to a bio file, and return the number of bytes
+written on success. The number of bytes written will always be the size of the
+type. The 'be' variants write the value in a big endian representation, and
+the 'le' variants write it in a little endian representation.
+
+The number of bytes written is determined by the number at the end of the
+function call. For eample, `putbe8()` will write 8 bits, or one byte.
+`putbe64` will write 64 bits, or 8 bytes. The number of
+bytes consumed is independent of the size of the type being assigned to.
+
+Examples
+---------
+
+The example below is the simplest program that creates and opens a file, and
+writes to it. It creates it with 0o644 permissions (ie, rw-r--r--), and then
+immediately closes it. The result of this program should be a file called
+`create-example` containing the words.
+
+ use std
+ use bio
+
+ const main = {
+ var f
+
+ match bio.create("create-example", bio.Wr, 0o644)
+ | `std.Some bio: f = bio
+ | `std.None: std.fatal(1, "Failed to open file\n")
+ ;;
+ bio.write(f, "Hello user\n")
+ bio.close(f)
+ }
+
+The next example shows reading from a file called "lines", line by line. It
+should echo the lines, numbering them as it prints them:
+
+ use std
+ use bio
+
+ const main = {
+ var f
+ var i
+
+ match bio.open("lines", bio.Rd)
+ | `std.Some bio: f = bio
+ | `std.None: std.fatal(1, "Unable to open data file\n")
+ ;;
+
+ while true
+ match bio.readlin(f)
+ | `std.Some ln:
+ std.put("line %i: %s\n", i, ln)
+ | `std.None:
+ break;
+ ;;
+ ;;
+ }