LEVIATHAN v962456e · 962456eee1

Standard Library

class Block

A fixed-length, mutable buffer of bytes.

since 0.1.0-alpha.1linuxwindowswasm

Overview

A Block is created zero-filled with a length that never changes. Bytes are read and written by index, singly with byteAt and setByte, as little-endian 32-bit or 64-bit integers with int32At and int64At, or in bulk with fill and blit. Every access is bounds-checked and throws a RuntimeException when it falls outside the block.

A Block is a reference type: assigning it shares the same bytes. slice returns a view onto part of a block that shares its storage, so a write through the view is visible in the original and the reverse. == compares references, whereas equals compares the bytes. Printing a Block shows only its length, as Block(len=8).

Description

Block is the language's mutable byte buffer. Nothing converts to or from it implicitly, so every use of a Block is a deliberate choice. It is a reference type: it is mutated in place and shared by reference like a class, unlike the immutable Array and Map, whose methods return new values.

A Block has a fixed length that is set when it is created. Block(n) makes a buffer of n zero bytes, and Block::fromString(text) copies the bytes of a string into a new one. byteAt and setByte read and write a single byte as an integer from 0 to 255, and toString(off, len) copies a range of bytes out as a string.

Creating, reading and writing bytes

Block b = Block(8);
console.writeln(b.length());
console.writeln(b.byteAt(0));

b.setByte(0, 72);
b.setByte(1, 105);
console.writeln(b.toString(0, 2));

Block c = Block::fromString("Hi");
console.writeln(c.length());
console.writeln(c.toString(0, 2));
console.writeln(b.equals(c));
console.writeln(b.slice(0, 2).equals(c));
console.writeln(b == c);
console.writeln(b);
8
0
Hi
2
Hi
false
true
false
Block(len=8)

b == c and b.equals(c) mean different things. == asks whether two names refer to the same buffer, and equals compares the bytes. In the program above, b and c are two buffers, so == is false, but a two-byte view of b has the same content as c, so equals is true. Printing a Block shows only its length, as Block(len=8).

Rules

  • Every access is bounds-checked. An offset or length outside the buffer throws a RuntimeException, and so does a value outside 0 to 255 passed to setByte or fill: a byte is never silently masked.
  • slice(off, len) returns a view that shares its storage with the original. A write through the view is visible in the parent and the other way round, and the view keeps the shared bytes alive. Copying is a separate, explicit step, with toString, blit or Block::fromString.
  • int32At(i) and setInt32(i, v) read and write four bytes, int64At(i) and setInt64(i, v) eight bytes, in little-endian order. The read of a four-byte integer is sign-extended: a stored bit pattern with the top bit set reads back as a negative int.
  • blit(dstOff, src, srcOff, len) copies bytes with memmove semantics, so the source and destination may overlap, including two views of the same buffer. fill(off, len, value) sets a range to one byte.
  • An empty range is legal at the end of the buffer, so mismatch(other, length()) returns -1.
  • equals(other) compares the entire contents and is false for buffers of different lengths. mismatch(other, from) returns the first index at or after from where the bytes differ, or -1; it throws if the buffers have different lengths.

Examples

A slice is a window onto the same bytes, not a copy. A second name for a buffer is the same buffer:

Aliasing slices, blit and fill

Block whole = Block(6);
Block part = whole.slice(2, 3);

part.setByte(0, 200);
console.writeln(whole.byteAt(2));

whole.setByte(3, 7);
console.writeln(part.byteAt(1));
console.writeln(part.length());

Block data = Block::fromString("abcdef");
data.blit(2, data, 0, 4);
console.writeln(data.toString(0, 6));
data.fill(0, 3, 120);
console.writeln(data.toString(0, 6));

Block alias = whole;
alias.setByte(0, 1);
console.writeln(whole.byteAt(0));
console.writeln(alias == whole);
200
7
3
ababcd
xxxbcd
1
true

Integers are stored little-endian, and the four-byte read is signed:

Integer accessors

Block b = Block(16);
b.setInt32(0, 258);
console.writeln(b.byteAt(0));
console.writeln(b.byteAt(1));
console.writeln(b.int32At(0));

b.setInt32(4, -2);
console.writeln(b.int32At(4));
console.writeln(b.byteAt(7));

b.setInt64(8, 4294967296);
console.writeln(b.int64At(8));
console.writeln(b.byteAt(12));
2
1
258
-2
255
4294967296
1

Every kind of out-of-range access throws:

Bounds checks and mismatch

Block b = Block(4);
try {
    b.setByte(0, 256);
} catch (RuntimeException e) {
    console.writeln("value out of range");
}
try {
    b.byteAt(4);
} catch (RuntimeException e) {
    console.writeln("index out of range");
}
try {
    b.slice(2, 5);
} catch (RuntimeException e) {
    console.writeln("slice out of range");
}
Block other = Block(5);
try {
    b.mismatch(other, 0);
} catch (RuntimeException e) {
    console.writeln("lengths differ");
}
Block same = Block(4);
same.setByte(2, 9);
console.writeln(b.mismatch(same, 0));
console.writeln(b.mismatch(same, 3));
console.writeln(b.mismatch(same, 4));
value out of range
index out of range
slice out of range
lengths differ
2
-1
-1

