The Lean Language Reference

19.17.Β Byte ArraysπŸ”—

Byte arrays are a specialized array type that can only contain elements of type UInt8. Due to this restriction, they can use a much more efficient representation, with no pointer indirections. Like other arrays, byte arrays are represented in compiled code as dynamic arrays, and the Lean runtime specially optimizes array operations. The operations that modify byte arrays first check the array's reference count, and if there are no other references to the array, it is modified in place.

There is no literal syntax for byte arrays. List.toByteArray can be used to construct an array from a list literal.

πŸ”—structure
ByteArray : Type
ByteArray : Type

ByteArray is like Array UInt8, but with an efficient run-time representation as a packed byte buffer.

Constructor

ByteArray.mk

Packs an array of bytes into a ByteArray.

Converting between Array and ByteArray takes linear time.

Fields

data : Array UInt8

The data contained in the byte array.

Converting between Array and ByteArray takes linear time.

19.17.1.Β API Reference

19.17.1.1.Β Constructing Byte Arrays

πŸ”—def
ByteArray.empty : ByteArray
ByteArray.empty : ByteArray

Constructs a new empty byte array with initial capacity 0.

Use ByteArray.emptyWithCapacity to create an array with a greater initial capacity.

πŸ”—def
ByteArray.emptyWithCapacity (c : Nat) : ByteArray
ByteArray.emptyWithCapacity (c : Nat) : ByteArray

Constructs a new empty byte array with initial capacity c.

πŸ”—def
ByteArray.append (a b : ByteArray) : ByteArray
ByteArray.append (a b : ByteArray) : ByteArray

Appends two byte arrays.

In compiled code, calls to ByteArray.append are replaced with the much more efficient ByteArray.fastAppend.

πŸ”—def
ByteArray.fastAppend (a b : ByteArray) : ByteArray
ByteArray.fastAppend (a b : ByteArray) : ByteArray
πŸ”—def
ByteArray.copySlice (src : ByteArray) (srcOff : Nat) (dest : ByteArray) (destOff len : Nat) (exact : Bool := true) : ByteArray
ByteArray.copySlice (src : ByteArray) (srcOff : Nat) (dest : ByteArray) (destOff len : Nat) (exact : Bool := true) : ByteArray

Copies the slice at [srcOff, srcOff + len) in src to [destOff, destOff + len) in dest, growing dest if necessary. If exact is false, the capacity will be doubled when grown.

19.17.1.2.Β Size

πŸ”—def
ByteArray.size : ByteArray β†’ Nat
ByteArray.size : ByteArray β†’ Nat

Returns the number of bytes in the byte array.

This is the number of bytes actually in the array, as distinct from its capacity, which is the amount of memory presently allocated for the array.

πŸ”—def
ByteArray.usize (a : ByteArray) : USize
ByteArray.usize (a : ByteArray) : USize

Retrieves the size of the array as a platform-specific fixed-width integer.

Because USize is big enough to address all memory on every platform that Lean supports, there are in practice no ByteArrays that have more elements that USize can count.

πŸ”—def
ByteArray.isEmpty (s : ByteArray) : Bool
ByteArray.isEmpty (s : ByteArray) : Bool

Returns true when s contains zero bytes.

19.17.1.3.Β Lookups

πŸ”—def
ByteArray.get (a : ByteArray) (i : Nat) (h : i < a.size := by get_elem_tactic) : UInt8
ByteArray.get (a : ByteArray) (i : Nat) (h : i < a.size := by get_elem_tactic) : UInt8

Retrieves the byte at the indicated index. Callers must prove that the index is in bounds.

Use uget for a more efficient alternative or get! for a variant that panics if the index is out of bounds.

πŸ”—def
ByteArray.uget (a : ByteArray) (i : USize) (h : i.toNat < a.size := by get_elem_tactic) : UInt8
ByteArray.uget (a : ByteArray) (i : USize) (h : i.toNat < a.size := by get_elem_tactic) : UInt8

