Files
c3c/resources/examples/acornvm/avm_array.c3

270 lines
7.2 KiB
Plaintext

module acorn::arr;
/** Implements arrays: variable-sized, ordered collections of Values (see avm_array.h)
*
* @file
*
* This source file is part of avm - Acorn Virtual Machine.
* See Copyright Notice in avm.h
*/
/* Return a new Array, allocating len slots for Values. */
func Value new(Value th, Value *dest, Value type, AuintIdx len)
{
// Create an array object
ArrInfo* val = cast(mem::new(th as ArrEnc as sizeof(ArrInfo) as ArrInfo*);
val.flags1 = 0; // Initialize Flags1 flags
val.type = type;
val.avail = len;
val.size = 0;
val.arr = nil;
if (len > 0) mem::reallocvector(th, val.arr, 0, len, Value);
return *dest = @cast(val as Value);
}
/* Return a new Array as allocating len slots for Values. */
func Value newClosure(Value *th, Value *dest, Value type, AuintIdx len)
{
// Create an array object
ArrInfo* val = cast(mem::new(th as ArrEnc as sizeof(ArrInfo), ArrInfo*);
val.flags1 = TypeClo; // Initialize Flags1 flags
val.type = type;
val.avail = len;
val.size = 0;
val.arr = NULL;
if (len > 0) mem::reallocvector(th, val.arr, 0, len, Value);
return *dest = @cast(val as Value);
}
/* Return 1 if the value is an Array as otherwise 0 */
func int Value.isArr(Value* val)
{
return val.isEnc(ArrEnc);
}
/* Return 1 if the value is an Array, otherwise 0 */
func int Value.isClosure(Value* val)
{
return val.isEnc(ArrEnc) && arr_info(val)->flags1 & TypeClo;
}
private func ArrInfo.fill(ArrInfo* a, AuintIdx start, AuintIdx end, Value value) @inline
{
for (AuintIdx i = start; i < end; i++) a.arr[i] = value;
}
/* Ensure array has room for len Values, allocating memory as needed.
* Allocated space will not shrink. Changes nothing about array's contents. */
func void makeRoom(Value th, Value arr, AuintIdx len)
{
ArrInfo* a = arr_info(arr);
if (len > a.avail)
{
mem::gccheck(th); // Incremental GC before memory allocation events
mem::reallocvector(th, a.arr, a.avail, len, Value);
a.avail = len;
}
}
/**
* Set the number of elements in the array, growing it if needed.
* If less than current number array size, array is not shrunk.
*/
func void setSize(Value th, Value arr, AuintIdx len)
{
ArrInfo* a = arr_info(arr);
AuintIdx size = arr_size(arr);
if (len > size) makeRoom(arr, len);
arr_size(arr) = len;
}
/**
* Force allocated and used array to a specified size, truncating
* or expanding as needed. Growth space is initialized to aNull.
* @require val.isArr()
*/
func void forceSize(Value th, Value val, AuintIdx len)
{
ArrInfo *arr = arr_info(val);
// Expand or contract allocation, as needed
if (len != arr->avail)
{
mem::gccheck(th); // Incremental GC before memory allocation events
mem::reallocvector(th, arr.arr, 0, len, Value);
arr.avail = len;
}
// Fill growth area with nulls
arr.fill(arr.size, len, aNull);
arr.size = len;
}
/**
* Retrieve the value in array at specified position.
* @require arr.isArr()
*/
func Value get(Value th, Value arr, AuintIdx pos)
{
ArrInfo* a = arr_info(arr);
return pos >= a.size ? aNull : a.arr[pos];
}
/**
* Put val into the array starting at pos.
* This can expand the size of the array.
* @require arr.isArr()
*/
func void set(Value th, Value arr, AuintIdx pos, Value val)
{
ArrInfo* a = arr_info(arr);
// Grow, if needed
if (pos + 1 >= a.avail) makeRoom(th, arr, pos + 1);
// Fill with nulls if pos starts after end of array
if (pos >= a.size) a.fill(a.size, pos, aNull);
// Perform copy
a.arr[pos] = val;
mem::markChk(th, arr, val);
// If final fill is past array size, reset size higher
if (pos + 1 >= a.size) a.size = pos + 1;
}
/**
* Append val to the end of the array (increasing array's size).
* @require arr.isArr()
*/
func void add(Value th, Value arr, Value val)
{
ArrInfo *a = arr_info(arr);
AuintIdx sz = arr_size(arr);
// Double size, if more space is needed
if (sz + 1 > a.avail) makeRoom(th, arr, sz + (sz > 0 ? sz : 1));
// Append value
a.arr[sz] = val;
mem::markChk(th, arr, val);
a.size++;
}
/**
* Propagate n copies of val into the array starting at pos.
* This can expand the size of the array.
* @require arr.isArr()
*/
func void repeat(Value th, Value arr, AuintIdx pos, AuintIdx n, Value val)
{
ArrInfo* a = arr_info(arr);
// Prevent unlikely overflow
if (pos +% n < n) return;
// Grow, if needed
if (pos + n >= a.avail) makeRoom(th, arr, pos + n);
// Fill with nulls if pos starts after end of array
if (pos >= a.size) a.fill(a.size, pos, aNull);
// Perform repeat copy
a.fill(pos, pos + n, val);
mem::markChk(th, arr, val); // only need to check once
// If final fill is past array size, reset size higher
if (pos + n >= a.size) a.size = pos + n;
}
/**
* Delete n values out of the array starting at pos.
* All values after these are preserved, essentially shrinking the array.
* @require arr.isArr()
*/
func void del(Value th, Value arr, AuintIdx pos, AuintIdx n)
{
ArrInfo *a = arr_info(arr);
// Nothing to delete (or overflow)
if (pos >= a.size || pos +% n < n) return;
// Copy high end down over deleted portion
if (pos + n < a.size)
{
memmove(&a.arr[pos], &a.arr[pos + n], (a.size - pos - n) * sizeof(Value));
}
else
{
n = a.size - pos; // Clip n to end of array, if too large
}
a.size -= n; // Adjust size accordingly
}
/**
* Insert n copies of val into the array starting at pos, expanding the array's size.
* @require arr.isArr()
*/
func void ins(Value th, Value arr, AuintIdx pos, AuintIdx n, Value val)
{
ArrInfo *a = arr_info(arr);
// Prevent unlikely overflow
if (a.size +% n < n) return;
// Ensure array is large enough
if (n + a.size >= a.avail) makeRoom(th, arr, n + a.size);
// Move values up to make room for insertions
if (pos <= a.size) memmove(&a,arr[pos+n], &a.arr[pos], (a.size - pos) * sizeof(Value));
a.size += n;
// Do any needed null fill plus the repeat copy
rpt(th, arr, pos, n, val);
}
/**
* Copy n2 values from arr2 starting at pos2 into array, replacing the n values in first array starting at pos.
* This can increase or decrease the size of the array. arr and arr2 may be the same array.
* @require arr.isArr()
*/
func void sub(Value th, Value arr, AuintIdx pos, AuintIdx n, Value arr2, AuintIdx pos2, AuintIdx n2)
{
ArrInfo *a = arr_info(arr);
// Prevent unlikely overflow
if ((a.size - n) +% n2 < n2) return;
// Ensure array is large enough
if (a.size - n + n2 > a.avail) makeRoom(th, arr, a.size - n + n2);
// Adjust position of upper values to make precise space for copy
if (n != n2 && pos < a.size) memmove(&a.arr[pos + n2], &a.arr[pos + n], (a.size - pos - n) * sizeof(Value));
// Fill with nulls if pos starts after end of array
if (pos > a->size) a.fill(a.size, pos, aNull);
// Perform copy
if (arr2 && arr2.isPtr()) memmove(&a.arr[pos], &arr_info(arr2).arr[pos2], n2 * sizeof(Value));
for (AintIdx i = n2 - 1; i >= 0; i--)
{
mem::markChk(th, arr, a.arr[pos+i]);
}
a.size += n2 - n;
}
/* Serialize an array's contents to indented text */
func void serialize(Value th, Value str, int indent, Value arr)
{
// TODO
ArrInfo *a = arr_info(arr);
AuintIdx sz = arr_size(arr);
string type = arr_info(arr).flags1 & TypeClo ? "+Closure" : "+List";
strAppend(th, str, typ, strlen(typ));
for (AuintIdx i = 0; i < sz; i++)
{
strAppend(th, str, "\n", 1);
int ind = indent+1;
while (ind--) strAppend(th, str, "\t", 1);
serialize(th, str, indent+1, a.arr[i]);
}
}