std.bytes.ByteArray
type pub ByteArrayAn array of bytes.
Byte arrays are arrays specialised for storing individual bytes in the most
efficient way possible. Unlike a regular Array of Int values, each value
only requires a single byte of space, instead of requiring 8 bytes of space.
Byte arrays are primarily meant for reading and writing data from/to a stream,
such as a file or a socket. If you simply want to store a list of numbers,
you're better off using the Array type.
Static methods
filled
Show source codeHide source code
fn pub static filled(with: Int, times: Int) -> ByteArray {
let bytes = with_capacity(times)
alloc.write(byte: with, to: bytes.buffer, size: times)
bytes.size = times
bytes
}fn pub static filled(with: Int, times: Int) -> ByteArrayReturns a ByteArray filled with the given byte.
The times argument specifies how many times the with argument must
exist in the byte array.
Panics
This method panics of times if less than zero.
Examples
let bytes = ByteArray.filled(with: 0, times: 2)
bytes.get(0) # => 0
bytes.get(1) # => 0
from_array
Show source codeHide source code
fn pub static from_array(array: ref Array[Int]) -> ByteArray {
let bytes = ByteArray.with_capacity(array.size)
for v in array.iter { bytes.push(v) }
bytes
}fn pub static from_array(array: ref Array[Int]) -> ByteArrayReturns a new ByteArray created from the given Array.
from_pointer
Show source codeHide source code
fn pub static from_pointer(pointer: Pointer[UInt8], size: Int) -> ByteArray {
if size < 0 { invalid_size(size) }
# While it's unlikely for `pointer` to be NULL while `size` is e.g. 4, it
# doesn't hurt to check as the alternative is a hard crash.
if pointer as Int == 0 { return new }
let bytes = with_capacity(size)
alloc.copy(pointer, bytes.buffer, size)
bytes.size = size
bytes
}fn pub static from_pointer(pointer: Pointer[UInt8], size: Int) -> ByteArrayReturns a ByteArray created from a raw pointer.
The size argument specifies the number of bytes to read starting at the
given pointer.
If size is greater than zero but pointer is NULL, the returned value is
an empty ByteArray.
Safety
If size is greater than zero and pointer points to invalid memory (but
isn't NULL), the behavior is undefined and will likely result in a crash.
The purpose of this method is to allow creating a ByteArray from a pointer
returned by C code. Avoid using this method for anything else.
Panics
This method panics of size if less than zero.
new
Show source codeHide source code
fn pub static new -> ByteArray {
Self(size: 0, capacity: 0, buffer: 0 as Pointer[UInt8])
}fn pub static new -> ByteArrayReturns a new empty ByteArray.
Examples
let bytes = ByteArray.new
bytes.size # => 0
bytes.capacity # => 0
with_capacity
Show source codeHide source code
fn pub static with_capacity(size: Int) -> ByteArray {
if size < 0 { invalid_capacity(size) }
Self(
size: 0,
capacity: size,
buffer: alloc.resize(0 as Pointer[UInt8], size),
)
}fn pub static with_capacity(size: Int) -> ByteArrayReturns a ByteArray with enough space for at least size bytes.
Panics
This method panics of size if less than zero.
Examples
ByteArray.with_capacity(32).capacity # => 32
Instance methods
!=
Show source codeHide source code
fn pub !=(other: ref Self) -> Bool {
!(self == other)
}fn pub !=(other: ref Self) -> BoolReturns true if self and the given object are not equal to each other.
==
Show source codeHide source code
fn pub ==(other: ref ByteArray) -> Bool {
let size = self.size
if size == other.size {
ptr.equal(pointer, other.pointer, size: size)
} else {
false
}
}fn pub ==(other: ref ByteArray) -> BoolReturns true if two ByteArray objects are equal to each other.
Two ByteArray objects are considered equal if they have the exact same
values in the exact same order.
Examples
Comparing two ByteArray objects:
ByteArray.from_array([10]) == ByteArray.from_array([10]) # => true
ByteArray.from_array([10]) == ByteArray.from_array([20]) # => false
append
Show source codeHide source code
fn pub mut append[T: Bytes](bytes: ref T) {
let len = bytes.size
if len == 0 { return }
reserve_exact(len)
alloc.copy(from: bytes.pointer, to: tail, size: len)
@size += len
}fn pub mut append[T: Bytes](bytes: ref T)Copies the bytes from bytes to the end of self.
The bytes argument can be any type that implements the Bytes trait, such
as String, ByteArray, or Slice.
Examples
let a = ByteArray.from_array([1, 2, 3, 4])
let b = ByteArray.new
let c = ByteArray.new
b.append(a)
c.append(a.slice(start: 0, end: 2))
b # => ByteArray.from_array([1, 2, 3, 4])
c # => ByteArray.from_array([1, 2])
bytes
Show source codeHide source code
fn pub bytes -> Iter[ref Self] {
Iter(source: self, index: 0)
}fn pub bytes -> Iter[ref Self]Returns an iterator over the bytes in self.
Examples
let bytes = ByteArray.from_array([10, 20, 30])
let iter = bytes.bytes
iter.next # => Option.Some(10)
capacity
Show source codeHide source code
fn pub inline capacity -> Int {
@capacity
}fn pub inline capacity -> IntReturns the number of bytes that can be stored in self before self needs
to be resized.
Examples
ByteArray.new.capacity # => 0
ByteArray.with_capacity(32).capacity # => 32
clear
Show source codeHide source code
fn pub mut clear {
@size = 0
}fn pub mut clearRemoves all values from this ByteArray.
This method has no effect on the amount of memory reserved by self,
instead it merely sets the size of self to zero.
Examples
Removing all values:
let bytes = ByteArray.from_array([10, 20, 30])
bytes.clear
bytes.size # => 0
clone
Show source codeHide source code
fn pub clone -> ByteArray {
let new = ByteArray.new
let _ = new.append(self)
new
}fn pub clone -> ByteArrayCreates a clone of self.
The returned value is an owned value that is the same type as the receiver
of this method. For example, cloning a ref Array[Int] results in a
Array[Int], not another ref Array[Int].
contains?
Show source codeHide source code
fn pub contains?(value: Int) -> Bool {
Slice.new(self, 0, size).contains?(value)
}fn pub contains?(value: Int) -> BoolReturns true if the given byte is contained in self.
Examples
let bytes = ByteArray.from_array([10, 20])
bytes.contains?(10) # => true
contains_bytes?
Show source codeHide source code
fn pub contains_bytes?[B: Bytes](bytes: ref B) -> Bool {
index_of(bytes, starting_at: 0).some?
}fn pub contains_bytes?[B: Bytes](bytes: ref B) -> BoolReturns true if self contains the given byte sequence.
Examples
'hello'.contains_bytes?('hello') # => true
drain_to_string
Show source codeHide source code
fn pub mut drain_to_string -> String {
if empty? { return '' }
let str = String.from_primitive(inko_string_from_bytes(@buffer, @size))
clear
str
}fn pub mut drain_to_string -> StringReturns a new String using the bytes in this ByteArray, draining it in
the process.
After this method is finished, self is empty.
Examples
let bytes = ByteArray.from_array([105, 110, 107, 111])
bytes.drain_to_string # => 'inko'
bytes.empty? # => true
empty?
Show source codeHide source code
fn pub empty? -> Bool {
size == 0
}fn pub empty? -> BoolReturns true if self is empty.
Examples
''.empty? # => true
'hello'.empty? # => false
ends_with?
Show source codeHide source code
fn pub ends_with?[T: Bytes](suffix: ref T) -> Bool {
ptr.ends_with?(pointer, size, suffix.pointer, suffix.size)
}fn pub ends_with?[T: Bytes](suffix: ref T) -> BoolReturns true if self ends with the given sequence of bytes.
Examples
'hello'.ends_with?('o'.to_byte_array) # => true
'hello'.ends_with?('o') # => true
equals?
Show source codeHide source code
fn pub equals?[O: Bytes](other: ref O) -> Bool {
let len = size
len == other.size and (len == 0 or ptr.equal(pointer, other.pointer, len))
}fn pub equals?[O: Bytes](other: ref O) -> BoolReturns true if self and other contain identical bytes.
This allows comparing of any Bytes type with any other Bytes type (e.g.
comparing a String to a ByteArray). In contrast, methods such as
String.== only support comparing against values of the same type.
Examples
'hello'.equals?('hello') # => true
'hello'.equals?('hello'.to_byte_array) # => true
equals_while_ignoring_case?
Show source codeHide source code
fn pub equals_while_ignoring_case?[O: Bytes](other: ref O) -> Bool {
let len = size
if len == 0 { return true }
if len != other.size { return false }
let lhs = pointer
let rhs = other.pointer
let mut idx = 0
# Since we have to convert ASCII bytes to lowercase (when necessary) we
# can't use a more efficient comparison and instead have to compare the data
# byte by byte.
while idx < len {
let mut a = ptr.add(lhs, idx).0 as Int
let mut b = ptr.add(rhs, idx).0 as Int
if upper?(a) { a = to_lower(a) }
if upper?(b) { b = to_lower(b) }
if a != b { return false }
idx += 1
}
true
}fn pub equals_while_ignoring_case?[O: Bytes](other: ref O) -> BoolReturns true if self and other contain identical bytes, using an ASCII
case-insensitive comparison.
This method only performs case-insensitive matching for characters in the ASCII range, meaning that "foo" and "FOO" are considered identical but "á" and "Á" are considered different.
Examples
'foo'.equals_while_ignoring_case?('foo') # => true
'foo'.equals_while_ignoring_case?('FOO') # => true
'abc'.equals_while_ignoring_case?('def') # => false
'á'.equals_while_ignoring_case?('Á') # => false
flush
Show source codeHide source code
fn pub mut flush -> Result[Nil, IoError] {
Result.Ok(nil)
}fn pub mut flush -> Result[Nil, Error]Flushes any pending writes to the file system.
Flushing writes is a potentially expensive operation, and unnecessarily calling this method may degrade performance.
When flushing data to disk it's important to remember that the actual behaviour may vary based on the type of file system, operating system and storage hardware that's used. In particular, it's possible for one of these components to say "Yup, I totally flushed the data, you're all good!" when in fact they have not fully flushed the data.
fmt
Show source codeHide source code
fn pub fmt(formatter: mut Formatter) {
let fmt = formatter.array
for byte in iter { fmt.value(byte) }
fmt.finish
}fn pub fmt(formatter: mut Formatter)Formats self in a human-readable format for debugging purposes.
get
Show source codeHide source code
fn pub inline get(index: Int) -> Result[Int, OutOfBounds] {
try check_bounds(index, size)
Result.Ok(get_unchecked(index))
}fn pub inline get(index: Int) -> Result[Int, OutOfBounds]Returns the byte at the given index.
If the index is out of bounds, a std.bounds.OutOfBounds error is returned.
Examples
let bytes = ByteArray.from_array([10, 20])
bytes.get(0) # => Result.Ok(10)
bytes.get(5) # => Result.Error(OutOfBounds(...))
hash
Show source codeHide source code
fn pub hash[H: mut + Hasher](hasher: mut H) {
hash_bytes(self, hasher)
}fn pub hash[H: mut + Hasher](hasher: mut H: mut)Writes the hash for self into the given Hasher.
index_of
Show source codeHide source code
fn pub index_of[B: Bytes](value: ref B, starting_at: Int) -> Option[Int] {
# This is a naive string searching algorithm (see
# https://en.wikipedia.org/wiki/String-searching_algorithm) for more details
# on the various algorithms.
#
# We're using the naive algorithm because:
#
# 1. It's easy to implement
# 2. It doesn't require any pre-processing
# 3. At the time of writing there was no need for something more performant
let find_size = value.size
if find_size == 0 or size == 0 or find_size > size { return Option.None }
let mut a = starting_at
let max = size - find_size
while a <= max {
let mut b = 0
while b < find_size and get(a + b) == value.get(b) { b += 1 }
if b == find_size { return Option.Some(a) }
a += 1
}
Option.None
}fn pub index_of[B: Bytes](value: ref B, starting_at: Int) -> Option[Int]Returns the byte index of the first occurrence of the given sequence of bytes, starting at the given byte index.
Examples
'hello'.index_of('h', starting_at: 0) # => Option.Some(0)
'hello'.index_of('l', starting_at: 0) # => Option.Some(2)
'hello'.index_of('l', starting_at: 3) # => Option.Some(3)
'hello'.index_of('x', starting_at: 0) # => Option.None
into_byte_array
Show source codeHide source code
fn pub move into_byte_array -> ByteArray {
self
}fn pub move into_byte_array -> ByteArrayinto_string
Show source codeHide source code
fn pub move into_string -> String {
drain_to_string
}fn pub move into_string -> StringMoves self into a String.
iter
Show source codeHide source code
fn pub iter -> Iter[ref Self] {
bytes
}fn pub iter -> Iter[ref ByteArray]Returns an iterator over the bytes in self.
last
Show source codeHide source code
fn pub last -> Option[Int] {
get(size - 1).ok
}fn pub last -> Option[Int]Returns the last byte in self
Examples
ByteArray.new.last # => Option.None
ByteArray.from_array([10, 20]).last # => Option.Some(20)
pointer
Show source codeHide source code
fn pub pointer -> Pointer[UInt8] {
@buffer
}fn pub pointer -> Pointer[UInt8]Returns a raw pointer to the bytes of self
This method is meant for FFI purposes, and use of it should be avoided at all costs.
pop
Show source codeHide source code
fn pub mut pop -> Option[Int] {
if @size == 0 { return Option.None }
@size -= 1
Option.Some(get_unchecked(@size))
}fn pub mut pop -> Option[Int]Removes a value from the back of the ByteArray, returning the removed
value.
If no value was found, a None is returned instead.
Examples
Popping an existing value:
let bytes = ByteArray.from_array([10])
bytes.pop # => Option.Some(10)
bytes.size # => 0
Popping a value when the ByteArray is empty:
let bytes = ByteArray.new
bytes.pop # => Option.None
Show source codeHide source code
fn pub mut print[B: Bytes](bytes: ref B) -> Result[Nil, E] {
try write(bytes)
write('\n')
}fn pub mut print[B: Bytes](bytes: ref B) -> Result[Nil, E]Writes the entirety of bytes to the underlying stream, followed by
a Unix newline.
push
Show source codeHide source code
fn pub mut push(value: Int) {
reserve(1)
write_to(@size, value)
@size += 1
}fn pub mut push(value: Int)Pushes a value to the back of the ByteArray, returning the pushed value.
Examples
Pushing a value into a ByteArray:
let bytes = ByteArray.new
bytes.push(10) # => 10
bytes.size # => 1
remove_at
Show source codeHide source code
fn pub mut remove_at(index: Int) -> Result[Int, OutOfBounds] {
try check_bounds(index, @size)
let addr = address_of(index)
let val = addr.0 as Int
alloc.copy(from: ptr.add(addr, 1), to: addr, size: @size - index - 1)
@size -= 1
Result.Ok(val)
}fn pub mut remove_at(index: Int) -> Result[Int, OutOfBounds]Removes the value at the given index, returning the removed value.
Panics
This method panics if the index is out of bounds.
Examples
Removing an existing value:
let bytes = ByteArray.from_array([10])
bytes.remove_at(0) # => 10
bytes.size # => 0
reserve
Show source codeHide source code
fn pub mut reserve(size: Int) {
if @capacity - @size >= size { return }
@capacity = max(@capacity * 2, @capacity + size)
@buffer = alloc.resize(@buffer, @capacity)
}fn pub mut reserve(size: Int)Reserves space for size additional values.
The actual space reserved may be greater to prevent frequent reallocations.
After calling this method, the capacity will be greater than or equal to
self.size + size.
If the capacity is great enough or the given size is less than zero, this method does nothing.
Examples
let bytes = ByteArray.from_array([1, 2, 3, 4])
bytes.reserve(2)
bytes.capacity # => 8
reserve_exact
Show source codeHide source code
fn pub mut reserve_exact(size: Int) {
if @capacity - @size >= size { return }
@capacity = @capacity + size
@buffer = alloc.resize(@buffer, @capacity)
}fn pub mut reserve_exact(size: Int)Reserves space for exactly size additional values.
Unlike ByteArray.reserve, this method won't allocate more than the given
amount.
If there's already enough space for size extra values, this method does
nothing.
Examples
let bytes = ByteArray.new
bytes.reserve_exact(4)
bytes.capacity # => 6
resize
Show source codeHide source code
fn pub mut resize(size: Int, value: Int) {
if size < 0 {
invalid_size(size)
} else if size == @size {
return
} else if size < @size {
@size = size
return
}
let add = size - @capacity
if add > 0 { reserve_exact(add) }
alloc.write(byte: value, to: tail, size: size - @size)
@size = size
}fn pub mut resize(size: Int, value: Int)Resizes self to the new size.
If the given size is greater than the current size, the value argument is
used to fill in the additional slots. If the given size is less than the
current size, self is simply truncated.
Panics
This method panics if the given size is less than zero.
Examples
let bytes = ByteArray.new
bytes.resize(size: 2, value: 1)
bytes # => ByteArray.from_array([1, 1])
bytes.resize(size: 0, value: 0)
bytes # => ByteArray.new
reverse
Show source codeHide source code
fn pub mut reverse {
reverse_at(0)
}fn pub mut reverseReverses self in-place
Examples
let a = ByteArray.from_array([10, 20, 30])
a.reverse
a # => ByteArray.from_array([30, 20, 10])
reverse_at
Show source codeHide source code
fn pub mut reverse_at(index: Int) {
check_bounds(index, size).or_panic
let mut a = index
let mut b = size - 1
while a < b {
let a_val = get_unchecked(a)
let b_val = get_unchecked(b)
write_to(b, a_val)
write_to(a, b_val)
a += 1
b -= 1
}
}fn pub mut reverse_at(index: Int)Reverses self in-place starting at the given index.
Panics
This method panics if the index is out of bounds.
Examples
let a = ByteArray.from_array([10, 20, 30, 40])
a.reverse_at(2)
a # => ByteArray.from_array([10, 20, 40, 30])
set
Show source codeHide source code
fn pub mut set(index: Int, value: Int) {
check_bounds(index, @size).or_panic
write_to(index, value)
}fn pub mut set(index: Int, value: Int)Writes the byte value to the position index.
Panics
This method panics if the index is out of bounds.
Examples
let bytes = ByteArray.from_array([10, 20])
bytes.set(0, 30)
bytes.get(0) # => 30
size
Show source codeHide source code
fn pub inline size -> Int {
@size
}fn pub inline size -> IntReturns the number of bytes in self.
Examples
ByteArray.new.size # => 0
ByteArray.from_array([10]).size # => 1
slice
Show source codeHide source code
fn pub slice(start: Int, end: Int) -> Slice[Self] {
Slice.checked(self, start, end)
}fn pub slice(start: Int, end: Int) -> Slice[ByteArray]Returns a Slice over self from start until (but excluding) end.
Because a ByteArray is a mutable type it's possible for the values covered
by a Slice to change. While the Slice type handles such cases (e.g. if
the ByteArray is cleared, a Slice doesn't produce any values), it's best
not to modify self when slices to it exist as this may result in
confusing/unexpected behavior.
The start index is allowed to be equal to the size of self, but only if
end is the same as start. Thus given a ByteArray with a size of 4,
the range 4 until 4 is valid.
Panics
This method panics if start or end is out of bounds.
Examples
Slicing a ByteArray:
let bytes = ByteArray.from_array([1, 2, 3, 4])
let slice = bytes.slice(start: 1, end: 3)
slice.get(0) # => 2
slice.get(1) # => 3
Slicing a ByteArray and updating the underlying values:
let bytes = ByteArray.from_array([1, 2, 3, 4])
let slice = bytes.slice(start: 1, end: 3)
slice.get(0) # => 1
bytes.set(0, 10)
slice.get(0) # => 10
starts_with?
Show source codeHide source code
fn pub starts_with?[T: Bytes](prefix: ref T) -> Bool {
ptr.starts_with?(pointer, size, prefix.pointer, prefix.size)
}fn pub starts_with?[T: Bytes](prefix: ref T) -> BoolReturns true if self starts with the given sequence of bytes.
Examples
'hello'.starts_with?('h'.to_byte_array) # => true
'hello'.starts_with?('h') # => true
to_array
Show source codeHide source code
fn pub to_array -> Array[Int] {
iter.to_array
}fn pub to_array -> Array[Int]Converts the ByteArray to an Array!(Int).
Examples
Converting a ByteArray:
let bytes = ByteArray.from_array([105, 110, 107, 111])
bytes.to_array # => [105, 110, 107, 111]
to_byte_array
Show source codeHide source code
fn pub to_byte_array -> ByteArray {
clone
}fn pub to_byte_array -> ByteArrayto_slice
Show source codeHide source code
fn pub to_slice -> Slice[Self] {
Slice.new(self, 0, size)
}fn pub to_slice -> Slice[ByteArray]Returns a Slice that covers all of self.
to_string
Show source codeHide source code
fn pub to_string -> String {
if empty? { return '' }
String.from_primitive(inko_string_from_bytes(@buffer, @size))
}fn pub to_string -> StringReturns a new String using the bytes in this ByteArray.
Any invalid UTF-8 sequences will be replaced with U+FFFD REPLACEMENT
CHARACTER, which looks like this: �
Examples
Converting a ByteArray into a String:
let bytes = ByteArray.from_array([105, 110, 107, 111])
bytes.to_string # => 'inko'
write
Show source codeHide source code
fn pub mut write[B: Bytes](bytes: ref B) -> Result[Nil, IoError] {
append(bytes)
Result.Ok(nil)
}fn pub mut write[B: Bytes](bytes: ref B) -> Result[Nil, Error]Writes the entirety of bytes to the underlying stream.
Types implementing this method must guarantee that upon returning from this
method, either all of the data is written and a Ok(Nil) is returned, or
an Error is returned.
zero
Show source codeHide source code
fn pub mut zero {
alloc.zero(pointer, size)
}fn pub mut zeroOverwrites all bytes in self with zero bytes.
Note that until this issue is resolved, the compiler might optimize code such that the writing of the zero bytes is optimized away.
Examples
let bytes = ByteArray.from_array([1, 2, 3])
bytes.zero
bytes # => ByteArray.from_array([0, 0, 0])
Implemented traits
Bytes
impl Bytes for ByteArrayIntoByteArray
impl IntoByteArray for ByteArrayToByteArray
impl ToByteArray for ByteArrayToSlice
impl ToSlice[ByteArray] for ByteArrayClone
impl Clone for ByteArrayEqual
impl Equal for ByteArrayDrop
impl Drop for ByteArrayFormat
impl Format for ByteArrayHash
impl Hash for ByteArrayWrite
impl Write[Error] for ByteArrayIntoString
impl IntoString for ByteArrayToString
impl ToString for ByteArray