Retrieves the byte at the indicated index. Callers must prove that the index is in bounds. The index is represented by a platform-specific fixed-width integer (either 32 or 64 bits).

Because USize is big enough to address all memory on every platform that Lean supports, there are in practice no ByteArrays for which uget cannot retrieve all elements.

πŸ”—def
ByteArray.get! : ByteArray β†’ Nat β†’ UInt8
ByteArray.get! : ByteArray β†’ Nat β†’ UInt8

Retrieves the byte at the indicated index. Panics if the index is out of bounds.

πŸ”—def
ByteArray.extract (a : ByteArray) (b e : Nat) : ByteArray
ByteArray.extract (a : ByteArray) (b e : Nat) : ByteArray

Copies the bytes with indices b (inclusive) to e (exclusive) to a new ByteArray.

19.17.1.4.Β Conversions

πŸ”—def
ByteArray.toList (bs : ByteArray) : List UInt8
ByteArray.toList (bs : ByteArray) : List UInt8

Converts a packed array of bytes to a linked list.

πŸ”—def
ByteArray.toUInt64BE! (bs : ByteArray) : UInt64
ByteArray.toUInt64BE! (bs : ByteArray) : UInt64

Interprets a ByteArray of size 8 as a big-endian UInt64.

Panics if the array's size is not 8.

πŸ”—def
ByteArray.toUInt64LE! (bs : ByteArray) : UInt64
ByteArray.toUInt64LE! (bs : ByteArray) : UInt64

Interprets a ByteArray of size 8 as a little-endian UInt64.

Panics if the array's size is not 8.

19.17.1.4.1.Β UTF-8

πŸ”—def
ByteArray.utf8Decode? (b : ByteArray) : Option (Array Char)
ByteArray.utf8Decode? (b : ByteArray) : Option (Array Char)

Decodes a sequence of characters from their UTF-8 representation. Returns none if the bytes are not a sequence of Unicode scalar values.

πŸ”—def
ByteArray.utf8DecodeChar? (bytes : ByteArray) (i : Nat) : Option Char
ByteArray.utf8DecodeChar? (bytes : ByteArray) (i : Nat) : Option Char

Decodes and returns the Char whose UTF-8 encoding begins at i in bytes.

Returns none if i is not the start of a valid UTF-8 encoding of a character.

πŸ”—def
ByteArray.utf8DecodeChar (bytes : ByteArray) (i : Nat) (h : (bytes.utf8DecodeChar? i).isSome = true) : Char
ByteArray.utf8DecodeChar (bytes : ByteArray) (i : Nat) (h : (bytes.utf8DecodeChar? i).isSome = true) : Char

Decodes and returns the Char whose UTF-8 encoding begins at i in bytes.

This function requires a proof that there is, in fact, a valid Char at i. utf8DecodeChar? is an alternative function that returns Option Char instead of requiring a proof ahead of time.

19.17.1.5.Β Modification

πŸ”—def
ByteArray.push : ByteArray β†’ UInt8 β†’ ByteArray
ByteArray.push : ByteArray β†’ UInt8 β†’ ByteArray

Adds an element to the end of an array. The resulting array's size is one greater than the input array. If there are no other references to the array, then it is modified in-place.

This takes amortized O(1) time because ByteArray is represented by a dynamic array.

πŸ”—def
ByteArray.set (a : ByteArray) (i : Nat) : UInt8 β†’ (h : autoParam (i < a.size) _auto✝) β†’ ByteArray
ByteArray.set (a : ByteArray) (i : Nat) : UInt8 β†’ (h : autoParam (i < a.size) _auto✝) β†’ ByteArray

Replaces the byte at the given index.

No bounds check is performed, but the function requires a proof that the index is in bounds. This proof can usually be omitted, and will be synthesized automatically.

The array is modified in-place if there are no other references to it.

πŸ”—def
ByteArray.uset (a : ByteArray) (i : USize) : UInt8 β†’ (h : autoParam (i.toNat < a.size) _auto✝) β†’ ByteArray
ByteArray.uset (a : ByteArray) (i : USize) : UInt8 β†’ (h : autoParam (i.toNat < a.size) _auto✝) β†’ ByteArray

