API/buffer: Difference between revisions

From OpenComputers: Rebooted
Jump to navigation Jump to search
imported>OCDoc Import
Imported from legacy OpenComputers documentation at ocdoc.cil.li
 
Clean up Markdown artifacts from DokuWiki migration
 
Line 1: Line 1:
= Buffer API =
= Buffer API =


The `buffer` library provides user friendly streams. These are the kind that the `io` library returns from `io.open` '''unlike''' the raw streams returned by `filesystem.open` which don't support as many helpful methods. These helper methods on the file handles you get from `io.open` are defined here, under [[API/buffer#instance_methods|Instance Methods]]. Thus, this API documentation is important and helpful even if you aren't building your own buffered streams.
The <code>buffer</code> library provides user friendly streams. These are the kind that the <code>io</code> library returns from <code>io.open</code> '''unlike''' the raw streams returned by <code>filesystem.open</code> which don't support as many helpful methods. These helper methods on the file handles you get from <code>io.open</code> are defined here, under [[API/buffer#instance_methods|Instance Methods]]. Thus, this API documentation is important and helpful even if you aren't building your own buffered streams.


Additionally, this API allows you to create buffered streams. You provide the backend stream read and write, the buffer library provides the formatting and buffering of the data. Generally, users will not need to make their own buffered streams. For reference, the io library uses buffered streams (which includes file io as well as terminal io)
Additionally, this API allows you to create buffered streams. You provide the backend stream read and write, the buffer library provides the formatting and buffering of the data. Generally, users will not need to make their own buffered streams. For reference, the io library uses buffered streams (which includes file io as well as terminal io)
Line 7: Line 7:
== Static Methods ==
== Static Methods ==


The following methods are called on the `buffer` library itself.
The following methods are called on the <code>buffer</code> library itself.


* `buffer.new([mode: string], stream: table)`
* <code>buffer.new([mode: string], stream: table)</code>


