Arrays

Arrays store a sequential collection of elements of the same type and a count of the number of elements in an array. Instead of declaring individual variables, such as num0, num1, …, and num99, you declare one array variable such as nums and use nums[0], nums[1], and …, nums[99] to represent individual variables. A specific element in an array is accessed by an index.

Both Static Arrays and Dynamic Arrays are autocasted to Array Views if the array view is a parameter of a function.

Jai supports both static stack allocated arrays, dynamically allocated arrays, resizable arrays, and array views.

Static arrays

a : [4] u32;      // An array of 4 u32 integers.
b : [30]float64; // An array of 30 float64's.

You access array members similarly to in other languages, using the [] subscript syntax.

a: [4] float; 
a[0] = 10.0;
a[1] = 20.0;
a[2] = 1.4;
a[3] = 10.0;

print("a = %\n", a);

Note: The print function uses % to indicate insertion points for variables. Unlike many other languages, you don’t need to specify what kind of thing is being printed, and it handles complex types too. However, if you want any special formatting of the variable to be printed, you must handle that separately.

You can initialize arrays using the following syntax:

array : [4] float = float.[10.0, 20.0, 1.4, 10.0];

Unlike C, Jai stores array length information. You can find out the array length by using array.count.

print("array has % number of elements\n", array.count);

Using Arrays as boolean values

Arrays implicitly convert to boolean values, and can be used in if statements to check if the array has elements. An array is true when the number of elements is greater than 0, and an array is false when the array has no elements in it.

array: [..] int;
if array {

} 

Multi-Dimensional Static arrays

Jai supports multi-dimensional static arrays. Multi-dimensional static arrays can be declared like this:

a: [4][4] float;    // 2D static array
b: [4][4][4] float; // 3D static array

Multi-dimenional arrays can be initialized using the following syntax:

array: [2][2] int = .[int.[1,0], int.[0,3]];

Dynamic arrays

Sometimes you don’t know how many you will need in your list. Dynamic arrays are the most basic data structure at your disposal for arbitrary-length data. You can of course build much more powerful data structures if you need them, but you’ll be surprised at how often a dynamic array is just what you need. Here’s the declaration syntax:

a : [..]int;   // A dynamic array of integers.
b : [..]string;  // A dynamic array of strings.

A few things to note about dynamic arrays:

  • They allocate memory as needed.
  • When you add things, memory is reallocated as needed.
  • They will use your context’s default allocator; this will be explained later.

Here is the struct definition and member fields of the resizable array found in Preload.jai:

Resizable_Array :: struct {
  count: s64;             // number of elements in the array
  data : *void;           // array data
  allocated: s64;         // total space used by the resizable array
  allocator: Allocator;   // the allocator in use by the resizable array
  allocator_data: *void;
}

The resizable array functions similar to C++ std::vector, but with the added bonus that they use your current context’s allocator. Contexts are explained in a different section ─ for now, just know that this is pretty great.

With those caveats out of the way, here’s how you work with them:

array_add(*myarray, 5); // Add 5 to the end of myarray
array_add(*myarray, 9); // Add 9 to the end of myarray
array_reset(*myarray);  // Reset myarray
array_find(myarray, 5); // look for 5 in myarray
array_copy(*anotherarray, myarray); // copy array into anotherarray

In many cases, you’ll be adding a number of entries to dynamic arrays at once, and you might even know how many there are. For this situation, it’s worth considering that allocating once is almost always better than allocating as needed. Here we’re going to demonstrate this using a loop ─ if these are unfamiliar to you, check ahead to the chapter on for loops before reading on.

myarray : [..]int;
N :: 50;
for 1..N array_add(*myarray, it);

The above example works just fine, but involves 50 individual reallocations for no reason, since we already knew we were going to add 50 items. So it’s better to do:

myarray : [..]int;
N :: 50;
array_reserve(*myarray, N); // Reserve 50 items!
for 1..N array_add(*myarray, it);

Note that array_reserve wants the total number of items to reserve, not the number to additionally reserve. So you may want to use myarray.count or myarray.allocated to get the number of items currently in the array, or the number currently reserved in the array.

When you’re done with a dynamic array, it’s good to array_free the array. Consider using defer for this!

Array views

The array view data structure represents a view into the data that is contained in an array or a subsection of an array. Here is how the array view is declared.

arr: []int = int.[1,2,3,4,5];

This is the array view struct declaration and data fields as found in Preload.jai:

Array_View_64 :: struct {
  count: s64;  // number of elements
  data : *u8;  // pointer to element array data
}

Both Static Arrays and Dynamic Arrays are autocasted to Array Views if the array view is a parameter of a function.