Skip to main content
FFXIVClientStructs provides C# wrappers for C++ STD containers found in the game client. These containers manage heap-allocated collections and expose them through familiar C# interfaces.

StdVector<T, TMemorySpace>

A dynamic array container similar to List<T>, wrapping std::vector.

Memory Layout

[StructLayout(LayoutKind.Sequential)]
public unsafe struct StdVector<T, TMemorySpace>
    where T : unmanaged
    where TMemorySpace : IStaticMemorySpace
{
    public T* First;      // Pointer to first element
    public T* Last;       // Pointer to one past last element
    public T* End;        // Pointer to end of allocation
}
Size: 24 bytes (3 pointers)

Key Properties

  • Count: Number of elements (Last - First)
  • Capacity: Allocated capacity (End - First)
  • LongCount: 64-bit element count

Access Patterns

// Direct element access
ref var element = ref vector[index];

// Span-based iteration (efficient)
Span<MyStruct> span = vector.AsSpan();
foreach (ref var item in span)
{
    // Modify item directly
}

// Pointer iteration
for (var ptr = vector.First; ptr < vector.Last; ptr++)
{
    // Access *ptr
}

// LINQ-style enumeration
foreach (ref var item in vector)
{
    // Enumerates by reference
}

Common Operations

// Adding elements
vector.AddCopy(in item);          // Copy item
vector.AddSpanCopy(span);         // Copy span
vector.InsertCopy(index, in item); // Insert at index

// Removing elements
vector.RemoveAt(index);
vector.RemoveRange(index, count);
vector.Clear();

// Searching
long idx = vector.LongIndexOf(item);
bool found = vector.Contains(item);

// Capacity management
vector.EnsureCapacity(newSize);
vector.TrimExcess();
vector.Resize(newSize);

Working with Pointer Collections

For vectors of pointers, use Pointer<T> wrapper:
// Vector of pointers to structs
StdVector<Pointer<MyStruct>, IStaticMemorySpace.Default> pointerVector;

// Access pointed-to data
for (int i = 0; i < pointerVector.Count; i++)
{
    MyStruct* ptr = pointerVector[i].Value;
    if (ptr != null)
    {
        // Use ptr->Field
    }
}

StdMap<TKey, TValue, TMemorySpace>

An ordered associative container wrapping std::map, implemented as a red-black tree.

Memory Layout

[StructLayout(LayoutKind.Sequential, Size = 0x10)]
public unsafe struct StdMap<TKey, TValue, TMemorySpace>
    where TKey : unmanaged
    where TValue : unmanaged
    where TMemorySpace : IStaticMemorySpace
{
    public RedBlackTree<StdPair<TKey, TValue>, TKey, PairKeyExtractor<TKey, TValue>> Tree;
}
Size: 16 bytes (red-black tree structure)

Access Patterns

// Dictionary-style access (creates entry if missing)
ref var value = ref map[key];

// Safe lookup
if (map.TryGetValue(key, out TValue value, copyCtor: false))
{
    // Use value
}

// Get pointer to value (no copy)
if (map.TryGetValuePointer(key, out TValue* valuePtr))
{
    // Modify *valuePtr directly
}

// Iterate key-value pairs
foreach (ref var pair in map)
{
    TKey key = pair.Item1;
    TValue value = pair.Item2;
}

// Keys and values collections
foreach (ref var key in map.Keys) { }
foreach (ref var value in map.Values) { }

Adding and Removing

// Try add (returns false if key exists)
if (map.TryAddValueKCopyVCopy(in key, in value))
{
    // Added successfully
}

// Remove by key
if (map.Remove(in key))
{
    // Removed successfully
}

// Clear all entries
map.Clear();

StdSet<T, TMemorySpace>

An ordered set container wrapping std::set, implemented as a red-black tree.

Memory Layout

[StructLayout(LayoutKind.Sequential, Size = 0x10)]
public unsafe struct StdSet<T, TMemorySpace>
    where T : unmanaged
    where TMemorySpace : IStaticMemorySpace
{
    public RedBlackTree<T, T, DefaultKeyExtractor<T>> Tree;
}
Size: 16 bytes

Operations

// Add elements (returns false if already exists)
if (set.AddCopy(in value))
{
    // Added successfully
}

// Check membership
if (set.Contains(in value))
{
    // Value exists
}

// Remove
set.Remove(in value);

// Set operations
set.UnionWith(otherSet);
set.IntersectWith(otherSet);
set.ExceptWith(otherSet);
set.SymmetricExceptWith(otherSet);