<pre>Creates a new buffered stream, wrapping `stream` with read-write `mode`. `mode` can be readonly (r or `nil`), read-write (rw), or write-only (w). Read about the stream [[API/buffer#interface_methods|interface methods]] required on the `stream` object.
<pre>Creates a new buffered stream, wrapping stream with read-write mode. mode can be readonly (r or nil), read-write (rw), or write-only (w). Read about the stream [[API/buffer#interface_methods|interface methods]] required on the stream object.
</pre>
</pre>
== Instance Methods ==
== Instance Methods ==


The following methods can only be called on instances created by `buffer.new` ('''note''' file handles returned by `io.open` are also buffered streams, created with `buffer.new`). These methods are instance methods, requiring instance call notation `:`. In order to help differentiate these instance methods from static methods (e.g. `buffer.new`), `b:` will be used to prefix the method names.
The following methods can only be called on instances created by <code>buffer.new</code> ('''note''' file handles returned by <code>io.open</code> are also buffered streams, created with <code>buffer.new</code>). These methods are instance methods, requiring instance call notation <code>:</code>. In order to help differentiate these instance methods from static methods (e.g. <code>buffer.new</code>), <code>b:</code> will be used to prefix the method names.


* `b:flush()`
* <code>b:flush()</code>


<pre>If any data is buffered it is immediately written to the stream and released.
<pre>If any data is buffered it is immediately written to the stream and released.
</pre>
</pre>
* `b:close()`
* <code>b:close()</code>


<pre>Flushes the buffer and closes the wrapped stream.
<pre>Flushes the buffer and closes the wrapped stream.
</pre>
</pre>
* `b:setvbuf([mode: string], [size: number]) mode, size`
* <code>b:setvbuf([mode: string], [size: number]) mode, size</code>


<pre>Sets the buffering `mode` and `size` and returns the result `mode` and `size`. The amount of data buffered is specified by `size` which defaults to [512, 8192] bytes, depending on available system memory. `mode` and `size` can be nil, in which case the previous values are used for either. `size` is also used in `read(n)` calls to the stream.
<pre>Sets the buffering mode and size and returns the result mode and size. The amount of data buffered is specified by size which defaults to [512, 8192] bytes, depending on available system memory. mode and size can be nil, in which case the previous values are used for either. size is also used in read(n) calls to the stream.


Modes only affect `write`, which include:
Modes only affect write, which include:
* "no" writes are immediately pushed to the stream.
* "no" writes are immediately pushed to the stream.
* "full" writes are buffered up to `size` bytes. This is the default mode.
* "full" writes are buffered up to size bytes. This is the default mode.
* "line" writes are buffered until newlines are found or `size` is reached, whichever comes first.
* "line" writes are buffered until newlines are found or size is reached, whichever comes first.
</pre>
</pre>
* `b:write([values...])`
* <code>b:write([values...])</code>


<pre>Writes each `value` to the stream, first buffering based on the mode and buffer size (see `setvbuf`). Note that to write to a file, you have to open it for ''write''.
<pre>Writes each value to the stream, first buffering based on the mode and buffer size (see setvbuf). Note that to write to a file, you have to open it for ''write''.
</pre>
</pre>
```lua local file = io.open("/tmp/foo.txt", "w") file:write("abc", "def", "\n") file:close() -- foo.txt now has "abcdef\n" ```
<syntaxhighlight lang="lua">
local file = io.open("/tmp/foo.txt", "w") file:write("abc", "def", "\n") file:close() -- foo.txt now has "abcdef\n"
</syntaxhighlight>


* `b:lines([line_formats...]) string array`
* <code>b:lines([line_formats...]) string array</code>


<pre>Returns a function iterator which reads from the stream until it reaches nil. On each read, the `line_formats` list of args as passed to `stream:read(...)`. The overwhelmingly typical use is to not define `line_formats`, i.e. passing no args to `lines()`. The default behavior (i..e without `line_formats`) is to read a "line" at a time from the stream.
<pre>Returns a function iterator which reads from the stream until it reaches nil. On each read, the line_formats list of args as passed to stream:read(...). The overwhelmingly typical use is to not define line_formats, i.e. passing no args to lines(). The default behavior (i..e without line_formats) is to read a "line" at a time from the stream.
</pre>
</pre>
```lua local file = io.open("/tmp/foobar.txt") for line in file:lines() do
<syntaxhighlight lang="lua">
local file = io.open("/tmp/foobar.txt") for line in file:lines() do


<pre>process_next_line(line)
process_next_line(line)
</pre>
 
end file:close() ```
end file:close()
</syntaxhighlight>


* `b:read([formats...]) string...`
* <code>b:read([formats...]) string...</code>


<pre>A fairly advanced reader that support various formats. First of all, if called with no `format`, i.e an empty param list, it reads the next line from the stream, which is equivalent to `read("*l")`
<pre>A fairly advanced reader that support various formats. First of all, if called with no format, i.e an empty param list, it reads the next line from the stream, which is equivalent to read("*l")


Each `format` is read from the stream and all returned in a multiple return value list of the results. Note all format strings are prefixed with \* and also note that only the first char of the string names of the formats matters, the rest is ignored. These are the supported formats:
Each format is read from the stream and all returned in a multiple return value list of the results. Note all format strings are prefixed with \* and also note that only the first char of the string names of the formats matters, the rest is ignored. These are the supported formats:
</pre>
</pre>
<pre>  * a number value, e.g. `10`
<pre>  * a number value, e.g. 10
</pre>
</pre>
<pre>  Read **n** bytes (in binary mode) or chars (in text mode) from the stream; result is returned as a string. See [[API/non-standard-lua-libs#input_and_output_facilities|io.open]] for more details about how to open files in different modes.
<pre>  Read **n** bytes (in binary mode) or chars (in text mode) from the stream; result is returned as a string. See [[API/non-standard-lua-libs#input_and_output_facilities|io.open]] for more details about how to open files in different modes.
</pre>
</pre>
<pre>  `local chars = b:read(10)`
<pre>  local chars = b:read(10)
</pre>
</pre>
<pre>  '' "\''n" or "\*number"
<pre>  '' "\''n" or "\*number"
Line 66: Line 70:
<pre>  Read the next series of bytes from the stream that can be interpreted as a number. Note that reading numbers is also affected by the open mode, binary or text. See [[API/non-standard-lua-libs#input_and_output_facilities|io.open]] for more details about how to open files in different modes..
<pre>  Read the next series of bytes from the stream that can be interpreted as a number. Note that reading numbers is also affected by the open mode, binary or text. See [[API/non-standard-lua-libs#input_and_output_facilities|io.open]] for more details about how to open files in different modes..
</pre>
</pre>
<pre>  `local number = b:read("*n")`
<pre>  local number = b:read("*n")
</pre>
</pre>
<pre>  '' "\''l" or "\*line"
<pre>  '' "\''l" or "\*line"
Line 72: Line 76:
<pre>  Read the next line from the stream, chopping off the line ending marker (which may be \n, \r, or \r\n)
<pre>  Read the next line from the stream, chopping off the line ending marker (which may be \n, \r, or \r\n)
</pre>
</pre>
<pre>  `local line = b:read("*l")`
<pre>  local line = b:read("*l")
</pre>
</pre>
<pre>  '' "\''L" or "\*Line"
<pre>  '' "\''L" or "\*Line"
Line 78: Line 82:
<pre>  Read the next line from the stream, like "*line", but preserves the line ending marker as part of the result
<pre>  Read the next line from the stream, like "*line", but preserves the line ending marker as part of the result
</pre>
</pre>
<pre>  `local whole_line = b:read("*L")`
<pre>  local whole_line = b:read("*L")
</pre>
</pre>
<pre>  '' "\''a" or "\*all"
<pre>  '' "\''a" or "\*all"
Line 84: Line 88:
<pre>  Reads all remaining data from the stream until nil. There would be no point in having formats following this.
<pre>  Reads all remaining data from the stream until nil. There would be no point in having formats following this.
</pre>
</pre>
<pre>  `local the_whole_file = b:read("*a")`
<pre>  local the_whole_file = b:read("*a")
</pre>
</pre>
* `b:getTimeout() number`
* <code>b:getTimeout() number</code>


<pre>Returns the current timeout (in seconds) set on the buffered stream. `math.huge` is the default timeout. Read `setTimeout` for more information about the effects of a buffered stream timeout.
<pre>Returns the current timeout (in seconds) set on the buffered stream. math.huge is the default timeout. Read setTimeout for more information about the effects of a buffered stream timeout.
</pre>
</pre>
* `b:setTimeout(timeout)`
* <code>b:setTimeout(timeout)</code>


<pre>Sets the time in seconds a buffered stream will try to limit a `read` operation. Note that this timeout cannot be strictly adhered to. A read operation that completes within a single `readChunk` (an internal method that invokes the actual `read` on the stream) does not check the `timeout` limit. Timeout is only checked between stream reads within a single buffered read (an example follows). Thus, if a read requires multiple chunk reads, and the time between the start of the first read before the start of the last read is greater than or equal to the timeout, then the buffered stream will error. Again note that a timeout is default `math.huge`.
<pre>Sets the time in seconds a buffered stream will try to limit a read operation. Note that this timeout cannot be strictly adhered to. A read operation that completes within a single readChunk (an internal method that invokes the actual read on the stream) does not check the timeout limit. Timeout is only checked between stream reads within a single buffered read (an example follows). Thus, if a read requires multiple chunk reads, and the time between the start of the first read before the start of the last read is greater than or equal to the timeout, then the buffered stream will error. Again note that a timeout is default math.huge.
</pre>
</pre>
```lua local file = buffer.new("r", { read = function() os.sleep(5) return "a" end }) file:setvbuf("full", 1) -- set buffer size to 1 char file:setTimeout(1) -- set buffer timeout to 1 second -- this will time out before trying to read the 2nd char local a, b = file:read(1, 1) -- read 1 char, then read 1 char again ```
<syntaxhighlight lang="lua">
local file = buffer.new("r", { read = function() os.sleep(5) return "a" end }) file:setvbuf("full", 1) -- set buffer size to 1 char file:setTimeout(1) -- set buffer timeout to 1 second -- this will time out before trying to read the 2nd char local a, b = file:read(1, 1) -- read 1 char, then read 1 char again
</syntaxhighlight>


* `b:seek([whence:string], [offset:number])`
* <code>b:seek([whence:string], [offset:number])</code>


<pre>Moves the stream position by `offset` bytes from `whence`, both optional params. `whence` defaults to "cur", and `offset` defaults to 0.
<pre>Moves the stream position by offset bytes from whence, both optional params. whence defaults to "cur", and offset defaults to 0.
Valid `whence` values:
Valid whence values:
* "cur" from the current position.
* "cur" from the current position.
* "set" from the start of the stream.
* "set" from the start of the stream.
Line 107: Line 113:
== Interface Methods ==
== Interface Methods ==


The following methods are expected to be implemented on the buffered streams passed to `buffer.new`.
The following methods are expected to be implemented on the buffered streams passed to <code>buffer.new</code>.


* `close() ok, reason`
* <code>close() ok, reason</code>


<pre>Close handles, release resources, disconnect -- and return success
<pre>Close handles, release resources, disconnect -- and return success
</pre>
</pre>
* `write(arg: string) ok, reason`
* <code>write(arg: string) ok, reason</code>


<pre>Write `arg` as bytes, assume a string of plain unformatted chars. Return falsey and reason on failure.
<pre>Write arg as bytes, assume a string of plain unformatted chars. Return falsey and reason on failure.
</pre>
</pre>
* `read(n: number) ok, reason`
* <code>read(n: number) ok, reason</code>


<pre>Return `n` bytes, and **not** `n` unicode-aware chars. Assume your data is binary data and let the buffer library manage the mode and the unicode string packaging (if applicable). Note that this is exactly how the [[API/filesystem|filesystem]] library operates.The caller assumes there is more data to read until `nil` is returned. A empty string or a string shorter than `n` chars long is a valid return, but the caller may assume there is more data to request until `nil` is returned.
<pre>Return n bytes, and **not** n unicode-aware chars. Assume your data is binary data and let the buffer library manage the mode and the unicode string packaging (if applicable). Note that this is exactly how the [[API/filesystem|filesystem]] library operates.The caller assumes there is more data to read until nil is returned. A empty string or a string shorter than n chars long is a valid return, but the caller may assume there is more data to request until nil is returned.
</pre>
</pre>
* `seek([whence: string], [offset: number]) [offset from start] or falsey, reason`
* <code>seek([whence: string], [offset: number]) [offset from start] or falsey, reason</code>


<pre>Refer to `b:seek()` for details. In short, move the stream position to `offset` from `whence`, and return the `offset` from the start of the stream of the position after the seek operation. Note that `seek("cur", 0)` is a valid request, typical of the caller wanting to determine the current position of the stream. Your stream is not required to support `seek`, in such case (or in any case of failure) you should return nil, and the reason (as a string) for the failure.
<pre>Refer to b:seek() for details. In short, move the stream position to offset from whence, and return the offset from the start of the stream of the position after the seek operation. Note that seek("cur", 0) is a valid request, typical of the caller wanting to determine the current position of the stream. Your stream is not required to support seek, in such case (or in any case of failure) you should return nil, and the reason (as a string) for the failure.
</pre>
</pre>
== Examples ==
== Examples ==
Line 130: Line 136:


{{:API/contents}}
{{:API/contents}}

Latest revision as of 20:26, 24 August 2026

Buffer API

The buffer library provides user friendly streams. These are the kind that the io library returns from io.open unlike the raw streams returned by filesystem.open which don't support as many helpful methods. These helper methods on the file handles you get from io.open are defined here, under Instance Methods. Thus, this API documentation is important and helpful even if you aren't building your own buffered streams.

Additionally, this API allows you to create buffered streams. You provide the backend stream read and write, the buffer library provides the formatting and buffering of the data. Generally, users will not need to make their own buffered streams. For reference, the io library uses buffered streams (which includes file io as well as terminal io)

Static Methods

The following methods are called on the buffer library itself.

  • buffer.new([mode: string], stream: table)
Creates a new buffered stream, wrapping stream with read-write mode. mode can be readonly (r or nil), read-write (rw), or write-only (w). Read about the stream [[API/buffer#interface_methods|interface methods]] required on the stream object.

Instance Methods

The following methods can only be called on instances created by buffer.new (note file handles returned by io.open are also buffered streams, created with buffer.new). These methods are instance methods, requiring instance call notation :. In order to help differentiate these instance methods from static methods (e.g. buffer.new), b: will be used to prefix the method names.

  • b:flush()
If any data is buffered it is immediately written to the stream and released.
  • b:close()
Flushes the buffer and closes the wrapped stream.
  • b:setvbuf([mode: string], [size: number]) mode, size
Sets the buffering mode and size and returns the result mode and size. The amount of data buffered is specified by size which defaults to [512, 8192] bytes, depending on available system memory. mode and size can be nil, in which case the previous values are used for either. size is also used in read(n) calls to the stream.

Modes only affect write, which include:
* "no" writes are immediately pushed to the stream.
* "full" writes are buffered up to size bytes. This is the default mode.
* "line" writes are buffered until newlines are found or size is reached, whichever comes first.
  • b:write([values...])
Writes each value to the stream, first buffering based on the mode and buffer size (see setvbuf). Note that to write to a file, you have to open it for ''write''.
local file = io.open("/tmp/foo.txt", "w") file:write("abc", "def", "\n") file:close() -- foo.txt now has "abcdef\n"
  • b:lines([line_formats...]) string array
Returns a function iterator which reads from the stream until it reaches nil. On each read, the line_formats list of args as passed to stream:read(...). The overwhelmingly typical use is to not define line_formats, i.e. passing no args to lines(). The default behavior (i..e without line_formats) is to read a "line" at a time from the stream.
local file = io.open("/tmp/foobar.txt") for line in file:lines() do

process_next_line(line)

end file:close()
  • b:read([formats...]) string...
A fairly advanced reader that support various formats. First of all, if called with no format, i.e an empty param list, it reads the next line from the stream, which is equivalent to read("*l")

Each format is read from the stream and all returned in a multiple return value list of the results. Note all format strings are prefixed with \* and also note that only the first char of the string names of the formats matters, the rest is ignored. These are the supported formats:
  * a number value, e.g. 10
  Read **n** bytes (in binary mode) or chars (in text mode) from the stream; result is returned as a string. See [[API/non-standard-lua-libs#input_and_output_facilities|io.open]] for more details about how to open files in different modes.
  local chars = b:read(10)
  '' "\''n" or "\*number"
  Read the next series of bytes from the stream that can be interpreted as a number. Note that reading numbers is also affected by the open mode, binary or text. See [[API/non-standard-lua-libs#input_and_output_facilities|io.open]] for more details about how to open files in different modes..
  local number = b:read("*n")
  '' "\''l" or "\*line"
  Read the next line from the stream, chopping off the line ending marker (which may be \n, \r, or \r\n)
  local line = b:read("*l")
  '' "\''L" or "\*Line"
  Read the next line from the stream, like "*line", but preserves the line ending marker as part of the result
  local whole_line = b:read("*L")
  '' "\''a" or "\*all"
  Reads all remaining data from the stream until nil. There would be no point in having formats following this.
  local the_whole_file = b:read("*a")
  • b:getTimeout() number
Returns the current timeout (in seconds) set on the buffered stream. math.huge is the default timeout. Read setTimeout for more information about the effects of a buffered stream timeout.
  • b:setTimeout(timeout)
Sets the time in seconds a buffered stream will try to limit a read operation. Note that this timeout cannot be strictly adhered to. A read operation that completes within a single readChunk (an internal method that invokes the actual read on the stream) does not check the timeout limit. Timeout is only checked between stream reads within a single buffered read (an example follows). Thus, if a read requires multiple chunk reads, and the time between the start of the first read before the start of the last read is greater than or equal to the timeout, then the buffered stream will error. Again note that a timeout is default math.huge.
local file = buffer.new("r", { read = function() os.sleep(5) return "a" end }) file:setvbuf("full", 1) -- set buffer size to 1 char file:setTimeout(1) -- set buffer timeout to 1 second -- this will time out before trying to read the 2nd char local a, b = file:read(1, 1) -- read 1 char, then read 1 char again
  • b:seek([whence:string], [offset:number])
Moves the stream position by offset bytes from whence, both optional params. whence defaults to "cur", and offset defaults to 0.
Valid whence values:
* "cur" from the current position.
* "set" from the start of the stream.
* "end" from the end of the stream.
Returns the result of the seek operation on the stream (which may fail).

Interface Methods

The following methods are expected to be implemented on the buffered streams passed to buffer.new.

  • close() ok, reason
Close handles, release resources, disconnect -- and return success
  • write(arg: string) ok, reason
Write arg as bytes, assume a string of plain unformatted chars. Return falsey and reason on failure.
  • read(n: number) ok, reason
Return n bytes, and **not** n unicode-aware chars. Assume your data is binary data and let the buffer library manage the mode and the unicode string packaging (if applicable). Note that this is exactly how the [[API/filesystem|filesystem]] library operates.The caller assumes there is more data to read until nil is returned. A empty string or a string shorter than n chars long is a valid return, but the caller may assume there is more data to request until nil is returned.
  • seek([whence: string], [offset: number]) [offset from start] or falsey, reason
Refer to b:seek() for details. In short, move the stream position to offset from whence, and return the offset from the start of the stream of the position after the seek operation. Note that seek("cur", 0) is a valid request, typical of the caller wanting to determine the current position of the stream. Your stream is not required to support seek, in such case (or in any case of failure) you should return nil, and the reason (as a string) for the failure.

Examples

Contents

APIs
OpenOS buffer - colors - component - computer - event - filesystem - uuid - internet - keyboard - note - process - rc - robot - serialization - shell - sides - term - text - thread - transforms - unicode
Lua Libraries coroutine - package - io - os