InfArray
A chunked array that bypasses Luau's internal table size limit of 2^26.
Values are stored across fixed-size chunks so a single logical array can hold
up to 2^53 elements (the largest exact integer a Luau number can represent).
Indices are 1-based and resolved with integer math.
Removals leave holes. InfArray:Length reports the highest assigned index
(holes included), while InfArray:Count reports the number of present
elements. A hole reads back as nil through InfArray:Get and is skipped by
iteration, but the backing slot holds the InfArray.EMPTY tombstone, so raw
chunks are dense tables rather than holey ones.
local InfArray = require(path.to.InfArray)
local arr = InfArray.new()
arr:PushBack("a")
arr:PushBack("b")
print(arr:Get(1)) --> "a"
print(#arr) --> 2
Several backwards-compatible aliases exist for the primary methods:
get/GetValueAtIndex (Get), set/Replace (Set), InsertBack (PushBack),
TransformRange (Transform), GetTotalLen (Count) and GetLength (Length).
Properties
LIMIT
This item is read only and cannot be modified. Read OnlystaticInfArray.LIMIT: numberMaximum number of elements a single chunk can hold (2^26 = 67108864).
EMPTY
This item is read only and cannot be modified. Read OnlystaticInfArray.EMPTY: anyMarks a hole in a raw chunk.
Use EMPTY only when you index a chunk table yourself. Everywhere else use
nil. The three functions that hand back a raw chunk are
InfArray:GetChunk, InfArray:GetChunkAndPosition and
InfArray:IterateChunks.
| Doing | Use |
|---|---|
Set(i, nil), PushBack(nil), Transform returning nil |
nil |
| A table with gaps passed to InfArray:SetChunk | nil, rewritten for you |
| Reading InfArray:Get | nil |
| InfArray:Transform callback's value argument | nil |
InfArray:Iterate or for..in value |
neither, holes are skipped |
chunk[j] from a raw chunk |
EMPTY |
Inside a chunk's tracked length a slot is a value or EMPTY, never nil.
Functions
new
InfArray.new(size: number?,--
Optional initial length; pre-allocates chunks.
value: any?--
Optional fill value written to every pre-allocated slot.
) → InfArray
Creates a new InfArray. When size is given, the array is pre-sized to size
elements; if value is also provided, every slot is filled with it and
InfArray:Count reflects the full size (otherwise the slots start as holes).
Get
InfArray:Get(index: number--
1-based global index.
) → any?--
The value at index, or nil if unset or out of range.
Reads the value at index in O(1). Returns nil for holes and for indices
whose chunk does not exist yet.
Set
InfArray:Set(index: number,--
1-based global index within an existing chunk.
value: any?--
New value; pass nil to clear the slot into a hole.
) → boolean--
true if the write happened, false if it was a no-op.
In-range O(1) write that keeps InfArray:Count and InfArray:Length correct.
Returns true when the write lands. This is a no-op returning false if
the index's chunk does not exist yet — use InfArray:PushBack,
InfArray.new with a size, or InfArray:SetChunk to grow the array first.
Writing past the chunk's current length extends it and tombstones the skipped slots, which costs O(gap).
GetChunk
InfArray:GetChunk(index: number--
1-based chunk index.
) → {any}?--
The raw backing table for that chunk, or nil.
Returns the raw chunk table by chunk index. Cleared slots hold
InfArray.EMPTY, not nil, and the table is padded past its logical length;
pair it with the chunk's tracked length (see InfArray:IterateChunks) when
scanning it.
SetChunk
InfArray:SetChunk(chunkIndex: number,--
1-based chunk index to replace.
value: {any},--
The new backing table for the chunk.
len: number?--
Logical length of value; defaults to #value.
) → ()Replaces an entire chunk in one call, recomputing InfArray:Count for the swapped range and extending InfArray:Length if the new chunk reaches further. Prefer this over per-element writes for bulk population.
Takes ownership of value: any nil below the chunk's length is rewritten in
place as an InfArray.EMPTY tombstone.
GetChunkAndPosition
InfArray:GetChunkAndPosition(index: number--
1-based global index.
) → ({any}?,--
The chunk containing index, or nil if absent.
number--
The 1-based position of index within that chunk.
)Resolves a global index to its chunk and in-chunk position in one call, useful for hot loops that want to read/write a slot without recomputing the location.
PushBack
InfArray:PushBack(value: any--
Value to append at the end of the array.
) → number--
The global index the value was written to.
Appends value after the highest assigned index, allocating a new chunk when
the current one fills up. Amortised O(1). Appending nil extends
InfArray:Length with a hole and leaves InfArray:Count unchanged.
RemoveIndex
InfArray:RemoveIndex(index: number--
1-based global index to clear.
) → ()
Clears the value at index, decrementing InfArray:Count. This leaves a hole.
InfArray:Length is unchanged and later elements are not shifted.
Iterate
InfArray:Iterate(callback: (index: number,value: any) → boolean?--
Called for each present element; return true to stop early.
) → ()
Iterates every present element in ascending index order, skipping holes. Return
true from callback to break out early. For maximum bulk throughput, use
InfArray:IterateChunks instead.
IterateChunks
InfArray:IterateChunks(callback: (chunk: {any},base: number,len: number) → boolean?--
Called once per chunk; return true to stop early.
) → ()
Hands each raw chunk to callback along with its base global-index offset and
tracked len. The fastest way to process the whole array in bulk. The caller
must skip slots equal to InfArray.EMPTY itself, since chunks tombstone
their holes. Return true to stop early.
Find
InfArray:Find(needle: any--
Value to search for.
) → number?--
The first global index equal to needle, or nil.
Linearly scans in ascending order and returns the first global index whose value
equals needle, or nil if not found. Holes never cut the search short, and
searching for nil always returns nil. Pass InfArray.EMPTY to locate the
first hole.
Transform
InfArray:Transform(start: number,--
First global index to visit (inclusive).
stop: number,--
Last global index to visit (inclusive).
step: number,--
Stride between visited indices.
updateFunc: (index: number,value: any) → any--
Returns the new value for each visited slot.
) → ()
Applies updateFunc over the range [start, stop] by step, writing back each
returned value. updateFunc sees nil for a hole and may return nil to clear
a slot; InfArray:Count stays correct either way. Indices whose chunk does not
exist are skipped.
Count
InfArray:Count() → number--
The number of present (non-nil) elements.
Returns how many elements are actually present, excluding holes. O(1).
Length
InfArray:Length() → number--
The highest assigned index (holes included).
Returns the logical length: the highest index ever assigned, counting holes.
Equivalent to #arr. O(1).