seq-arrays-openarrays-slicing-views

star 493

Nim seq, arrays, openarray, slicing, and views best practices for zero-allocation cryptographic code. Use when working with buffers, slicing data, or managing memory in Constantine to avoid heap allocations and side-channels.

mratsim By mratsim schedule Updated 4/27/2026

name: seq-arrays-openarrays-slicing-views description: Nim seq, arrays, openarray, slicing, and views best practices for zero-allocation cryptographic code. Use when working with buffers, slicing data, or managing memory in Constantine to avoid heap allocations and side-channels. license: MIT metadata: audience: developers language: nim

What I do

Provide guidance on working with dynamically-sized buffers in Nim for cryptographic code.

Emphasizes avoiding seq in favor of auditable memory management through Constantine's shim over the system allocator.

Avoid Nim slicing syntax ..< slice syntax on arrays, sequences and openarrays as it creates an intermediate seq, which:

  • Causes an avoidable heap allocation
  • Allocates using Nim allocator
  • Violates no-alloc conventions in cryptographic code that handle secrets
  • Can trigger side-channel vulnerabilities

Why No seq?

We actively avoid memory allocation for any protocol that:

  • handle secrets
  • could be expected to run in a trusted enclave or embedded devices

This includes encryption, hashing and signatures protocols.

When memory allocation is necessary, for example for multithreading, GPU computing or succinct or zero-knowledge proof protocol, we use custom allocators from constantine/platforms/allocs.nim. Those are thin wrappers around the OS malloc/free with effect tracking {.tags:[HeapAlloc].} or {.tags:[Alloca].}. Then we can use Nim's effect tracking {.tags:[].} to ensure no heap allocation or alloca is used in the call stack of specific functions.

# Compiler tracked heap allocation
let ptr = allocHeapArrayAligned(int, 128, alignment = 64)

# Compiler tracked stack allocation
let stackPtr = allocStackArray(int, 128)

# Don't do this in cryptographic code!
let bad = @[1, 2, 3]  # seq - hidden allocation!

Array/View Types Overview

array[T, U]

  • Fixed size, stack-allocated
  • Safe for cryptographic constants
  • Use sizeof to compute total size for heap allocation

openArray[T]

  • A virtual type (ptr + length) passed by value
  • Cannot be stored in types or returned from functions
  • Slicing with [a ..< b] creates an intermediate seq (heap allocation!)
  • Use .toOpenArray(start, stopInclusive) instead

ptr UncheckedArray[T]

  • Raw pointer to contiguous memory
  • Can be stored in types
  • Use cast[ptr UncheckedArray[T]](addr) or .asUnchecked() to convert

View[T]

  • A Nim type storing (ptr + length)
  • Can be stored in types or returned from functions
  • Convert to openArray via .toOpenArray template

StridedView[T]

  • For non-contiguous data (e.g., FFT even/odd splitting)
  • Stores: data ptr, length, stride, offset

Converting Between Types

openArray to ptr UncheckedArray

# PREFERRED: Using views.nim (import constantine/platforms/views)
let ptrArr = oa.asUnchecked()

# Alternative: Using cast
let ptrArr = cast[ptr UncheckedArray[T]](oa[0].unsafeAddr)

ptr UncheckedArray to openArray

# Using system.nim (start, stopInclusive) - default
let oa = toOpenArray(ptrArr, start, stopInclusive)

# Using views.nim (ptr, length) - convenience template
let oa = toOpenArray(ptrArr, length)
# Or simply: ptrArr.toOpenArray(len)

openArray to View

let v = toView(oa)
# Or: View[T](data: cast[ptr UncheckedArray[T]](oa[0].unsafeAddr), len: oa.len)

Slicing Without seq Creation

NEVER use slice syntax like array[0 ..< len] on openArray parameters - this creates a seq (heap allocation).

Instead use:

# Bad - creates seq!
process(data[0 ..< count])

# Good - no allocation (system.nim uses stopInclusive)
process(data.toOpenArray(0, count-1))

ptr UncheckedArray should use the ptr+len syntax from import constantine/platforms/views

# Good - using views.nim convenience (ptr, length)
process(myDataPtr.toOpenArray(count))

Constantine Allocator

Constantine provides tracked memory management in constantine/platforms/allocs.nim:

Stack Allocation

template allocStack*(T: typedesc): ptr T
template allocStackUnchecked*(T: typedesc, size: int): ptr T
template allocStackArray*(T: typedesc, len: SomeInteger): ptr UncheckedArray[T]

Heap Allocation

# Standard allocation (uninitialized memory)
proc allocHeap*(T: typedesc): ptr T
proc allocHeapUnchecked*(T: typedesc, size: int): ptr T
proc allocHeapArray*(T: typedesc, len: SomeInteger): ptr UncheckedArray[T]
proc allocHeapAligned*(T: typedesc, alignment: static Natural): ptr T
proc allocHeapArrayAligned*(T: typedesc, len: int, alignment: static Natural): ptr UncheckedArray[T]
proc allocHeapAlignedPtr*(T: typedesc[ptr], alignment: static Natural): T
proc allocHeapUncheckedAlignedPtr*(T: typedesc[ptr], size: int, alignment: static Natural): T

# Zero-initialized allocation (critical for ARC with custom =destroy procs)
proc alloc0Heap*(T: typedesc): ptr T
proc alloc0HeapUnchecked*(T: typedesc, size: int): ptr T
proc alloc0HeapArray*(T: typedesc, len: SomeInteger): ptr UncheckedArray[T]
proc alloc0HeapAligned*(T: typedesc, alignment: static Natural): ptr T
proc alloc0HeapArrayAligned*(T: typedesc, len: int, alignment: static Natural): ptr UncheckedArray[T]
proc alloc0HeapAlignedPtr*(T: typedesc[ptr], alignment: static Natural): T
proc alloc0HeapUncheckedAlignedPtr*(T: typedesc[ptr], size: int, alignment: static Natural): T

Important: Use alloc0* variants when:

  • Allocating structs with custom =destroy procs that check for nil pointers
  • Working with ARC memory management to avoid double-free on uninitialized memory
  • Example: EthereumKZGContext has ECFFT_Descriptor fields with =destroy that free memory

varargs

Nim varargs can accept:

  • Arrays: foo([1, 2, 3])
  • Seqs: foo(@[1, 2, 3]) - avoid in crypto code
  • OpenArray: foo(someOpenArray)
  • Direct args: foo(1, 2, 3)

When to use me

  • Working with Constantine library code
  • When slicing buffers
  • When needing dynamic memory management
Install via CLI
npx skills add https://github.com/mratsim/constantine --skill seq-arrays-openarrays-slicing-views
Repository Details
star Stars 493
call_split Forks 63
navigation Branch main
article Path SKILL.md
More from Creator