Notes

  • A Block holding a packed value such as a color that is stored with setInt32 reads back negative from int32At when the value's top bit is set, because the read sign-extends. The bit pattern is not changed, so mask the value (& 0xFFFFFFFF) or compare against the sign-extended form.
  • File has read(Block, max) and write(Block, off, len) overloads, so a buffer can be filled from a file and written back without going through a string.

Examples

Writing, slicing and integers

Block b = Block(8);
b.setByte(0, 72);
b.setByte(1, 105);
console.writeln(b.toString(0, 2));
Block view = b.slice(1, 3);
view.setByte(0, 33);
console.writeln(b.toString(0, 2));
b.setInt32(4, -2);
console.writeln(b.int32At(4));
console.writeln(b.byteAt(4));
console.writeln(b);
Hi
H!
-2
254
Block(len=8)

Constructors

fromString

Block::fromString(string s)

Create a block holding the UTF-8 bytes of a string.

The bytes are copied, so the block is independent of the string. Its length is the string's length in bytes, which is larger than its character count when the text is not ASCII.

Parameters

s
The text to copy.

Examples

Block b = Block::fromString("héllo");
console.writeln(b.length());
console.writeln(b.byteAt(1));
console.writeln(b.byteAt(2));
console.writeln(b.toString(0, 6));
6
195
169
héllo

new

new(int size)

Create a block of the given size with every byte zero.

Parameters

size
The length of the block in bytes.

Examples

Block b = Block(4);
console.writeln(b.length());
console.writeln(b.byteAt(3));
4
0

Methods

blit

blit(int dstOff, Block src, int srcOff, int len) -> void

Copy bytes from another block into this one.

The copy is safe when the source and destination overlap, including two views of the same block: the destination receives the bytes the source held before the copy.

Parameters

dstOff
The index in this block where the copy starts.
src
The block to copy from.
srcOff
The index in src of the first byte to copy.
len
The number of bytes to copy.

Throws

RuntimeException
when either range is not inside its block.

Examples

Block src = Block::fromString("abcdef");
Block dst = Block(6);
dst.blit(1, src, 0, 3);
console.writeln(dst.toString(1, 3));
src.blit(2, src, 0, 4);
console.writeln(src.toString(0, 6));
abc
ababcd

byteAt

byteAt(int i) -> int

Read the byte at an index.

Parameters

i
The index, from 0 to length() - 1.

Returns

The byte as an int from 0 to 255.

Throws

RuntimeException
when i is outside the block.

Examples

Block b = Block::fromString("A");
console.writeln(b.byteAt(0));
try {
    console.writeln(b.byteAt(1));
} catch (RuntimeException e) {
    console.writeln("caught: ${e.message}");
}
65
caught: index 1 out of bounds (length 1)

equals

equals(Block other) -> bool

Compare the bytes of two blocks.

Two blocks are equal when they have the same length and the same bytes. This is different from ==, which asks whether both names refer to the very same block.

Parameters

other
The block to compare with.

Returns

true when the contents are identical.

Examples

Block x = Block::fromString("abcd");
Block y = Block::fromString("abcd");
console.writeln(x.equals(y));
console.writeln(x == y);
console.writeln(x == x);
console.writeln(x.equals(Block::fromString("abc")));
console.writeln(x.equals(Block::fromString("abXd")));
true
false
true
false
false

See also: mismatch

fill

fill(int off, int len, int value) -> void

Set a range of bytes to one value.

Parameters

off
The index of the first byte to set.
len
The number of bytes to set. Zero is allowed and changes nothing.
value
The byte to store, from 0 to 255.

Throws

RuntimeException
when the range off to off + len is not inside the block or value is outside 0..255.

Examples

Block b = Block(6);
b.fill(0, 6, 7);
b.fill(2, 2, 0);
console.writeln("${b.byteAt(0)} ${b.byteAt(2)} ${b.byteAt(5)}");
try {
    b.fill(4, 5, 1);
} catch (RuntimeException e) {
    console.writeln("caught: ${e.message}");
}
7 0 7
caught: index 4 out of bounds (length 6)

int32At

int32At(int i) -> int

Read four bytes as a signed 32-bit integer.

The four bytes starting at i are read in little-endian order, the least significant byte first, and the result is sign-extended, so a pattern with the top bit set gives a negative number.

Parameters

i
The index of the first byte.

Returns

The value as an int, from -2147483648 to 2147483647.

Throws

RuntimeException
when fewer than four bytes remain from i.

Examples

Block b = Block(4);
b.setByte(0, 2);
b.setByte(1, 1);
console.writeln(b.int32At(0));
b.setByte(3, 128);
console.writeln(b.int32At(0));
258
-2147483390