Replaces the byte at the given index.

No bounds check is performed, but the function requires a proof that the index is in bounds. This proof can usually be omitted, and will be synthesized automatically.

The array is modified in-place if there are no other references to it.

πŸ”—def
ByteArray.set! : ByteArray β†’ Nat β†’ UInt8 β†’ ByteArray
ByteArray.set! : ByteArray β†’ Nat β†’ UInt8 β†’ ByteArray

Replaces the byte at the given index.

The array is modified in-place if there are no other references to it.

If the index is out of bounds, the array is returned unmodified.

19.17.1.6.Β Iteration

πŸ”—def
ByteArray.foldl.{v} {Ξ² : Type v} (f : Ξ² β†’ UInt8 β†’ Ξ²) (init : Ξ²) (as : ByteArray) (start : Nat := 0) (stop : Nat := as.size) : Ξ²
ByteArray.foldl.{v} {Ξ² : Type v} (f : Ξ² β†’ UInt8 β†’ Ξ²) (init : Ξ²) (as : ByteArray) (start : Nat := 0) (stop : Nat := as.size) : Ξ²

A left fold on ByteArray that iterates over an array from low to high indices, computing a running value.

Each element of the array is combined with the value from the prior elements using a function f. The initial value init is the starting value before any elements have been processed.

ByteArray.foldlM is a monadic variant of this function.

πŸ”—def
ByteArray.foldlM.{v, w} {Ξ² : Type v} {m : Type v β†’ Type w} [Monad m] (f : Ξ² β†’ UInt8 β†’ m Ξ²) (init : Ξ²) (as : ByteArray) (start : Nat := 0) (stop : Nat := as.size) : m Ξ²
ByteArray.foldlM.{v, w} {Ξ² : Type v} {m : Type v β†’ Type w} [Monad m] (f : Ξ² β†’ UInt8 β†’ m Ξ²) (init : Ξ²) (as : ByteArray) (start : Nat := 0) (stop : Nat := as.size) : m Ξ²

A monadic left fold on ByteArray that iterates over an array from low to high indices, computing a running value.

Each element of the array is combined with the value from the prior elements using a monadic function f. The initial value init is the starting value before any elements have been processed.

πŸ”—def
ByteArray.forIn.{v, w} {Ξ² : Type v} {m : Type v β†’ Type w} [Monad m] (as : ByteArray) (b : Ξ²) (f : UInt8 β†’ Ξ² β†’ m (ForInStep Ξ²)) : m Ξ²
ByteArray.forIn.{v, w} {Ξ² : Type v} {m : Type v β†’ Type w} [Monad m] (as : ByteArray) (b : Ξ²) (f : UInt8 β†’ Ξ² β†’ m (ForInStep Ξ²)) : m Ξ²

The reference implementation of ForIn.forIn for ByteArray.

In compiled code, this is replaced by the more efficient ByteArray.forInUnsafe.

19.17.1.7.Β Iterators

πŸ”—def
ByteArray.iter (arr : ByteArray) : ByteArray.Iterator
ByteArray.iter (arr : ByteArray) : ByteArray.Iterator

Creates an iterator at the beginning of an array.

πŸ”—structure
ByteArray.Iterator : Type
ByteArray.Iterator : Type

Iterator over the bytes (UInt8) of a ByteArray.

Typically created by arr.iter, where arr is a ByteArray.

An iterator is valid if the position i is valid for the array arr, meaning 0 ≀ i ≀ arr.size

Most operations on iterators return arbitrary values if the iterator is not valid. The functions in the ByteArray.Iterator API should rule out the creation of invalid iterators, with two exceptions:

  • Iterator.next iter is invalid if iter is already at the end of the array (iter.atEnd is true)

  • Iterator.forward iter n/Iterator.nextn iter n is invalid if n is strictly greater than the number of remaining bytes.

