0 up to 18446744073709551614. It is sparse: an index either holds a value or is empty, and writing to a distant index does not allocate the slots in between. Nothing shifts when a value is deleted, so an index keeps its meaning for the lifetime of the key. That makes an array the right shape for data that already has a natural numeric key, such as a sequence number or a bucketed timestamp, where a list would force a scan and a hash would store the index as a string.
Two numbers describe an array and they are not the same. ARCOUNT is how many slots hold a value; ARLEN is the highest occupied index plus one. They agree only when the array is densely filled from 0.
Each array also keeps an append cursor, holding the index the last append wrote to. ARINSERT appends after it, ARRING appends after it and wraps within a fixed number of slots, ARNEXT reads where the next append will land, and ARSEEK moves it. The cursor tracks appends only: positional writes with ARSET and deletions never move it, so a freed index is never silently reused.
ARSET
Set contiguous array values from an index
ARMSET
Set values at several array indexes
ARGET
Get the value at an array index
ARMGET
Get the values at several array indexes
ARGETRANGE
Get array values in an index range
ARSCAN
Scan the populated slots of an array
ARGREP
Search array values with predicates
ARDEL
Delete values at the given array indexes
ARDELRANGE
Delete array values in one or more index ranges
ARCOUNT
Count the values stored in an array
ARLEN
Get the length of an array
ARINSERT
Append values to an array
ARRING
Append values to a fixed-size ring
ARLASTITEMS
Read the most recently written array values
ARNEXT
Get the index the next append will use
ARSEEK
Move the array append cursor
AROP
Aggregate array values in an index range
ARINFO
Inspect the internal layout of an array