iostream
Binary streams and blobs.
from "iostream" import ...
Streams and the cursor
A stream is a cursor over a run of bytes. readn and writen act at the cursor and move it forward by as many bytes as they touched, tell reports where it is, and seek puts it somewhere else. Nothing here reads or writes at an address; every operation is relative to where the cursor is.
Two things are streams, and they share the same method set, listed on the stream page: a blob, whose bytes are in memory, and a file from the io module. Code written against the stream methods works with either, which is the usual reason to parse a downloaded buffer and a file on disk with the same function.
What a blob is
A blob is a resizable block of memory with a cursor on it. blob(n) gives n zero bytes with the cursor at the start; blob(0) is empty.
Length and cursor are independent, and mixing them up is the common bug:
- Writing past the end grows the blob, so a blob is an output buffer that never needs sizing up front.
- resize changes the length and leaves the cursor where it was. After growing a blob you are not at the new end;
seek(0, 'e')goes there. - len is the size, not the amount written. A
blob(1024)that has taken four bytes still reports 1024.
Indexing bytes directly
A blob is also indexable, which the stream methods are not. b[i] reads the byte at position i as an integer from 0 to 255, and b[i] = v writes one. Neither moves the cursor, so byte access and stream access can be mixed freely.
let b = blob(3)
b[0] = 72; b[1] = 105; b[2] = 33
b.as_string() // "Hi!", and the cursor is still at 0
foreach over a blob yields the position and the byte, in order.
There are two edge cases. An index outside the blob throws Error in '_get' metamethod: index out of range, since indexing is implemented as a metamethod. A value outside 0 to 255 does not throw: only the low byte is stored, so b[0] = 300 stores 44. Mask or range-check the value yourself when it comes from arithmetic.
Text and bytes
as_string is the conversion to text: it returns the bytes as a string. tostring is the ordinary object printer and gives an address, so it does not show the contents in a log.
Functions
| castf2i | pure fastcall castf2i(f: number): int | Reinterprets the bits of a float as an integer |
| casti2f | pure fastcall casti2f(i: int): float | Reinterprets the bits of an integer as a float |
| swap2 | pure fastcall swap2(val: number): int | Byte-swaps a 16-bit value |
| swap4 | pure fastcall swap4(val: number): int | Byte-swaps a 32-bit value |
| swapfloat | pure fastcall swapfloat(val: number): float | Byte-swaps the bits of a float |
blob class
A resizable block of memory read and written as a stream.
Functions
| as_string | instance.as_string(): string | Returns the blob contents as a string |
| constructor | constructor([size: int]): instance | Creates a blob of the given size (default 0) |
| eos | instance.eos(): int|null | Returns non-null if the stream is at end-of-stream |
| flush | instance.flush(): int|null | Flushes the stream and returns non-null on success |
| len | instance.len(): int | Returns the stream length |
| readblob | instance.readblob(size: int): instance | Reads up to size bytes and returns them as a blob |
| readn | instance.readn(format: int): number | Reads a value of the given numeric format and returns it |
| readobject | instance.readobject([classes: table|null]): any | Deserializes an object from the stream |
| resize | instance.resize(size: int) | Resizes the blob to the given size |
| seek | instance.seek(offset: int, [origin: int]): int | Seeks to the given offset; origin is 'b' (begin), 'c' (current) or 'e' (end) |
| swap2 | instance.swap2() | Byte-swaps the blob contents as an array of 16-bit values |
| swap4 | instance.swap4() | Byte-swaps the blob contents as an array of 32-bit values |
| tell | instance.tell(): int | Returns the current stream position |
| tostring | instance.tostring(): string | Allows calling .tostring() on blob instances (bypassing _get) |
| writeblob | instance.writeblob(blob: instance): int | Writes the given blob and returns the number of bytes written |
| writen | instance.writen(value: number, format: int) | Writes a numeric value in the given format |
| writeobject | instance.writeobject(obj, [classes: table|null]) | Serializes the object to the stream |
| writestring | instance.writestring(str: string): int | Writes the string and returns the number of characters written |
stream class
The stream methods shared by blob and file.
Functions
| eos | instance.eos(): int|null | Returns non-null if the stream is at end-of-stream |
| flush | instance.flush(): int|null | Flushes the stream and returns non-null on success |
| len | instance.len(): int | Returns the stream length |
| readblob | instance.readblob(size: int): instance | Reads up to size bytes and returns them as a blob |
| readn | instance.readn(format: int): number | Reads a value of the given numeric format and returns it |
| readobject | instance.readobject([classes: table|null]): any | Deserializes an object from the stream |
| seek | instance.seek(offset: int, [origin: int]): int | Seeks to the given offset; origin is 'b' (begin), 'c' (current) or 'e' (end) |
| tell | instance.tell(): int | Returns the current stream position |
| writeblob | instance.writeblob(blob: instance): int | Writes the given blob and returns the number of bytes written |
| writen | instance.writen(value: number, format: int) | Writes a numeric value in the given format |
| writeobject | instance.writeobject(obj, [classes: table|null]) | Serializes the object to the stream |
| writestring | instance.writestring(str: string): int | Writes the string and returns the number of characters written |