Constructor

ByteArray.Iterator.mk

Fields

array : ByteArray

The array the iterator is for.

idx : Nat

The current position.

This position is not necessarily valid for the array, for instance if one keeps calling Iterator.next when Iterator.atEnd is true. If the position is not valid, then the current byte is (default : UInt8).

πŸ”—def
ByteArray.Iterator.pos (self : ByteArray.Iterator) : Nat
ByteArray.Iterator.pos (self : ByteArray.Iterator) : Nat

The current position.

This position is not necessarily valid for the array, for instance if one keeps calling Iterator.next when Iterator.atEnd is true. If the position is not valid, then the current byte is (default : UInt8).

πŸ”—def
ByteArray.Iterator.atEnd : ByteArray.Iterator β†’ Bool
ByteArray.Iterator.atEnd : ByteArray.Iterator β†’ Bool

True if the iterator is past the array's last byte.

πŸ”—def
ByteArray.Iterator.hasNext : ByteArray.Iterator β†’ Bool
ByteArray.Iterator.hasNext : ByteArray.Iterator β†’ Bool

True if the iterator is valid; that is, it is not past the array's last byte.

πŸ”—def
ByteArray.Iterator.hasPrev : ByteArray.Iterator β†’ Bool
ByteArray.Iterator.hasPrev : ByteArray.Iterator β†’ Bool

True if the position is not zero.

πŸ”—def
ByteArray.Iterator.curr : ByteArray.Iterator β†’ UInt8
ByteArray.Iterator.curr : ByteArray.Iterator β†’ UInt8

The byte at the current position.

On an invalid position, returns (default : UInt8).

πŸ”—def
ByteArray.Iterator.curr' (it : ByteArray.Iterator) (h : it.hasNext = true) : UInt8
ByteArray.Iterator.curr' (it : ByteArray.Iterator) (h : it.hasNext = true) : UInt8

The byte at the current position. -

πŸ”—def
ByteArray.Iterator.next : ByteArray.Iterator β†’ ByteArray.Iterator
ByteArray.Iterator.next : ByteArray.Iterator β†’ ByteArray.Iterator

Moves the iterator's position forward by one byte, unconditionally.

It is only valid to call this function if the iterator is not at the end of the array, i.e. Iterator.atEnd is false; otherwise, the resulting iterator will be invalid.

πŸ”—def
ByteArray.Iterator.next' (it : ByteArray.Iterator) (_h : it.hasNext = true) : ByteArray.Iterator
ByteArray.Iterator.next' (it : ByteArray.Iterator) (_h : it.hasNext = true) : ByteArray.Iterator

Moves the iterator's position forward by one byte. -

πŸ”—def
ByteArray.Iterator.forward : ByteArray.Iterator β†’ Nat β†’ ByteArray.Iterator
ByteArray.Iterator.forward : ByteArray.Iterator β†’ Nat β†’ ByteArray.Iterator

Moves the iterator's position several bytes forward.

The resulting iterator is only valid if the number of bytes to skip is less than or equal to the number of bytes left in the iterator.

πŸ”—def
ByteArray.Iterator.nextn : ByteArray.Iterator β†’ Nat β†’ ByteArray.Iterator
ByteArray.Iterator.nextn : ByteArray.Iterator β†’ Nat β†’ ByteArray.Iterator

Moves the iterator's position several bytes forward.

The resulting iterator is only valid if the number of bytes to skip is less than or equal to the number of bytes left in the iterator.

πŸ”—def
ByteArray.Iterator.prev : ByteArray.Iterator β†’ ByteArray.Iterator
ByteArray.Iterator.prev : ByteArray.Iterator β†’ ByteArray.Iterator

Decreases the iterator's position.

If the position is zero, this function is the identity.

πŸ”—def
ByteArray.Iterator.prevn : ByteArray.Iterator β†’ Nat β†’ ByteArray.Iterator
ByteArray.Iterator.prevn : ByteArray.Iterator β†’ Nat β†’ ByteArray.Iterator

