package core:mem

⌘K
Ctrl+K
or
/

    Overview

    package mem implements various types of allocators.

    An example of how to use the Tracking_Allocator to track subsequent allocations in your program and report leaks and bad frees:

    `odin package foo

    import "core:mem" import "core:fmt"

    _main :: proc() {

    do stuff
    

    }

    main :: proc() {

    track: mem.Tracking_Allocator
    mem.tracking_allocator_init(&track, context.allocator)
    defer mem.tracking_allocator_destroy(&track)
    context.allocator = mem.tracking_allocator(&track)
    
    _main()
    
    for _, leak in track.allocation_map {
    	fmt.printf("%v leaked %m\n", leak.location, leak.size)
    }
    for bad_free in track.bad_free_array {
    	fmt.printf("%v allocation %p was freed badly\n", bad_free.location, bad_free.memory)
    }
    

    } `

    Index

    Variables (0)

    This section is empty.

    Procedures (104)
    Procedure Groups (2)

    Types

    Allocator ¶

    Allocator :: runtime.Allocator

    Allocator_Error ¶

    Allocator_Error :: runtime.Allocator_Error

    Allocator_Mode ¶

    Allocator_Mode :: runtime.Allocator_Mode
     

    NOTE(bill, 2019-12-31): These are defined in package runtime as they are used in the context. This is to prevent an import definition cycle.

    Allocator_Mode_Set ¶

    Allocator_Mode_Set :: runtime.Allocator_Mode_Set

    Allocator_Proc ¶

    Allocator_Proc :: runtime.Allocator_Proc
     

    Allocator_Error :: enum byte {

    None                 = 0,
    Out_Of_Memory        = 1,
    Invalid_Pointer      = 2,
    Invalid_Argument     = 3,
    Mode_Not_Implemented = 4,
    

    }

    Allocator_Query_Info ¶

    Allocator_Query_Info :: runtime.Allocator_Query_Info

    Arena ¶

    Arena :: struct {
    	data:       []u8,
    	offset:     int,
    	peak_used:  int,
    	temp_count: int,
    }
    Related Procedures With Parameters

    Arena_Temp_Memory ¶

    Arena_Temp_Memory :: struct {
    	arena:       ^Arena,
    	prev_offset: int,
    }
    Related Procedures With Parameters
    Related Procedures With Returns

    Dynamic_Pool ¶

    Dynamic_Pool :: struct {
    	block_size:           int,
    	out_band_size:        int,
    	alignment:            int,
    	unused_blocks:        [dynamic]rawptr,
    	used_blocks:          [dynamic]rawptr,
    	out_band_allocations: [dynamic]rawptr,
    	current_block:        rawptr,
    	current_pos:          rawptr,
    	bytes_left:           int,
    	block_allocator:      runtime.Allocator,
    }
    Related Procedures With Parameters

    Fixed_Byte_Buffer ¶

    Fixed_Byte_Buffer :: distinct [dynamic]u8
    Related Procedures With Returns

    Raw_Any ¶

    Raw_Any :: runtime.Raw_Any

    Raw_Complex128 ¶

    Raw_Complex128 :: struct {
    	real: f64,
    	imag: f64,
    }

    Raw_Complex64 ¶

    Raw_Complex64 :: struct {
    	real: f32,
    	imag: f32,
    }

    Raw_Cstring ¶

    Raw_Cstring :: runtime.Raw_Cstring

    Raw_Dynamic_Array ¶

    Raw_Dynamic_Array :: runtime.Raw_Dynamic_Array

    Raw_Map ¶

    Raw_Map :: runtime.Raw_Map

    Raw_Quaternion128 ¶

    Raw_Quaternion128 :: struct {
    	imag: f32,
    	jmag: f32,
    	kmag: f32,
    	real: f32,
    }

    Raw_Quaternion128_Vector_Scalar ¶

    Raw_Quaternion128_Vector_Scalar :: struct {
    	vector: [3]f32,
    	scalar: f32,
    }

    Raw_Quaternion256 ¶

    Raw_Quaternion256 :: struct {
    	imag: f64,
    	jmag: f64,
    	kmag: f64,
    	real: f64,
    }

    Raw_Quaternion256_Vector_Scalar ¶

    Raw_Quaternion256_Vector_Scalar :: struct {
    	vector: [3]f64,
    	scalar: f64,
    }

    Raw_Slice ¶

    Raw_Slice :: runtime.Raw_Slice

    Raw_Soa_Pointer ¶

    Raw_Soa_Pointer :: runtime.Raw_Soa_Pointer

    Raw_String ¶

    Raw_String :: runtime.Raw_String

    Scratch_Allocator ¶

    Scratch_Allocator :: struct {
    	data:               []u8,
    	curr_offset:        int,
    	prev_allocation:    rawptr,
    	backup_allocator:   runtime.Allocator,
    	leaked_allocations: [dynamic][]u8,
    }
    Related Procedures With Parameters

    Small_Stack ¶

    Small_Stack :: struct {
    	data:      []u8,
    	offset:    int,
    	peak_used: int,
    }
     

    Small_Stack is a stack-like allocator which uses the smallest possible header but at the cost of non-strict memory freeing order

    Related Procedures With Parameters

    Small_Stack_Allocation_Header ¶

    Small_Stack_Allocation_Header :: struct {
    	padding: u8,
    }

    Stack ¶

    Stack :: struct {
    	data:        []u8,
    	prev_offset: int,
    	curr_offset: int,
    	peak_used:   int,
    }
     

    Stack is a stack-like allocator which has a strict memory freeing order

    Related Procedures With Parameters

    Stack_Allocation_Header ¶

    Stack_Allocation_Header :: struct {
    	prev_offset: int,
    	padding:     int,
    }

    Tracking_Allocator ¶

    Tracking_Allocator :: struct {
    	backing:           runtime.Allocator,
    	allocation_map:    map[rawptr]Tracking_Allocator_Entry,
    	bad_free_array:    [dynamic]Tracking_Allocator_Bad_Free_Entry,
    	mutex:             sync.Mutex,
    	clear_on_free_all: bool,
    }
    Related Procedures With Parameters

    Tracking_Allocator_Bad_Free_Entry ¶

    Tracking_Allocator_Bad_Free_Entry :: struct {
    	memory:   rawptr,
    	location: runtime.Source_Code_Location,
    }

    Tracking_Allocator_Entry ¶

    Tracking_Allocator_Entry :: struct {
    	memory:    rawptr,
    	size:      int,
    	alignment: int,
    	mode:      runtime.Allocator_Mode,
    	err:       runtime.Allocator_Error,
    	location:  runtime.Source_Code_Location,
    }

    Constants

    Byte ¶

    Byte :: runtime.Byte

    DEFAULT_ALIGNMENT ¶

    DEFAULT_ALIGNMENT :: 2 * align_of(rawptr)

    DEFAULT_PAGE_SIZE ¶

    DEFAULT_PAGE_SIZE :: 64 * 1024 when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 else 16 * 1024 when ODIN_OS == .Darwin && ODIN_ARCH == .arm64 else 4 * 1024

    DYNAMIC_POOL_BLOCK_SIZE_DEFAULT ¶

    DYNAMIC_POOL_BLOCK_SIZE_DEFAULT :: 65536

    DYNAMIC_POOL_OUT_OF_BAND_SIZE_DEFAULT ¶

    DYNAMIC_POOL_OUT_OF_BAND_SIZE_DEFAULT :: 6554

    Exabyte ¶

    Exabyte :: runtime.Exabyte

    Gigabyte ¶

    Gigabyte :: runtime.Gigabyte

    Kilobyte ¶

    Kilobyte :: runtime.Kilobyte

    Megabyte ¶

    Megabyte :: runtime.Megabyte

    Petabyte ¶

    Petabyte :: runtime.Petabyte

    Terabyte ¶

    Terabyte :: runtime.Terabyte

    Variables

    This section is empty.

    Procedures

    align_backward ¶

    align_backward :: proc(ptr: rawptr, align: uintptr) -> rawptr {…}

    align_backward_int ¶

    align_backward_int :: proc(ptr, align: int) -> int {…}

    align_backward_uint ¶

    align_backward_uint :: proc(ptr, align: uint) -> uint {…}

    align_backward_uintptr ¶

    align_backward_uintptr :: proc(ptr, align: uintptr) -> uintptr {…}

    align_formula ¶

    align_formula :: proc "contextless" (size, align: int) -> int {…}

    align_forward ¶

    align_forward :: proc(ptr: rawptr, align: uintptr) -> rawptr {…}

    align_forward_int ¶

    align_forward_int :: proc(ptr, align: int) -> int {…}

    align_forward_uint ¶

    align_forward_uint :: proc(ptr, align: uint) -> uint {…}

    align_forward_uintptr ¶

    align_forward_uintptr :: proc(ptr, align: uintptr) -> uintptr {…}

    alloc ¶

    alloc :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (rawptr, runtime.Allocator_Error) {…}

    alloc_bytes ¶

    alloc_bytes :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]u8, runtime.Allocator_Error) {…}

    alloc_bytes_non_zeroed ¶

    alloc_bytes_non_zeroed :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]u8, runtime.Allocator_Error) {…}

    any_to_bytes ¶

    any_to_bytes :: proc "contextless" (val: any) -> []u8 {…}

    arena_allocator ¶

    arena_allocator :: proc(arena: ^Arena) -> runtime.Allocator {…}

    arena_allocator_proc ¶

    arena_allocator_proc :: proc(
    	allocator_data:  rawptr, 
    	mode:            runtime.Allocator_Mode, 
    	size, alignment: int, 
    	old_memory:      rawptr, 
    	old_size:        int, 
    	location := #caller_location, 
    ) -> ([]u8, runtime.Allocator_Error) {…}

    arena_init ¶

    arena_init :: proc(a: ^Arena, data: []u8) {…}

    begin_arena_temp_memory ¶

    begin_arena_temp_memory :: proc(a: ^Arena) -> Arena_Temp_Memory {…}

    buffer_from_slice ¶

    buffer_from_slice :: proc "contextless" (backing: $T/[]$T) -> [dynamic]$T {…}

    byte_slice ¶

    byte_slice :: proc "contextless" (data: rawptr, len: int) -> []u8 {…}

    calc_padding_with_header ¶

    calc_padding_with_header :: proc "contextless" (ptr: uintptr, align: uintptr, header_size: int) -> int {…}

    check_zero ¶

    check_zero :: proc(data: []u8) -> bool {…}

    check_zero_ptr ¶

    check_zero_ptr :: proc(ptr: rawptr, len: int) -> bool {…}

    clone_slice ¶

    clone_slice :: proc(slice: $T/[]$T, allocator := context.allocator, loc := #caller_location) -> (new_slice: $T/[]$T) {…}

    compare ¶

    compare :: proc "contextless" (a, b: []u8) -> int {…}

    compare_byte_ptrs ¶

    compare_byte_ptrs :: proc "contextless" (a, b: ^u8, n: int) -> int {…}

    compare_ptrs ¶

    compare_ptrs :: proc "contextless" (a, b: rawptr, n: int) -> int {…}

    context_from_allocator ¶

    context_from_allocator :: proc(a: runtime.Allocator) -> runtime.Context {…}

    copy ¶

    copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {…}

    copy_non_overlapping ¶

    copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {…}

    default_resize_align ¶

    default_resize_align :: proc(
    	old_memory:                    rawptr, 
    	old_size, new_size, alignment: int, 
    	allocator := context.allocator, 
    	loc := #caller_location, 
    ) -> (res: rawptr, err: runtime.Allocator_Error) {…}

    default_resize_bytes_align ¶

    default_resize_bytes_align :: proc(old_data: []u8, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]u8, runtime.Allocator_Error) {…}

    default_resize_bytes_align_non_zeroed ¶

    default_resize_bytes_align_non_zeroed :: proc(old_data: []u8, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]u8, runtime.Allocator_Error) {…}

    delete_cstring ¶

    delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) -> runtime.Allocator_Error {…}

    delete_dynamic_array ¶

    delete_dynamic_array :: proc(array: $T/[dynamic]$T, loc := #caller_location) -> runtime.Allocator_Error {…}

    delete_map ¶

    delete_map :: proc(m: $T/map[$T]$T, loc := #caller_location) -> runtime.Allocator_Error {…}

    delete_slice ¶

    delete_slice :: proc(array: $T/[]$T, allocator := context.allocator, loc := #caller_location) -> runtime.Allocator_Error {…}

    delete_string ¶

    delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) -> runtime.Allocator_Error {…}

    dynamic_pool_alloc ¶

    dynamic_pool_alloc :: proc(pool: ^Dynamic_Pool, bytes: int) -> (rawptr, runtime.Allocator_Error) {…}

    dynamic_pool_alloc_bytes ¶

    dynamic_pool_alloc_bytes :: proc(p: ^Dynamic_Pool, bytes: int) -> ([]u8, runtime.Allocator_Error) {…}

    dynamic_pool_allocator ¶

    dynamic_pool_allocator :: proc(pool: ^Dynamic_Pool) -> runtime.Allocator {…}

    dynamic_pool_allocator_proc ¶

    dynamic_pool_allocator_proc :: proc(
    	allocator_data:  rawptr, 
    	mode:            runtime.Allocator_Mode, 
    	size, alignment: int, 
    	old_memory:      rawptr, 
    	old_size:        int, 
    	loc := #caller_location, 
    ) -> ([]u8, runtime.Allocator_Error) {…}

    dynamic_pool_destroy ¶

    dynamic_pool_destroy :: proc(pool: ^Dynamic_Pool) {…}

    dynamic_pool_free_all ¶

    dynamic_pool_free_all :: proc(p: ^Dynamic_Pool) {…}

    dynamic_pool_init ¶

    dynamic_pool_init :: proc(
    	pool:            ^Dynamic_Pool, 
    	block_allocator := context.allocator, 
    	array_allocator := context.allocator, 
    	block_size:      int = DYNAMIC_POOL_BLOCK_SIZE_DEFAULT, 
    	out_band_size:   int = DYNAMIC_POOL_OUT_OF_BAND_SIZE_DEFAULT, 
    	alignment:       int = 8, 
    ) {…}

    dynamic_pool_reset ¶

    dynamic_pool_reset :: proc(p: ^Dynamic_Pool) {…}

    end_arena_temp_memory ¶

    end_arena_temp_memory :: proc(tmp: Arena_Temp_Memory) {…}

    free ¶

    free :: proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> runtime.Allocator_Error {…}

    free_all ¶

    free_all :: proc(allocator := context.allocator, loc := #caller_location) -> runtime.Allocator_Error {…}

    free_bytes ¶

    free_bytes :: proc(bytes: []u8, allocator := context.allocator, loc := #caller_location) -> runtime.Allocator_Error {…}

    free_with_size ¶

    free_with_size :: proc(ptr: rawptr, byte_count: int, allocator := context.allocator, loc := #caller_location) -> runtime.Allocator_Error {…}

    init_arena ¶

    init_arena :: proc(a: ^Arena, data: []u8) {…}

    init_small_stack ¶

    init_small_stack :: proc(s: ^Small_Stack, data: []u8) {…}

    init_stack ¶

    init_stack :: proc(s: ^Stack, data: []u8) {…}

    is_power_of_two ¶

    is_power_of_two :: proc "contextless" (x: uintptr) -> bool {…}

    make_aligned ¶

    make_aligned :: proc($T: typeid/[]T, #any_int len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (slice: T, err: runtime.Allocator_Error) {…}

    make_any ¶

    make_any :: proc "contextless" (data: rawptr, id: typeid) -> any {…}

    make_dynamic_array ¶

    make_dynamic_array :: proc($T: typeid/[dynamic]T, allocator := context.allocator, loc := #caller_location) -> (T, runtime.Allocator_Error) {…}

    make_dynamic_array_len ¶

    make_dynamic_array_len :: proc($T: typeid/[dynamic]T, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, runtime.Allocator_Error) {…}

    make_dynamic_array_len_cap ¶

    make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]T, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: runtime.Allocator_Error) {…}

    make_fixed_byte_buffer ¶

    make_fixed_byte_buffer :: proc "contextless" (backing: []u8) -> Fixed_Byte_Buffer {…}

    make_map ¶

    make_map :: proc($T: typeid/map[T]T, #any_int cap: int = 1 << runtime.MAP_MIN_LOG2_CAPACITY, allocator := context.allocator, loc := #caller_location) -> (m: T, err: runtime.Allocator_Error) {…}

    make_multi_pointer ¶

    make_multi_pointer :: proc($T: typeid/[^]T, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (mp: $/[^]T, err: runtime.Allocator_Error) {…}

    make_slice ¶

    make_slice :: proc($T: typeid/[]T, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, runtime.Allocator_Error) {…}

    new ¶

    new :: proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> (^typeid, runtime.Allocator_Error) {…}

    new_aligned ¶

    new_aligned :: proc($T: typeid, alignment: int, allocator := context.allocator, loc := #caller_location) -> (t: ^typeid, err: runtime.Allocator_Error) {…}

    new_clone ¶

    new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_location) -> (t: ^$T, err: runtime.Allocator_Error) {…}

    nil_allocator ¶

    nil_allocator :: proc() -> runtime.Allocator {…}

    nil_allocator_proc ¶

    nil_allocator_proc :: proc(
    	allocator_data:  rawptr, 
    	mode:            runtime.Allocator_Mode, 
    	size, alignment: int, 
    	old_memory:      rawptr, 
    	old_size:        int, 
    	loc := #caller_location, 
    ) -> ([]u8, runtime.Allocator_Error) {…}

    panic_allocator ¶

    panic_allocator :: proc() -> runtime.Allocator {…}

    panic_allocator_proc ¶

    panic_allocator_proc :: proc(
    	allocator_data:  rawptr, 
    	mode:            runtime.Allocator_Mode, 
    	size, alignment: int, 
    	old_memory:      rawptr, 
    	old_size:        int, 
    	loc := #caller_location, 
    ) -> ([]u8, runtime.Allocator_Error) {…}

    ptr_offset ¶

    ptr_offset :: intrinsics.ptr_offset

    ptr_sub ¶

    ptr_sub :: intrinsics.ptr_sub

    ptr_to_bytes ¶

    ptr_to_bytes :: proc "contextless" (ptr: ^$T, len: int) -> []u8 {…}

    query_features ¶

    query_features :: proc(allocator := context.allocator, loc := #caller_location) -> (set: runtime.Allocator_Mode_Set) {…}

    query_info ¶

    query_info :: proc(pointer: rawptr, allocator: runtime.Allocator, loc := #caller_location) -> (props: runtime.Allocator_Query_Info) {…}

    raw_data ¶

    raw_data :: builtin.raw_data

    reinterpret_copy ¶

    reinterpret_copy :: proc "contextless" ($T: typeid, ptr: rawptr) -> (value: typeid) {…}

    resize ¶

    resize :: proc(
    	ptr:                rawptr, 
    	old_size, new_size: int, 
    	alignment:          int = DEFAULT_ALIGNMENT, 
    	allocator := context.allocator, 
    	loc := #caller_location, 
    ) -> (rawptr, runtime.Allocator_Error) {…}

    resize_bytes ¶

    resize_bytes :: proc(old_data: []u8, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]u8, runtime.Allocator_Error) {…}

    scratch_allocator ¶

    scratch_allocator :: proc(allocator: ^Scratch_Allocator) -> runtime.Allocator {…}

    scratch_allocator_destroy ¶

    scratch_allocator_destroy :: proc(s: ^Scratch_Allocator) {…}

    scratch_allocator_init ¶

    scratch_allocator_init :: proc(s: ^Scratch_Allocator, size: int, backup_allocator := context.allocator) -> runtime.Allocator_Error {…}

    scratch_allocator_proc ¶

    scratch_allocator_proc :: proc(
    	allocator_data:  rawptr, 
    	mode:            runtime.Allocator_Mode, 
    	size, alignment: int, 
    	old_memory:      rawptr, 
    	old_size:        int, 
    	loc := #caller_location, 
    ) -> ([]u8, runtime.Allocator_Error) {…}

    set ¶

    set :: proc "contextless" (data: rawptr, value: u8, len: int) -> rawptr {…}

    simple_equal ¶

    simple_equal :: proc "contextless" (a, b: $T) -> bool {…}

    slice_data_cast ¶

    slice_data_cast :: proc "contextless" ($T: typeid/[]T, slice: T) -> T {…}

    slice_ptr ¶

    slice_ptr :: proc "contextless" (ptr: ^$T, len: int) -> []$T {…}

    slice_to_bytes ¶

    slice_to_bytes :: proc "contextless" (slice: $T/[]$T) -> []u8 {…}

    slice_to_components ¶

    slice_to_components :: proc "contextless" (slice: $T/[]$T) -> (data: ^$T, len: int) {…}

    small_stack_allocator ¶

    small_stack_allocator :: proc(stack: ^Small_Stack) -> runtime.Allocator {…}

    small_stack_allocator_proc ¶

    small_stack_allocator_proc :: proc(
    	allocator_data:  rawptr, 
    	mode:            runtime.Allocator_Mode, 
    	size, alignment: int, 
    	old_memory:      rawptr, 
    	old_size:        int, 
    	location := #caller_location, 
    ) -> ([]u8, runtime.Allocator_Error) {…}

    small_stack_init ¶

    small_stack_init :: proc(s: ^Small_Stack, data: []u8) {…}

    stack_allocator ¶

    stack_allocator :: proc(stack: ^Stack) -> runtime.Allocator {…}

    stack_allocator_proc ¶

    stack_allocator_proc :: proc(
    	allocator_data:  rawptr, 
    	mode:            runtime.Allocator_Mode, 
    	size, alignment: int, 
    	old_memory:      rawptr, 
    	old_size:        int, 
    	location := #caller_location, 
    ) -> ([]u8, runtime.Allocator_Error) {…}

    stack_init ¶

    stack_init :: proc(s: ^Stack, data: []u8) {…}

    tracking_allocator ¶

    tracking_allocator :: proc(data: ^Tracking_Allocator) -> runtime.Allocator {…}

    tracking_allocator_clear ¶

    tracking_allocator_clear :: proc(t: ^Tracking_Allocator) {…}

    tracking_allocator_destroy ¶

    tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) {…}

    tracking_allocator_init ¶

    tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: runtime.Allocator, internals_allocator := context.allocator) {…}

    tracking_allocator_proc ¶

    tracking_allocator_proc :: proc(
    	allocator_data:  rawptr, 
    	mode:            runtime.Allocator_Mode, 
    	size, alignment: int, 
    	old_memory:      rawptr, 
    	old_size:        int, 
    	loc := #caller_location, 
    ) -> (result: []u8, err: runtime.Allocator_Error) {…}

    zero ¶

    zero :: proc "contextless" (data: rawptr, len: int) -> rawptr {…}

    zero_explicit ¶

    zero_explicit :: proc "contextless" (data: rawptr, len: int) -> rawptr {…}

    zero_item ¶

    zero_item :: proc "contextless" (item: $P/^$T) -> $P/^$T {…}

    zero_slice ¶

    zero_slice :: proc "contextless" (data: $T/[]$T) -> $T/[]$T {…}

    Procedure Groups

    Source Files

    Generation Information

    Generated with odin version dev-2024-03 (vendor "odin") Windows_amd64 @ 2024-03-01 21:10:24.310877400 +0000 UTC