ARRAY[T]
Array[T] is an owned allocator-bound contiguous buffer.
[ ARRAY SOURCE ] [ HOSTED ARRAY SOURCE ]
import "alloc.dby" as alloc
pub struct Array[T]:
data: ptr[T]
len: usize
capacity: usize
allocator_context: ptr[u8]
allocator_vtable: const_ptr[alloc.AllocatorVTable]
fn allocator_of[T](values: borrow Array[T]) -> alloc.AllocatorRef:
return alloc.AllocatorRef(context = (*values).allocator_context, vtable = (*values).allocator_vtable)
fn checked_bytes[T](elements: usize) -> Result[usize, alloc.AllocError]:
let element_size: usize = size_of(T)
if element_size == (0 as usize):
return Result[usize, alloc.AllocError].Err(alloc.AllocError.ContractViolation())
if elements > (((0xffffffff as u32) as usize) / element_size):
return Result[usize, alloc.AllocError].Err(alloc.AllocError.SizeOverflow())
return Result[usize, alloc.AllocError].Ok(elements * element_size)
pub fn with_allocator[T](allocator: alloc.AllocatorRef) -> Array[T]:
let alignment: usize = align_of(T)
let pointer_result = alloc.allocate(allocator, 0 as usize, alignment)
match pointer_result:
case Result.Ok(pointer):
return Array[T](data = pointer as usize as ptr[T], len = 0 as usize, capacity = 0 as usize, allocator_context = allocator.context, allocator_vtable = allocator.vtable)
case Result.Err(error):
return Array[T](data = alignment as ptr[T], len = 0 as usize, capacity = 0 as usize, allocator_context = allocator.context, allocator_vtable = allocator.vtable)
pub fn with_capacity[T](capacity: usize, allocator: alloc.AllocatorRef) -> Result[Array[T], alloc.AllocError]:
if size_of(T) == (0 as usize):
return Result[Array[T], alloc.AllocError].Err(alloc.AllocError.ContractViolation())
if capacity == (0 as usize):
return Result[Array[T], alloc.AllocError].Ok(with_allocator[T](allocator))
let bytes = checked_bytes[T](capacity)?
let result = alloc.allocate(allocator, bytes, align_of(T))
match result:
case Result.Ok(pointer):
return Result[Array[T], alloc.AllocError].Ok(Array[T](data = pointer as usize as ptr[T], len = 0 as usize, capacity = capacity, allocator_context = allocator.context, allocator_vtable = allocator.vtable))
case Result.Err(error):
return Result[Array[T], alloc.AllocError].Err(error)
pub fn reserve[T](values: borrow mut Array[T], additional: usize) -> Result[unit, alloc.AllocError]:
if additional > (((0xffffffff as u32) as usize) - (*values).len):
return Result[unit, alloc.AllocError].Err(alloc.AllocError.SizeOverflow())
let required: usize = (*values).len + additional
if required <= (*values).capacity:
return Result[unit, alloc.AllocError].Ok(unit)
let new_capacity: usize = (*values).capacity
if new_capacity == (0 as usize):
new_capacity = 4 as usize
while new_capacity < required:
if new_capacity > (((0xffffffff as u32) as usize) / (2 as usize)):
new_capacity = required
else:
new_capacity = new_capacity * (2 as usize)
let old_bytes = checked_bytes[T]((*values).capacity)?
let new_bytes = checked_bytes[T](new_capacity)?
let allocator = allocator_of[T](borrow *values)
let result = alloc.resize(allocator, (*values).data as usize as ptr[u8], old_bytes, new_bytes, align_of(T))
match result:
case Result.Ok(pointer):
(*values).data = pointer as usize as ptr[T]
(*values).capacity = new_capacity
return Result[unit, alloc.AllocError].Ok(unit)
case Result.Err(error):
return Result[unit, alloc.AllocError].Err(error)
pub fn push[T](values: borrow mut Array[T], value: T) -> Result[unit, alloc.AllocError]:
reserve[T](values, 1 as usize)?
(*values).data[(*values).len] = value
(*values).len = (*values).len + (1 as usize)
return Result[unit, alloc.AllocError].Ok(unit)
pub fn pop[T](values: borrow mut Array[T]) -> Option[T]:
if (*values).len == (0 as usize):
return Option[T].None()
let index: usize = (*values).len - (1 as usize)
let value: T = (*values).data[index]
(*values).len = index
return Option[T].Some(value)
pub fn insert[T](values: borrow mut Array[T], index: usize, value: T) -> Result[unit, alloc.AllocError]:
if index > (*values).len:
return Result[unit, alloc.AllocError].Err(alloc.AllocError.ContractViolation())
reserve[T](values, 1 as usize)?
let cursor: usize = (*values).len
while cursor > index:
(*values).data[cursor] = (*values).data[cursor - (1 as usize)]
cursor = cursor - (1 as usize)
(*values).data[index] = value
(*values).len = (*values).len + (1 as usize)
return Result[unit, alloc.AllocError].Ok(unit)
pub fn remove[T](values: borrow mut Array[T], index: usize) -> Option[T]:
if index >= (*values).len:
return Option[T].None()
let value: T = (*values).data[index]
let cursor: usize = index
while (cursor + (1 as usize)) < (*values).len:
(*values).data[cursor] = (*values).data[cursor + (1 as usize)]
cursor = cursor + (1 as usize)
(*values).len = (*values).len - (1 as usize)
return Option[T].Some(value)
pub fn get[T](values: borrow Array[T], index: usize) -> Option[T]:
if index >= (*values).len:
return Option[T].None()
return Option[T].Some((*values).data[index])
pub fn get_mut[T](values: borrow mut Array[T], index: usize) -> Option[borrow mut T]:
if index >= (*values).len:
return Option[borrow mut T].None()
return Option[borrow mut T].Some(borrow mut (*values).data[index])
pub fn as_slice[T](values: borrow Array[T]) -> slice[T]:
return __dbyte_runtime.slice_from_raw((*values).data, (*values).len)
pub fn as_mut_slice[T](values: borrow mut Array[T]) -> mut_slice[T]:
return __dbyte_runtime.mut_slice_from_raw((*values).data, (*values).len)
pub fn clone[T](values: borrow Array[T]) -> Result[Array[T], alloc.AllocError]:
return clone_in[T](values, allocator_of[T](values))
pub fn clone_in[T](values: borrow Array[T], allocator: alloc.AllocatorRef) -> Result[Array[T], alloc.AllocError]:
let copy = with_allocator[T](allocator)
reserve[T](borrow mut copy, (*values).len)?
let index: usize = 0 as usize
while index < (*values).len:
copy.data[index] = (*values).data[index]
index = index + (1 as usize)
copy.len = (*values).len
return Result[Array[T], alloc.AllocError].Ok(copy)
pub fn clear[T](values: borrow mut Array[T]) -> unit:
let remaining: usize = (*values).len
while remaining > (0 as usize):
remaining = remaining - (1 as usize)
drop((*values).data[remaining])
(*values).len = 0 as usize
return unit
pub fn length[T](values: borrow Array[T]) -> usize:
return (*values).len
pub fn capacity[T](values: borrow Array[T]) -> usize:
return (*values).capacity
drop fn destroy[T](values: borrow mut Array[T]):
clear[T](values)
let bytes = checked_bytes[T]((*values).capacity)
match bytes:
case Result.Ok(size):
alloc.release(allocator_of[T](borrow *values), (*values).data as usize as ptr[u8], size, align_of(T))
case Result.Err(error):
return unit
(*values).len = 0 as usize
(*values).capacity = 0 as usize
(*values).data = align_of(T) as ptr[T]
return unit
import "array.dby" as owned
import "hosted_alloc.dby" as host
# Hosted/VM convenience constructor. Freestanding callers use
# owned.with_allocator explicitly and never receive a fallback allocator.
pub fn new[T]() -> owned.Array[T]:
return owned.with_allocator[T](host.system_allocator())
SOURCE SHA-256 f94368480873666a54db6177d72804b87d5b7ad5a74a9e31ca6213aa959110dc · RAW CONTRACT MIRROR
00 i686 layout§
SIZE 20 BYTES WORD 0 data WORD 1 len WORD 2 capacity WORD 3 allocator context WORD 4 allocator vtable
Empty arrays use a non-null dangling view with zero length and capacity. Zero-sized element types are rejected.
10 growth + failure§
0 -> 4 -> 8 -> 16 -> ... CHECK ELEMENT COUNT CHECK BYTE COUNT ALLOCATE OR RESIZE BEFORE PUBLISH
Reserve failure preserves pointer, length, capacity and initialized elements.
20 operations + destruction§
Push, pop, insert, remove, clear and reserve operate through borrowed array references. Bounds-sensitive operations return Option and Result instead of unchecked safe access. Destruction drops initialized elements in reverse index order before freeing the buffer through the array's original allocator.
BOUNDARY: v12.1 cloning is restricted to Copy elements.
clone_in is the only way to select a different allocator; ordinary moves preserve allocator identity.NEXT: Read String contract.