Moves the iterator's position several bytes back.

If asked to go back more bytes than available, stops at the beginning of the array.

πŸ”—def
ByteArray.Iterator.remainingBytes : ByteArray.Iterator β†’ Nat
ByteArray.Iterator.remainingBytes : ByteArray.Iterator β†’ Nat

The number of bytes remaining in the iterator.

πŸ”—def
ByteArray.Iterator.toEnd : ByteArray.Iterator β†’ ByteArray.Iterator
ByteArray.Iterator.toEnd : ByteArray.Iterator β†’ ByteArray.Iterator

Moves the iterator's position to the end of the array.

Given i : ByteArray.Iterator, note that i.toEnd.atEnd is always true.

19.17.1.8.Β Slices

πŸ”—def
ByteArray.toByteSlice (as : ByteArray) (start : Nat := 0) (stop : Nat := as.size) : ByteSlice
ByteArray.toByteSlice (as : ByteArray) (start : Nat := 0) (stop : Nat := as.size) : ByteSlice

Returns a byte slice of a byte array, with the given bounds.

If start or stop are not valid bounds for a byte slice, then they are clamped to byte array's size. Additionally, the starting index is clamped to the ending index.

πŸ”—def
ByteSlice : Type
ByteSlice : Type

A region of some underlying byte array.

A byte slice contains a byte array together with the start and end indices of a region of interest. Byte slices can be used to avoid copying or allocating space, while being more convenient than tracking the bounds by hand. The region of interest consists of every index that is both greater than or equal to start and strictly less than stop.

πŸ”—def
ByteSlice.beq (a b : ByteSlice) : Bool
ByteSlice.beq (a b : ByteSlice) : Bool

Comparison function

πŸ”—def
ByteSlice.byteArray (xs : ByteSlice) : ByteArray
ByteSlice.byteArray (xs : ByteSlice) : ByteArray

The underlying byte array.

πŸ”—def
ByteSlice.contains (s : ByteSlice) (byte : UInt8) : Bool
ByteSlice.contains (s : ByteSlice) (byte : UInt8) : Bool

Checks if the byte slice contains a specific byte value.

Returns true if any byte in the slice equals the given value, false otherwise.

πŸ”—def
ByteSlice.empty : ByteSlice
ByteSlice.empty : ByteSlice

The empty byte slice.

This empty byte slice is backed by an empty byte array.

πŸ”—def
ByteSlice.foldr.{v} {Ξ² : Type v} (f : UInt8 β†’ Ξ² β†’ Ξ²) (init : Ξ²) (as : ByteSlice) : Ξ²
ByteSlice.foldr.{v} {Ξ² : Type v} (f : UInt8 β†’ Ξ² β†’ Ξ²) (init : Ξ²) (as : ByteSlice) : Ξ²

Folds an operation from right to left over the bytes in a byte slice.

An accumulator of type Ξ² is constructed by starting with init and combining each byte of the byte slice with the current accumulator value in turn, moving from the end to the start.

Examples:

πŸ”—def
ByteSlice.foldrM.{v, w} {Ξ² : Type v} {m : Type v β†’ Type w} [Monad m] (f : UInt8 β†’ Ξ² β†’ m Ξ²) (init : Ξ²) (as : ByteSlice) : m Ξ²
ByteSlice.foldrM.{v, w} {Ξ² : Type v} {m : Type v β†’ Type w} [Monad m] (f : UInt8 β†’ Ξ² β†’ m Ξ²) (init : Ξ²) (as : ByteSlice) : m Ξ²

Folds a monadic operation from right to left over the bytes in a byte slice.

An accumulator of type Ξ² is constructed by starting with init and monadically combining each byte of the byte slice with the current accumulator value in turn, moving from the end to the start. The monad in question may permit early termination or repetition.

Examples:

#eval (ByteArray.mk #[1, 2, 3]).toByteSlice.foldrM (init := 0) fun x acc =>
  some x.toNat + acc