See also: setInt32

int64At

int64At(int i) -> int

Read eight bytes as a signed 64-bit integer.

The eight bytes starting at i are read in little-endian order, the least significant byte first.

Parameters

i
The index of the first byte.

Returns

The value as an int.

Throws

RuntimeException
when fewer than eight bytes remain from i.

Examples

Block b = Block(8);
b.setByte(0, 2);
b.setByte(1, 1);
console.writeln(b.int64At(0));
Block small = Block(6);
try {
    console.writeln(small.int64At(0));
} catch (RuntimeException e) {
    console.writeln("caught: ${e.message}");
}
258
caught: index 0 out of bounds (length 6)

See also: setInt64

length

length() -> int

Return the number of bytes in the block.

For a view made by slice this is the length of the view.

Returns

The length in bytes.

Examples

Block b = Block(8);
console.writeln(b.length());
console.writeln(b.slice(2, 3).length());
console.writeln(Block(0).length());
8
3
0

mismatch

mismatch(Block other, int from) -> int

Find the first byte that differs from another block.

The two blocks must have the same length. Searching starts at index from, which may equal the length, in which case the result is -1.

Parameters

other
The block to compare with, of the same length.
from
The index at which to start searching.

Returns

The smallest index at or after from where the bytes differ, or -1 when they match from there on.

Throws

RuntimeException
when the blocks have different lengths.

Examples

Block x = Block::fromString("abcd");
Block y = Block::fromString("abXd");
console.writeln(x.mismatch(y, 0));
console.writeln(x.mismatch(y, 3));
try {
    console.writeln(x.mismatch(Block::fromString("abc"), 0));
} catch (RuntimeException e) {
    console.writeln("caught: ${e.message}");
}
2
-1
caught: Block.mismatch requires equal lengths

See also: equals

setByte

setByte(int i, int value) -> void

Store one byte at an index.

The value must already be in range. It is not masked, so 256 throws instead of storing 0.

Parameters

i
The index, from 0 to length() - 1.
value
The byte to store, from 0 to 255.

Throws

RuntimeException
when i is outside the block or value is outside 0..255.

Examples

Block b = Block(2);
b.setByte(0, 255);
console.writeln(b.byteAt(0));
try {
    b.setByte(1, 256);
} catch (RuntimeException e) {
    console.writeln("caught: ${e.message}");
}
255
caught: byte value 256 out of range 0..255

setInt32

setInt32(int i, int value) -> void

Write an integer as four bytes.

The bytes are stored in little-endian order, the least significant byte first, starting at i.

Parameters

i
The index of the first byte to write.
value
The value to store as four bytes.

Throws

RuntimeException
when fewer than four bytes remain from i.

Examples

Block b = Block(8);
b.setInt32(4, -2);
console.writeln(b.int32At(4));
console.writeln(b.byteAt(4));
console.writeln(b.byteAt(7));
console.writeln(b.byteAt(0));
-2
254
255
0

See also: int32At

setInt64

setInt64(int i, int value) -> void

Write an integer as eight bytes.

The bytes are stored in little-endian order, the least significant byte first, starting at i.

Parameters

i
The index of the first byte to write.
value
The value to store as eight bytes.

Throws

RuntimeException
when fewer than eight bytes remain from i.

Examples

Block b = Block(8);
b.setInt64(0, 258);
console.writeln(b.byteAt(0));
console.writeln(b.byteAt(1));
b.setInt64(0, -1);
console.writeln(b.int64At(0));
console.writeln(b.byteAt(7));
2
1
-1
255

See also: int64At

slice

slice(int off, int len) -> Block

Return a view onto part of the block.

The view shares storage with the block, so no bytes are copied: a write through the view changes the original and a write to the original shows in the view. Index 0 of the view is index off of the block.

Parameters

off
The index in this block where the view starts.
len
The length of the view in bytes.

Returns

A block of length len that aliases the bytes from off.

Throws

RuntimeException
when the range off to off + len is not inside the block.

Examples

Block b = Block::fromString("abcdef");
Block view = b.slice(2, 2);
console.writeln(view.toString(0, 2));
view.setByte(0, 88);
console.writeln(b.toString(0, 6));
b.setByte(3, 89);
console.writeln(view.toString(0, 2));
cd
abXdef
XY

toString

toString(int off, int len) -> string

Copy a range of bytes into a string.

The bytes are interpreted as UTF-8 text. The string is a copy, so later changes to the block do not affect it.

Parameters

off
The index of the first byte to copy.
len
The number of bytes to copy.

Returns

A string made from the bytes.

Throws

RuntimeException
when the range off to off + len is not inside the block.

Examples

Block b = Block::fromString("hello world");
console.writeln(b.toString(6, 5));
string copy = b.toString(0, 5);
b.setByte(0, 74);
console.writeln(copy);
console.writeln(b.toString(0, 5));
world
hello
Jello

See also

  • Array — An ordered sequence of values of one type, Array<T>, with value semantics.
  • File — An open file.