Got logging setup

There is an issue with the tracked allocators made for the host module.
I'll need to see later whats going on, for now it doesn't matter.
This commit is contained in:
Edward R. Gonzalez 2024-02-08 22:33:53 -05:00
parent d205aba15a
commit 6819336696
10 changed files with 272 additions and 123 deletions

3
.gitignore vendored
View File

@ -1,4 +1,5 @@
build/**
*.exe
thirdparty/**
~thirdparty/odin/core/**
~thirdparty/odin/core/**
logs

View File

@ -27,8 +27,11 @@ ModuleAPI :: struct {
}
@export
startup :: proc( live_mem : virtual.Arena, snapshot_mem : []u8 )
startup :: proc( live_mem : virtual.Arena, snapshot_mem : []u8, host_logger : ^ Logger )
{
init( & memory.logger, "Sectr", host_logger.file_path, host_logger.file )
context.logger = to_odin_logger( & memory.logger )
// Setup memory for the first time
{
arena_size :: size_of( mem.Arena)
@ -53,6 +56,7 @@ startup :: proc( live_mem : virtual.Arena, snapshot_mem : []u8 )
context.allocator = tracked_allocator( transient )
context.temp_allocator = tracked_allocator( temp )
}
state := new( State, tracked_allocator( memory.persistent ) )
using state
@ -64,12 +68,13 @@ startup :: proc( live_mem : virtual.Arena, snapshot_mem : []u8 )
screen_height = 1000
win_title : cstring = "Sectr Prototype"
rl.InitWindow( screen_width, screen_height, win_title )
log( "Raylib initialized and window opened" )
// Determining current monitor and setting the target frametime based on it..
monitor_id = rl.GetCurrentMonitor ()
monitor_refresh_hz = rl.GetMonitorRefreshRate( monitor_id )
rl.SetTargetFPS( monitor_refresh_hz )
fmt.println( "Set target FPS to: %v", monitor_refresh_hz )
log( fmt.tprintf( "Set target FPS to: %v", monitor_refresh_hz ) )
// Basic Font Setup
{
@ -80,6 +85,7 @@ startup :: proc( live_mem : virtual.Arena, snapshot_mem : []u8 )
rl.GuiSetFont( font_rec_mono_semicasual_reg ) // TODO(Ed) : Does this do anything?
default_font = font_rec_mono_semicasual_reg
log( "Default font loaded" )
}
}
@ -102,10 +108,11 @@ sectr_shutdown :: proc()
rl.UnloadFont ( state.font_rec_mono_semicasual_reg )
rl.CloseWindow()
}
log("Module shutdown complete")
}
@export
reload :: proc( live_mem : virtual.Arena, snapshot_mem : []u8 )
reload :: proc( live_mem : virtual.Arena, snapshot_mem : []u8, host_logger : ^ Logger )
{
using memory;
block := live_mem.curr_block
@ -120,6 +127,8 @@ reload :: proc( live_mem : virtual.Arena, snapshot_mem : []u8 )
persistent = cast( ^TrackedAllocator ) & persistent_slice[0]
transient = cast( ^TrackedAllocator ) & transient_slice[0]
temp = cast( ^TrackedAllocator ) & temp_slice[0]
log("Module reloaded")
}
// TODO(Ed) : This lang really not have a fucking swap?
@ -152,32 +161,26 @@ update :: proc() -> b32
}
}}
DO_NOT_CONTINUE : b32 = false
if debug_actions.play_replay { switch replay.mode
{
case ReplayMode.Off : {
if ! file_exists( Path_Input_Replay ) {
save_snapshot( & memory.snapshot[0] )
replay_recording_begin( Path_Input_Replay )
break
}
else {
load_snapshot( & memory.snapshot[0] )
replay_playback_begin( Path_Input_Replay )
break
}
}
case ReplayMode.Playback : {
replay_playback_end()
load_snapshot( & memory.snapshot[0] )
break
}
case ReplayMode.Record : {
replay_recording_end()
load_snapshot( & memory.snapshot[0] )
replay_playback_begin( Path_Input_Replay )
break
}
}}

31
code/assert.odin Normal file
View File

@ -0,0 +1,31 @@
package sectr
import "base:runtime"
import "core:os"
ensure :: proc( condition : b32, msg : string, location := #caller_location )
{
if ! condition {
return
}
log( msg, LogLevel.Warning, location )
runtime.debug_trap()
}
// TODO(Ed) : Setup exit codes!
fatal :: proc ( msg : string, exit_code : int = -1, location := #caller_location )
{
log( msg, LogLevel.Fatal, location )
runtime.debug_trap()
os.exit( exit_code )
}
verify :: proc ( condition : b32, msg : string, exit_code : int = -1, location := #caller_location )
{
if ! condition {
return
}
log( msg, LogLevel.Fatal, location )
runtime.debug_trap()
os.exit( exit_code )
}

View File

@ -21,7 +21,8 @@ Memory :: struct {
transient : ^ TrackedAllocator,
temp : ^ TrackedAllocator,
replay : ReplayState
replay : ReplayState,
logger : Logger,
}
State :: struct {
@ -93,6 +94,11 @@ poll_debug_actions :: proc( actions : ^ DebugActions, input : ^ InputState )
}
save_snapshot :: proc( snapshot : [^]u8 ) {
state := get_state()
// state.font_rec_mono_semicasual_reg
// state.default_font
live_ptr := cast( ^ rawptr ) memory.live.curr_block.base
mem.copy_non_overlapping( & snapshot[0], live_ptr, memory_chunk_size )
}
@ -118,25 +124,12 @@ replay_recording_begin :: proc( path : string )
{
if file_exists( path ) {
result := os.remove( path )
if ( result != os.ERROR_NONE )
{
// TODO(Ed) : Setup a proper logging interface
fmt. printf( "Failed to delete replay file before beginning a new one" )
runtime.debug_trap()
os. exit( -1 )
// TODO(Ed) : Figure out the error code enums..
}
verify( result != os.ERROR_NONE, "Failed to delete replay file before beginning a new one" )
}
replay_file, open_error := os.open( path, os.O_RDWR | os.O_CREATE )
if ( open_error != os.ERROR_NONE )
{
// TODO(Ed) : Setup a proper logging interface
fmt. printf( "Failed to create or open the replay file" )
runtime.debug_trap()
os. exit( -1 )
// TODO(Ed) : Figure out the error code enums..
}
verify( open_error != os.ERROR_NONE, "Failed to create or open the replay file" )
os.seek( replay_file, 0, 0 )
replay := & memory.replay
@ -154,34 +147,20 @@ replay_recording_end :: proc() {
replay_playback_begin :: proc( path : string )
{
if ! file_exists( path )
{
// TODO(Ed) : Setup a proper logging interface
fmt. printf( "Failed to create or open the replay file" )
runtime.debug_trap()
os. exit( -1 )
// TODO(Ed) : Figure out the error code enums..
}
verify( ! file_exists( path ), "Failed to find replay file" )
replay_file, open_error := os.open( path, os.O_RDWR | os.O_CREATE )
if ( open_error != os.ERROR_NONE )
{
// TODO(Ed) : Setup a proper logging interface
fmt. printf( "Failed to create or open the replay file" )
runtime.debug_trap()
os. exit( -1 )
// TODO(Ed) : Figure out the error code enums..
}
// TODO(Ed): WE need to wrap any actions that can throw a fatal like this. Files need a grime wrap.
verify( open_error != os.ERROR_NONE, "Failed to create or open the replay file" )
os.seek( replay_file, 0, 0 )
replay := & memory.replay
replay.active_file = replay_file
replay.mode = ReplayMode.Playback
replay.mode = ReplayMode.Playback
}
replay_playback_end :: proc() {
input := get_state().input
input := get_state().input
replay := & memory.replay
replay.mode = ReplayMode.Off
os.seek( replay.active_file, 0, 0 )

View File

@ -11,7 +11,7 @@ copy_file_sync :: proc( path_src, path_dst: string ) -> b32
{
path_info, result := os.stat( path_src, context.temp_allocator )
if result != os.ERROR_NONE {
fmt.println("Error getting file info: ", result )
logf("Could not get file info: %v", result, LogLevel.Error )
return false
}
file_size = path_info.size
@ -19,14 +19,14 @@ copy_file_sync :: proc( path_src, path_dst: string ) -> b32
src_content, result := os.read_entire_file( path_src, context.temp_allocator )
if ! result {
fmt.println( "Failed to read file to copy" )
logf( "Failed to read file to copy: %v", path_src, LogLevel.Error )
runtime.debug_trap()
return false
}
result = os.write_entire_file( path_dst, src_content, false )
if ! result {
fmt.println( "Failed to copy file")
logf( "Failed to copy file: %v", path_dst, LogLevel.Error )
runtime.debug_trap()
return false
}

View File

@ -15,6 +15,7 @@ import "core:mem/virtual"
Petabyte :: 1024 * Terabyte
Exabyte :: 1024 * Petabyte
import "core:os"
file_resize :: os.ftruncate
import "core:strings"
import "core:time"
import rl "vendor:raylib"
@ -24,9 +25,15 @@ TrackedAllocator :: sectr.TrackedAllocator
tracked_allocator :: sectr.tracked_allocator
tracked_allocator_init :: sectr.tracked_allocator_init
LogLevel :: sectr.LogLevel
log :: sectr.log
fatal :: sectr.fatal
verify :: sectr.verify
path_snapshot :: "VMemChunk_1.snapshot"
when ODIN_OS == runtime.Odin_OS_Type.Windows
{
path_logs :: "../logs"
path_sectr_module :: "sectr.dll"
path_sectr_live_module :: "sectr_live.dll"
path_sectr_debug_symbols :: "sectr.pdb"
@ -65,29 +72,14 @@ setup_memory :: proc () -> VMemChunk
base_address : rawptr = transmute( rawptr) u64(Terabyte * 1)
result := arena_init_static( & sectr_live, base_address, sectr.memory_chunk_size, sectr.memory_chunk_size )
if result != runtime.Allocator_Error.None
{
// TODO(Ed) : Setup a proper logging interface
fmt. printf( "Failed to allocate live memory for the sectr module" )
runtime.debug_trap()
os. exit( -1 )
// TODO(Ed) : Figure out the error code enums..
}
verify( result != runtime.Allocator_Error.None, "Failed to allocate live memory for the sectr module" )
}
// Setup memory mapped io for snapshots
{
file_resize :: os.ftruncate
snapshot_file, open_error := os.open( path_snapshot, os.O_RDWR | os.O_CREATE )
if ( open_error != os.ERROR_NONE )
{
// TODO(Ed) : Setup a proper logging interface
fmt. printf( "Failed to open snapshot file for the sectr module" )
runtime.debug_trap()
os. exit( -1 )
// TODO(Ed) : Figure out the error code enums..
}
verify( open_error != os.ERROR_NONE, "Failed to open snapshot file for the sectr module" )
file_info, stat_code := os.stat( path_snapshot )
{
if file_info.size != sectr.memory_chunk_size {
@ -95,16 +87,10 @@ setup_memory :: proc () -> VMemChunk
}
}
map_error : virtual.Map_File_Error
sectr_snapshot, map_error = virtual.map_file_from_file_descriptor( uintptr(snapshot_file), { virtual.Map_File_Flag.Read, virtual.Map_File_Flag.Write } )
if map_error != virtual.Map_File_Error.None
{
// TODO(Ed) : Setup a proper logging interface
fmt. printf( "Failed to allocate snapshot memory for the sectr module" )
runtime.debug_trap()
os. exit( -1 )
// TODO(Ed) : Figure out the error code enums..
}
map_error : virtual.Map_File_Error
map_flags : virtual.Map_File_Flags = { virtual.Map_File_Flag.Read, virtual.Map_File_Flag.Write }
sectr_snapshot, map_error = virtual.map_file_from_file_descriptor( uintptr(snapshot_file), map_flags )
verify( map_error != virtual.Map_File_Error.None, "Failed to allocate snapshot memory for the sectr module" )
os.close(snapshot_file)
}
@ -112,8 +98,7 @@ setup_memory :: proc () -> VMemChunk
// Reassign default allocators for host
memory.og_allocator = context.allocator
memory.og_temp_allocator = context.temp_allocator
context.allocator = tracked_allocator( & memory.host_persistent )
context.temp_allocator = tracked_allocator( & memory.host_transient )
log("Memory setup")
return memory;
}
@ -121,10 +106,9 @@ load_sectr_api :: proc ( version_id : i32 ) -> sectr.ModuleAPI
{
loaded_module : sectr.ModuleAPI
write_time,
result := os.last_write_time_by_name("sectr.dll")
write_time, result := os.last_write_time_by_name("sectr.dll")
if result != os.ERROR_NONE {
fmt. println("Could not resolve the last write time for sectr.dll")
log( "Could not resolve the last write time for sectr.dll", LogLevel.Warning )
runtime.debug_trap()
return {}
}
@ -134,8 +118,7 @@ load_sectr_api :: proc ( version_id : i32 ) -> sectr.ModuleAPI
lib, load_result := dynlib.load_library( live_file )
if ! load_result {
// TODO(Ed) : Setup a proper logging interface
fmt. println( "Failed to load the sectr module." )
log( "Failed to load the sectr module.", LogLevel.Warning )
runtime.debug_trap()
return {}
}
@ -159,6 +142,7 @@ load_sectr_api :: proc ( version_id : i32 ) -> sectr.ModuleAPI
return {}
}
log("Loaded sectr API")
loaded_module = {
lib = lib,
write_time = write_time,
@ -179,9 +163,10 @@ unload_sectr_api :: proc ( module : ^ sectr.ModuleAPI )
dynlib.unload_library( module.lib )
os.remove( path_sectr_live_module )
module^ = {}
log("Unloaded sectr API")
}
sync_sectr_api :: proc ( sectr_api : ^ sectr.ModuleAPI, memory : ^ VMemChunk )
sync_sectr_api :: proc ( sectr_api : ^ sectr.ModuleAPI, memory : ^ VMemChunk, logger : ^ sectr.Logger )
{
if write_time, result := os.last_write_time_by_name( path_sectr_module );
result == os.ERROR_NONE && sectr_api.write_time != write_time
@ -194,13 +179,9 @@ sync_sectr_api :: proc ( sectr_api : ^ sectr.ModuleAPI, memory : ^ VMemChunk )
time.sleep( time.Millisecond )
sectr_api ^ = load_sectr_api( version_id )
if sectr_api.lib_version == 0 {
fmt.println("Failed to hot-reload the sectr module")
runtime.debug_trap()
os.exit(-1)
// TODO(Ed) : Figure out the error code enums..
}
sectr_api.reload( memory.sectr_live, memory.sectr_snapshot )
verify( sectr_api.lib_version == 0, "Failed to hot-reload the sectr module" )
sectr_api.reload( memory.sectr_live, memory.sectr_snapshot, logger )
}
}
@ -209,6 +190,32 @@ main :: proc()
state : RuntimeState
using state
path_logger_finalized : string
{
startup_time := time.now()
year, month, day := time.date( startup_time)
hour, min, sec := time.clock_from_time( startup_time)
if ! os.is_dir( path_logs ) {
os.make_directory( path_logs )
}
timestamp := fmt.tprintf("%04d-%02d-%02d_%02d-%02d-%02d", year, month, day, hour, min, sec)
path_logger_finalized = strings.clone( fmt.tprintf( "%s/sectr_%v.log", path_logs, timestamp) )
}
logger : sectr.Logger
sectr.init( & logger, "Sectr Host", fmt.tprintf( "%s/sectr.log", path_logs ) )
context.logger = sectr.to_odin_logger( & logger )
{
// Log System Context
backing_builder : [16 * Kilobyte] u8
builder := strings.builder_from_bytes( backing_builder[:] )
fmt.sbprintf( & builder, "Core Count: %v, ", os.processor_core_count() )
fmt.sbprintf( & builder, "Page Size: %v", os.get_page_size() )
sectr.log( strings.to_string(builder) )
}
// Basic Giant VMem Block
{
// By default odin uses a growing arena for the runtime context
@ -218,28 +225,26 @@ main :: proc()
memory = setup_memory()
}
// TODO(Ed): Cannot use the manually created allocators for the host. Not sure why
// context.allocator = tracked_allocator( & memory.host_persistent )
// context.temp_allocator = tracked_allocator( & memory.host_transient )
// Load the Enviornment API for the first-time
{
sectr_api = load_sectr_api( 1 )
if sectr_api.lib_version == 0 {
// TODO(Ed) : Setup a proper logging interface
fmt. println( "Failed to initially load the sectr module" )
runtime.debug_trap()
os. exit( -1 )
// TODO(Ed) : Figure out the error code enums..
}
sectr_api = load_sectr_api( 1 )
verify( sectr_api.lib_version == 0, "Failed to initially load the sectr module" )
}
running = true;
memory = memory
sectr_api = sectr_api
sectr_api.startup( memory.sectr_live, memory.sectr_snapshot )
sectr_api.startup( memory.sectr_live, memory.sectr_snapshot, & logger )
// TODO(Ed) : This should have an end status so that we know the reason the engine stopped.
for ; running ;
{
// Hot-Reload
sync_sectr_api( & sectr_api, & memory )
sync_sectr_api( & sectr_api, & memory, & logger )
running = sectr_api.update()
sectr_api.render()
@ -254,4 +259,8 @@ main :: proc()
sectr_api.shutdown()
unload_sectr_api( & sectr_api )
log("Succesfuly closed")
os.close( logger.file )
os.rename( logger.file_path, path_logger_finalized )
}

View File

@ -2,11 +2,13 @@
// This was made becaause odin didn't expose the base_address param that virtual alloc allows.
package host
import "base:runtime"
import "core:mem"
import "core:mem/virtual"
import win32 "core:sys/windows"
when ODIN_OS == runtime.Odin_OS_Type.Windows {
@(private="file")
virtual_Platform_Memory_Block :: struct {
block: virtual.Memory_Block,
@ -124,3 +126,6 @@ arena_init_static :: proc(arena: ^virtual.Arena, base_address : rawptr,
arena.total_reserved = arena.curr_block.reserved
return
}
// END WINDOWS CHECK WRAP
}

View File

@ -16,7 +16,7 @@ btn_pressed :: proc( btn : DigitalBtn ) -> b32 {
}
pressed :: proc {
btn_pressed
btn_pressed,
}
MaxKeyboardKeys :: 256
@ -286,7 +286,7 @@ poll_input :: proc( old, new : ^ InputState )
input_process_digital_btn :: proc ( old_state, new_state : ^ DigitalBtn, is_down : b32 )
{
new_state.ended_down = is_down
had_transition := old_state.ended_down != new_state.ended_down
had_transition := old_state.ended_down != new_state.ended_down
if had_transition {
new_state.half_transitions += 1
}
@ -477,7 +477,7 @@ to_raylib_key :: proc ( key : i32 ) -> rl.KeyboardKey {
rl.KeyboardKey.KP_SUBTRACT,
rl.KeyboardKey.KP_MULTIPLY,
rl.KeyboardKey.KP_DIVIDE,
rl.KeyboardKey.KP_ENTER
rl.KeyboardKey.KP_ENTER
}
return raylib_key_lookup_table[ key ]
}

125
code/logger.odin Normal file
View File

@ -0,0 +1,125 @@
package sectr
import "base:runtime"
import "core:fmt"
import "core:mem"
import "core:os"
import "core:strings"
import "core:time"
import core_log "core:log"
Max_Logger_Message_Width :: 80
LogLevel :: core_log.Level
Logger :: struct {
file_path : string,
file : os.Handle,
id : string,
}
to_odin_logger :: proc ( logger : ^ Logger ) -> core_log.Logger {
return { logger_interface, logger, core_log.Level.Debug, core_log.Default_File_Logger_Opts }
}
init :: proc ( logger : ^ Logger, id : string, file_path : string, file := os.INVALID_HANDLE )
{
if file == os.INVALID_HANDLE
{
logger_file, result_code := os.open( file_path, os.O_RDWR | os.O_CREATE )
if result_code != os.ERROR_NONE {
// Log failures are fatal and must never occur at runtime (there is no logging)
runtime.debug_trap()
os. exit( -1 )
// TODO(Ed) : Figure out the error code enums..
}
logger.file = logger_file
}
else {
logger.file = file
}
logger.file_path = file_path
logger.id = id
context.logger = { logger_interface, logger, core_log.Level.Debug, core_log.Default_File_Logger_Opts }
log("Initialized Logger")
when false {
log("This sentence is over 80 characters long on purpose to test the ability of this fucking logger to properfly fucking wrap long as fuck logs with a new line and then at the end of that pad it with the appropraite signature.")
}
}
logger_interface :: proc (
logger_data : rawptr,
level : core_log.Level,
text : string,
options : core_log.Options,
location := #caller_location )
{
logger := cast(^ Logger) logger_data
@static builder_backing : [16 * Kilobyte] byte; {
mem.set( raw_data( builder_backing[:] ), 0, len(builder_backing) )
}
builder := strings.builder_from_bytes( builder_backing[:] )
first_line_length := len(text) > Max_Logger_Message_Width ? Max_Logger_Message_Width : len(text)
first_line := transmute(string) text[ 0 : first_line_length ]
fmt.sbprintf( & builder, "%-*s ", Max_Logger_Message_Width, first_line )
// Signature
{
when time.IS_SUPPORTED
{
if core_log.Full_Timestamp_Opts & options != nil {
fmt.sbprint( & builder, "[")
t := time.now()
y, m, d := time.date(t)
h, min, s := time.clock(t)
if .Date in options {
fmt.sbprintf( & builder, "%d-%02d-%02d ", y, m, d )
}
if .Time in options {
fmt.sbprintf( & builder, "%02d:%02d:%02d", h, min, s)
}
fmt.sbprint( & builder, "] ")
}
}
core_log.do_level_header( options, level, & builder )
if logger.id != "" {
fmt.sbprintf( & builder, "[%s] ", logger.id )
}
core_log.do_location_header( options, & builder, location )
}
// Oversized message handling
if len(text) > Max_Logger_Message_Width
{
offset := Max_Logger_Message_Width
bytes := transmute([]u8) text
for left := len(bytes) - Max_Logger_Message_Width; left > 0; left -= Max_Logger_Message_Width
{
fmt.sbprintf( & builder, "\n" )
subset_length := len(text) - offset
if subset_length > Max_Logger_Message_Width {
subset_length = Max_Logger_Message_Width
}
subset := slice_ptr( ptr_offset( raw_data(bytes), offset), subset_length )
fmt.sbprintf( & builder, "%s", transmute(string)subset )
offset += Max_Logger_Message_Width
}
}
fmt.fprintln( logger.file, strings.to_string(builder) )
}
log :: proc ( msg : string, level := LogLevel.Info, loc := #caller_location ) {
core_log.log( level, msg, location = loc )
}
logf :: proc ( fmt : string, args : ..any, level := LogLevel.Info, loc := #caller_location ) {
core_log.logf( level, fmt, args, location = loc )
}

View File

@ -34,6 +34,7 @@ tracked_allocator :: proc ( self : ^ TrackedAllocator ) -> Allocator {
return tracking_allocator( & self.tracker )
}
// TODO(Ed): These allocators are bad... not sure why.
tracked_allocator_init :: proc( size, internals_size : int, allocator := context.allocator ) -> TrackedAllocator
{
result : TrackedAllocator
@ -44,20 +45,22 @@ tracked_allocator_init :: proc( size, internals_size : int, allocator := context
raw_size := backing_size + internals_size
raw_mem, raw_mem_code := alloc( raw_size, mem.DEFAULT_ALIGNMENT, allocator )
if ( raw_mem_code != mem.Allocator_Error.None )
{
// TODO(Ed) : Setup a proper logging interface
fmt. printf( "Failed to allocate memory for the TrackingAllocator" )
runtime.debug_trap()
os. exit( -1 )
// TODO(Ed) : Figure out the error code enums..
}
arena_init( & result.backing, slice_ptr( cast( ^ byte) raw_mem, backing_size ) )
arena_init( & result.internals, slice_ptr( memory_after( result.backing.data), internals_size ) )
verify( raw_mem_code != mem.Allocator_Error.None, "Failed to allocate memory for the TrackingAllocator" )
backing_slice := slice_ptr( cast( ^ byte) raw_mem, backing_size )
internals_slice := slice_ptr( memory_after( backing_slice), internals_size )
arena_init( & result.backing, backing_slice )
arena_init( & result.internals, internals_slice )
backing_allocator := arena_allocator( & result.backing )
internals_allocator := arena_allocator( & result.internals )
tracking_allocator_init( & result.tracker, backing_allocator, internals_allocator )
{
tracker_arena := cast(^Arena) result.tracker.backing.data
arena_len := len( tracker_arena.data )
verify( arena_len != len(result.backing.data), "BAD SIZE ON TRACKER'S ARENA" )
}
return result
}
@ -68,14 +71,7 @@ tracked_allocator_init_vmem :: proc( vmem : [] byte, internals_size : int ) -> ^
backing_size := len(vmem) - internals_size
raw_size := backing_size + internals_size
if backing_size < 0 || len(vmem) < raw_size
{
// TODO(Ed) : Setup a proper logging interface
fmt. printf( "Provided virtual memory slice is not large enough to hold the TrackedAllocator" )
runtime.debug_trap()
os. exit( -1 )
// TODO(Ed) : Figure out the error code enums..
}
verify( backing_size < 0 || len(vmem) < raw_size, "Provided virtual memory slice is not large enough to hold the TrackedAllocator" )
result := cast( ^ TrackedAllocator) & vmem[0]
result_slice := slice_ptr( & vmem[0], tracking_allocator_size )