some 6
πŸ”—def
ByteSlice.forM.{v, w} {m : Type v β†’ Type w} [Monad m] (f : UInt8 β†’ m PUnit) (as : ByteSlice) : m PUnit
ByteSlice.forM.{v, w} {m : Type v β†’ Type w} [Monad m] (f : UInt8 β†’ m PUnit) (as : ByteSlice) : m PUnit

Runs a monadic action on each byte of a byte slice.

The bytes are processed starting at the lowest index and moving up.

πŸ”—def
ByteSlice.get (s : ByteSlice) (i : Fin s.size) : UInt8
ByteSlice.get (s : ByteSlice) (i : Fin s.size) : UInt8

Extracts a byte from the byte slice.

The index is relative to the start of the byte slice, rather than the underlying byte array.

πŸ”—def
ByteSlice.get! (s : ByteSlice) (i : Nat) : UInt8
ByteSlice.get! (s : ByteSlice) (i : Nat) : UInt8

Extracts a byte from the byte slice, or returns a default value when the index is out of bounds.

The index is relative to the start and end of the byte slice, rather than the underlying byte array. The default value is 0.

πŸ”—def
ByteSlice.getD (s : ByteSlice) (i : Nat) (vβ‚€ : UInt8) : UInt8
ByteSlice.getD (s : ByteSlice) (i : Nat) (vβ‚€ : UInt8) : UInt8

Extracts a byte from the byte slice, or returns a default value vβ‚€ when the index is out of bounds.

The index is relative to the start and end of the byte slice, rather than the underlying byte array.

πŸ”—def
ByteSlice.ofByteArray (ba : ByteArray) : ByteSlice
ByteSlice.ofByteArray (ba : ByteArray) : ByteSlice

Creates a new ByteSlice of a ByteArray

πŸ”—def
ByteSlice.size (s : ByteSlice) : Nat
ByteSlice.size (s : ByteSlice) : Nat

Computes the size of the byte slice.

πŸ”—def
ByteSlice.slice (s : ByteSlice) (start : Nat := 0) (stop : Nat := s.size) : ByteSlice
ByteSlice.slice (s : ByteSlice) (start : Nat := 0) (stop : Nat := s.size) : ByteSlice

Creates a sub-slice of the byte slice with the given bounds.

If start or stop are not valid bounds for a sub-slice, then they are clamped to the slice's size. Additionally, the starting index is clamped to the ending index.

The indices are relative to the current slice, not the underlying byte array.

πŸ”—def
ByteSlice.start (xs : ByteSlice) : Nat
ByteSlice.start (xs : ByteSlice) : Nat

The starting index of the region of interest (inclusive).

πŸ”—def
ByteSlice.stop (xs : ByteSlice) : Nat
ByteSlice.stop (xs : ByteSlice) : Nat

The ending index of the region of interest (exclusive).

πŸ”—def
ByteSlice.toByteArray (s : ByteSlice) : ByteArray
ByteSlice.toByteArray (s : ByteSlice) : ByteArray

Converts a byte slice back to a byte array by copying the relevant portion.

19.17.1.9.Β Element Predicates

πŸ”—def
ByteArray.findIdx? (a : ByteArray) (p : UInt8 β†’ Bool) (start : Nat := 0) : Option Nat
ByteArray.findIdx? (a : ByteArray) (p : UInt8 β†’ Bool) (start : Nat := 0) : Option Nat

Finds the index of the first byte in a for which p returns true. If no byte in a satisfies p, then the result is none.

The variant findFinIdx? additionally returns a proof that the found index is in bounds.

πŸ”—def
ByteArray.findFinIdx? (a : ByteArray) (p : UInt8 β†’ Bool) (start : Nat := 0) : Option (Fin a.size)
ByteArray.findFinIdx? (a : ByteArray) (p : UInt8 β†’ Bool) (start : Nat := 0) : Option (Fin a.size)

Finds the index of the first byte in a for which p returns true. If no byte in a satisfies p, then the result is none.

The index is returned along with a proof that it is a valid index in the array.