Menu

Reference / Buffer

ByteBuffer

finalclass
final class ByteBuffer

A growable byte string with a read cursor.

Append bytes at the end with append(); consume them from the front with read(), skip() and friends. Offsets and lengths are in bytes and always relative to the cursor (the first unconsumed byte). The buffer never throws: a read past the end is short, and a lookup that finds nothing answers -1. It is built for incremental parsers, where a partial message is left in place and finished when more bytes arrive.

$b = new Buffer\ByteBuffer();
$b->append("line one\r\nline two");
$end = $b->indexOf("\r\n");
$line = $b->read($end);
$b->skip(2);

This is not Manticore\Ds\ByteBuffer, which is a fixed-size native buffer.

Example

// Buffer\ByteBuffer: append bytes, consume them through a cursor.

use Buffer\ByteBuffer;

$b = new ByteBuffer();
$b->append("GET / HTTP/1.1\r\nHost: x\r\n\r\nBODY");

// indexOf() is relative to the cursor; -1 means "not there (yet)".
$end = $b->indexOf("\r\n\r\n");
echo "head ends at ", $end, "\n";

$head = $b->read($end);        // consume the head
$b->skip(4);                   // and the blank line, without copying it out
echo "head: ", json_encode($head), "\n";
echo "left: ", $b->length(), " bytes, next byte ", $b->byteAt(0), " (", chr($b->byteAt(0)), ")\n";
echo "byteAt past the end: ", $b->byteAt(100), "\n";

// peek() looks, read() consumes; both are short at the end of the buffer.
echo "peek: ", $b->peek(2), "\n";
echo "read: ", $b->read(100), "\n";
echo "empty: ", var_export($b->isEmpty(), true), "\n";

// A capped buffer refuses a write that would cross the cap, and appends nothing.
$c = new ByteBuffer(8);
var_dump($c->append("12345"));
var_dump($c->append("6789"));
echo "view: ", $c->view(), "\n";
echo "readAll: ", $c->readAll(), " then empty=", var_export($c->isEmpty(), true), "\n";

Output

head ends at 23
head: "GET \/ HTTP\/1.1\r\nHost: x"
left: 4 bytes, next byte 66 (B)
byteAt past the end: -1
peek: BO
read: BODY
empty: true
bool(true)
bool(false)
view: 12345
readAll: 12345 then empty=true

See also: Reader, Writer

Properties

bufstringThe raw bytes, including the part already consumed.
posintOffset into {@see $buf} of the first unconsumed byte.
capintCeiling on the number of unconsumed bytes; 0 (the default) means no limit.

buf#source

string $buf = ''

The raw bytes, including the part already consumed. Normally read it through view() or peek() instead; this is the storage the cursor {@see $pos} indexes into.

pos#source

int $pos = 0

Offset into {@see $buf} of the first unconsumed byte. Everything before it has been consumed and is dropped lazily by compact().

cap#source

int $cap = 0

Ceiling on the number of unconsumed bytes; 0 (the default) means no limit. append() refuses a write that would cross it.

Methods

__construct()Create an empty buffer.
length()The number of unconsumed bytes.
isEmpty()Whether there are no unconsumed bytes.
append()Append bytes at the end.
peek()Look at the next bytes without consuming them.
read()Consume and return the next bytes.
readAll()Consume and return everything unconsumed, leaving the buffer empty.
skip()Consume bytes without returning them.
indexOf()Find a byte string in the unconsumed bytes.
byteAt()The value of one unconsumed byte.
view()All unconsumed bytes as a string, without consuming them.
compact()Release memory held by already-consumed bytes.
clear()Discard all bytes, consumed or not, and reset the cursor.

__construct()#source

function __construct(int $cap = 0)

Create an empty buffer.

Parameters

$cap int = 0
limit on unconsumed bytes, or 0 for no limit

length()#source

function length(): int

The number of unconsumed bytes.

Returns

int — the byte count

isEmpty()#source

function isEmpty(): bool

Whether there are no unconsumed bytes.

Returns

bool — true when length() is 0

append()#source

function append(string $s): bool

Append bytes at the end.

When the write would push the number of unconsumed bytes past {@see $cap}, nothing is appended and the answer is false; the caller decides what a full buffer means (a server answers 431 or 413, for instance).

Parameters

$s string
the bytes to add

Returns

bool — true when appended, false when refused for exceeding the cap

peek()#source

function peek(int $n): string

Look at the next bytes without consuming them.

Parameters

$n int
how many bytes; zero or negative gives ''

Returns

string — up to $n bytes, fewer when the buffer holds fewer

read()#source

function read(int $n): string

Consume and return the next bytes.

Parameters

$n int
how many bytes; zero or negative gives '' and consumes nothing

Returns

string — up to $n bytes, fewer when the buffer holds fewer

readAll()#source

function readAll(): string

Consume and return everything unconsumed, leaving the buffer empty.

Returns

string — all unconsumed bytes, possibly ''

skip()#source

function skip(int $n): void

Consume bytes without returning them. Skipping more than the buffer holds stops at the end; zero or negative does nothing.

Parameters

$n int
how many bytes to drop

indexOf()#source

function indexOf(string $needle, int $from = 0): int

Find a byte string in the unconsumed bytes.

Both the answer and $from are relative to the cursor, so a parser that has already scanned a prefix can resume after it instead of rescanning.

Parameters

$needle string
the bytes to look for
$from int = 0
where to start, relative to the cursor; negative counts as 0

Returns

int — offset of the first match relative to the cursor, or -1 when there is none

byteAt()#source

function byteAt(int $i): int

The value of one unconsumed byte.

Parameters

$i int
offset relative to the cursor

Returns

int — the byte, 0 to 255, or -1 when $i is negative or past the end

view()#source

function view(): string

All unconsumed bytes as a string, without consuming them.

Scan it, then tell the buffer how much to consume with skip() or read(); a partial message costs no copy that way.

Returns

string — the unconsumed bytes

compact()#source

function compact(): void

Release memory held by already-consumed bytes.

read() and skip() call this for you. A fully consumed buffer is reset to empty; otherwise the consumed prefix is dropped once it reaches 8192 bytes. Calling it by hand is never required.

clear()#source

function clear(): void

Discard all bytes, consumed or not, and reset the cursor.

View source