// Iterate in sorted order
foreach (ref var item in set)
{
    // Items are sorted
}

// Reverse iteration
foreach (ref var item in set.Reverse())
{
    // Descending order
}

StdList<T, TMemorySpace>

A doubly-linked list wrapping std::list.

Memory Layout

[StructLayout(LayoutKind.Sequential, Size = 0x10)]
public unsafe struct StdList<T, TMemorySpace>
    where T : unmanaged
    where TMemorySpace : IStaticMemorySpace
{
    public IStdList<T>.Node* Head;  // Sentinel node
    public ulong Size;              // Number of elements
}
Size: 16 bytes

Node Structure

Each node contains:
  • T Value - The stored value
  • Node* Next - Pointer to next node
  • Node* Previous - Pointer to previous node

Access Patterns

// Add elements
var node = list.AddLastCopy(in value);
var firstNode = list.AddFirstCopy(in value);

// Add after/before specific node
var newNode = list.AddAfterCopy(existingNode, in value);

// Manual node iteration
for (var node = list.First.Value; node != list.Last.Value; node = node->Next)
{
    ref T value = ref node->Value;
}

// Find nodes
var foundNode = list.Find(in value);
if (foundNode.Value != null)
{
    list.Remove(foundNode);
}

// Remove from ends
list.RemoveFirst();
list.RemoveLast();

StdDeque<T>

A double-ended queue wrapping std::deque, implemented as a map of fixed-size blocks.

Memory Layout

[StructLayout(LayoutKind.Sequential, Size = 0x28)]
public unsafe struct StdDeque<T>
    where T : unmanaged
{
    public ContainerProxy<T>* ContainerBase;
    public T** Map;         // Array of pointers to blocks
    public ulong MapSize;   // Size of map
    public ulong MyOff;     // Offset of first element
    public ulong MySize;    // Number of elements
}
Size: 40 bytes

Block Size

Block size varies by element size:
  • 1 byte elements: 16 per block
  • 2 byte elements: 8 per block
  • 4 byte elements: 4 per block
  • 8 byte elements: 2 per block
  • Larger: 1 per block

Access

// Random access (index calculation)
ref var element = ref deque[index];

// Iteration
foreach (ref var item in deque)
{
    // Access items
}

// Convert to array
T[] array = deque.ToArray();
StdDeque is less commonly used in game structures. Most mutable collections use StdVector.

StdPair<T1, T2>

A value tuple wrapper for std::pair.

Memory Layout

[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct StdPair<T1, T2>
    where T1 : unmanaged
    where T2 : unmanaged
{
    public T1 Item1;
    public T2 Item2;
}
Size: Varies (sum of T1 and T2 sizes, 8-byte aligned)

Usage

// Create
var pair = new StdPair<int, float>(42, 3.14f);

// Access
int key = pair.Item1;
float value = pair.Item2;

// Deconstruct
var (key, value) = pair;

// Convert to/from KeyValuePair
KeyValuePair<int, float> kvp = pair;
StdPair<int, float> pair2 = kvp;

// Used in StdMap
StdMap<int, MyStruct, IStaticMemorySpace.Default> map;
foreach (ref var pair in map)
{
    int key = pair.Item1;
    MyStruct value = pair.Item2;
}

Memory Management

All STD containers use the specified TMemorySpace for allocations:
// Default game allocator
StdVector<int, IStaticMemorySpace.Default> gameVector;

// Specific allocator types
StdVector<MyStruct, IStaticMemorySpace.ApricotUserSpace> customVector;

Disposal

Containers must be disposed to free native memory:
vector.Dispose();  // Frees all elements and capacity
map.Dispose();     // Frees all tree nodes
list.Dispose();    // Frees all list nodes
Never dispose containers that are part of game structures. Only dispose containers you create.

Common Patterns

Safe Enumeration

// Make defensive copy if modifying during enumeration
var items = vector.ToArray();
foreach (var item in items)
{
    // Safe to modify vector here
}

Null Checks for Pointers

StdVector<Pointer<MyStruct>, IStaticMemorySpace.Default> pointers;

for (int i = 0; i < pointers.Count; i++)
{
    var ptr = pointers[i].Value;
    if (ptr == null) continue;
    
    // Use ptr safely
}

Capacity Pre-allocation

// Avoid reallocations
vector.EnsureCapacity(expectedCount);
for (int i = 0; i < expectedCount; i++)
{
    vector.AddCopy(items[i]);
}

See Also

Build docs developers (and LLMs) love