APIBytes

Bytes

Immutable byte sequences.

String is valid UTF-8 by contract, so a response body, an image, or a framed record has no String to live in; Bytes holds any bytes, and indexes and slices by byte rather than by character. It also declares Source and Sink, the leaves that buffered readers and writers are built on.

Bytes

type BytesSource

An immutable sequence of bytes. Values come from empty, fromString, fromList, and the operations here; nothing ever writes into one after it is built, so slices share storage safely.

Bytes compare lexicographically by unsigned value, a prefix ordering before what extends it. show gives a quoted form in which printable ASCII passes through, \\, \", \n, \t, and \r are escapes, and every other byte is \xNN in uppercase hex. It is a display form: there is no Bytes literal.

import Bytes

Bytes.fromString "ab" < Bytes.fromString "abc"
Bytes.fromList [255] > Bytes.fromList [1, 2]
show (Bytes.fromList [104, 105, 10, 200]) == "\"hi\\n\\xC8\""

Instances:

Eq Bytes
Ord Bytes
Show Bytes

Sink

type Sink e = { write : Bytes ->{e} () }Source

A consumer of byte chunks whose writes perform e. Writer buffers one. It has no close: the scope that owns the underlying file or socket closes it.

Fields

write

write : Bytes ->{e} ()Source

Writes the whole chunk before returning.

Source

type Source e = { pull : () ->{e} Maybe Bytes }Source

A producer of byte chunks whose reads perform e. Reader buffers one. It has no close: the scope that owns the underlying file or socket closes it.

Fields

pull

pull : () ->{e} Maybe BytesSource

The next available chunk, which may be short, or Nothing at the end of input. An empty chunk is not the end of input.

append

append : Bytes -> Bytes -> BytesSource

The first sequence followed by the second.

import Bytes

Bytes.append (Bytes.fromString "ab") (Bytes.fromString "c") == Bytes.fromString "abc"

byteAt

byteAt : Int -> Bytes -> Maybe IntSource

The byte at a zero-based index, as an Int from 0 to 255, or Nothing when the index is out of range.

import Bytes

Bytes.byteAt 0 (Bytes.fromString "A") == Just 65
Bytes.byteAt 1 (Bytes.fromString "A") == Nothing

concat

concat : List Bytes -> BytesSource

The sequences joined in list order, allocated once for the whole result.

import Bytes

Bytes.concat [Bytes.fromString "a", Bytes.empty, Bytes.fromString "bc"] == Bytes.fromString "abc"

empty

empty : BytesSource

The empty sequence.

fromList

fromList : List Int -> BytesSource

The bytes of a list of integers, each truncated to its low eight bits. It is meant for fixtures and binary framing rather than bulk data, since a List Int takes eight bytes per byte.

import Bytes

Bytes.toList (Bytes.fromList [256, -1, 65]) == [0, 255, 65]

fromString

fromString : String -> BytesSource

The UTF-8 encoding of the text.

import Bytes

Bytes.toList (Bytes.fromString "é") == [195, 169]

indexOf

indexOf : Bytes -> Bytes -> Maybe IntSource

The first index at which needle occurs, or Nothing. The empty needle occurs at index 0 of every sequence. The scan runs natively and allocates nothing per match.

import Bytes

Bytes.indexOf (Bytes.fromString "c") (Bytes.fromString "abcabc") == Just 2
Bytes.indexOf (Bytes.fromString "x") (Bytes.fromString "abc") == Nothing
Bytes.indexOf Bytes.empty Bytes.empty == Just 0

indexOfFrom

indexOfFrom : Int -> Bytes -> Bytes -> Maybe IntSource

indexOf beginning at a clamped index start, where the empty needle occurs. Resuming where an earlier search stopped keeps repeated searches over a growing buffer linear in its size.

import Bytes

Bytes.indexOfFrom 3 (Bytes.fromString "c") (Bytes.fromString "abcabc") == Just 5

length

length : Bytes -> IntSource

The number of bytes.

import Bytes

Bytes.length (Bytes.fromString "naïve") == 6

slice

slice : Int -> Int -> Bytes -> BytesSource

The bytes from index start up to but not including end. Indices are clamped to the sequence, and the result is empty when end is not greater than start. A slice shares the storage it was cut from, so it costs the same however large the original is.

import Bytes

Bytes.slice 1 3 (Bytes.fromString "abcd") == Bytes.fromString "bc"
Bytes.slice (-5) 2 (Bytes.fromString "abcd") == Bytes.fromString "ab"
Bytes.slice 3 1 (Bytes.fromString "abcd") == Bytes.empty

startsWith

startsWith : Bytes -> Bytes -> BoolSource

Whether bytes begins with prefix. The empty prefix begins everything.

import Bytes

Bytes.startsWith (Bytes.fromString "ab") (Bytes.fromString "abc")

toList

toList : Bytes -> List IntSource

The bytes as integers from 0 to 255, in order.

import Bytes

Bytes.toList (Bytes.fromString "hi") == [104, 105]

toString

toString : Bytes -> Maybe StringSource

The text these bytes encode, or Nothing when they are not valid UTF-8, so input that is not text is never silently rewritten.

import Bytes

Bytes.toString (Bytes.fromList [104, 105]) == Just "hi"
Bytes.toString (Bytes.fromList [255]) == Nothing

toStringLossy

toStringLossy : Bytes -> StringSource

The text these bytes encode, with U+FFFD in place of each malformed byte. It is meant for reporting what was received, not for conversion.

import Bytes

Bytes.toStringLossy (Bytes.fromList [104, 255, 105]) == "h�i"