Added basic string interning

This commit is contained in:
Edward R. Gonzalez 2024-03-08 23:20:49 -05:00
parent c395cbaeb6
commit f1edf1c43e
13 changed files with 144 additions and 25 deletions

View File

@ -79,6 +79,8 @@ startup :: proc( persistent_mem, frame_mem, transient_mem, files_buffer_mem : ^V
verify( alloc_error == .None, "Failed to allocate the general slab allocator" )
}
string_cache = str_cache_init()
context.user_ptr = state
input = & input_data[1]
@ -143,7 +145,7 @@ startup :: proc( persistent_mem, frame_mem, transient_mem, files_buffer_mem : ^V
// path_squidgy_slimes := strings.concatenate( { Path_Assets, "Squidgy Slimes.ttf" } )
// font_squidgy_slimes = font_load( path_squidgy_slimes, 24.0, "Squidgy_Slime" )
path_firacode := strings.concatenate( { Path_Assets, "FiraCode-Regular.ttf" }, frame_allocator() )
path_firacode := strings.concatenate( { Path_Assets, "FiraCode-Regular.ttf" }, transient_allocator() )
font_firacode = font_load( path_firacode, 24.0, "FiraCode" )
default_font = font_firacode
log( "Default font loaded" )
@ -152,9 +154,9 @@ startup :: proc( persistent_mem, frame_mem, transient_mem, files_buffer_mem : ^V
// Demo project setup
{
using project
path = "./"
name = "First Project"
workspace.name = "First Workspace"
path = str_intern("./")
name = str_intern( "First Project" )
workspace.name = str_intern( "First Workspace" )
{
using project.workspace
cam = {

View File

@ -133,6 +133,7 @@ AppConfig :: struct {
State :: struct {
general_slab : Slab,
string_cache : StringCache,
font_provider_data : FontProviderData,
@ -190,8 +191,8 @@ ProjectConfig :: struct {
}
Project :: struct {
path : string,
name : string,
path : StringCached,
name : StringCached,
config : ProjectConfig,
codebase : CodeBase,
@ -209,7 +210,7 @@ Frame :: struct
}
Workspace :: struct {
name : string,
name : StringCached,
cam : Camera,
zoom_target : f32,

View File

@ -160,6 +160,7 @@ font_load :: proc( path_file : string,
rl.UnloadImage( atlas )
}
free_all( context.temp_allocator )
return { key, desired_id }
}

View File

@ -94,6 +94,11 @@ cm_to_pixels :: proc {
range2_cm_to_pixels,
}
draw_text :: proc {
draw_text_string,
draw_text_string_cached,
}
get_bounds :: proc {
view_get_bounds,
}

View File

@ -44,7 +44,7 @@ SlabPolicy :: StackFixed(SlabSizeClass, Slab_Max_Size_Classes)
SlabHeader :: struct {
backing : Allocator,
policy : SlabPolicy,
policy : SlabPolicy, // TODO(Ed) : Remove this, the policy can't be changed after its been set so its meaningless to have...
pools : StackFixed(Pool, Slab_Max_Size_Classes),
}
@ -222,7 +222,6 @@ slab_allocator_proc :: proc(
alignment := uint(alignment)
old_size := uint(old_size)
// TODO(Ed) : Compiler bug - Some of these are commented out until I finish resolving issues with the pool allocator
switch mode
{
case .Alloc, .Alloc_Non_Zeroed:

View File

@ -1 +1,87 @@
/*
This is a quick and dirty string table.
IT uses the HMapZPL for the hashtable of strings, and the string's content is stored in a dedicated slab.
Future Plans (IF needed for performance):
The goal is to eventually swap out the slab with possilby a dedicated growing vmem arena for the strings.
The table would be swapped with a table stored in the general slab and uses either linear probing or open addressing
If linear probing, the hash node list per table bucket is store with the strigns in the same arena.
If open addressing, we just keep the open addressed array of node slots in the general slab (but hopefully better perf)
*/
package sectr
import "core:mem"
import "core:slice"
import "core:strings"
StringCached :: struct {
str : string,
runes : []rune,
}
StringCache :: struct {
slab : Slab,
table : HMapZPL(StringCached),
}
str_cache_init :: proc( /*allocator : Allocator*/ ) -> ( cache : StringCache ) {
alignment := uint(mem.DEFAULT_ALIGNMENT)
policy : SlabPolicy
policy_ptr := & policy
push( policy_ptr, SlabSizeClass { 8 * Megabyte, 16, alignment })
push( policy_ptr, SlabSizeClass { 8 * Megabyte, 32, alignment })
push( policy_ptr, SlabSizeClass { 16 * Megabyte, 64, alignment })
push( policy_ptr, SlabSizeClass { 16 * Megabyte, 128, alignment })
push( policy_ptr, SlabSizeClass { 16 * Megabyte, 256, alignment })
push( policy_ptr, SlabSizeClass { 16 * Megabyte, 512, alignment })
push( policy_ptr, SlabSizeClass { 32 * Megabyte, 1 * Kilobyte, alignment })
push( policy_ptr, SlabSizeClass { 32 * Megabyte, 4 * Kilobyte, alignment })
push( policy_ptr, SlabSizeClass { 64 * Megabyte, 16 * Kilobyte, alignment })
push( policy_ptr, SlabSizeClass { 64 * Megabyte, 32 * Kilobyte, alignment })
// push( policy_ptr, SlabSizeClass { 64 * Megabyte, 64 * Kilobyte, alignment })
// push( policy_ptr, SlabSizeClass { 64 * Megabyte, 128 * Kilobyte, alignment })
// push( policy_ptr, SlabSizeClass { 64 * Megabyte, 256 * Kilobyte, alignment })
// push( policy_ptr, SlabSizeClass { 64 * Megabyte, 512 * Kilobyte, alignment })
// push( policy_ptr, SlabSizeClass { 64 * Megabyte, 1 * Megabyte, alignment })
header_size :: size_of( Slab )
alloc_error : AllocatorError
cache.slab, alloc_error = slab_init( & policy, allocator = persistent_allocator() )
verify(alloc_error == .None, "Failed to initialize the string cache" )
cache.table, alloc_error = zpl_hmap_init_reserve( StringCached, general_slab_allocator(), 64 * Kilobyte )
return
}
// str_cache_intern_string :: proc(
// cache : ^StringCache,
str_intern :: proc(
content : string
) -> StringCached
{
cache := get_state().string_cache
key := u64( crc32( transmute([]byte) content ))
result := zpl_hmap_get( & cache.table, key )
if result != nil {
return (result ^)
}
length := len(content)
str_mem, alloc_error := slab_alloc( cache.slab, uint(length), uint(mem.DEFAULT_ALIGNMENT) )
verify( alloc_error == .None, "String cache had a backing allocator error" )
copy_non_overlapping( raw_data(str_mem), raw_data(content), length )
runes : []rune
runes, alloc_error = to_runes( content, slab_allocator(cache.slab) )
verify( alloc_error == .None, "String cache had a backing allocator error" )
result, alloc_error = zpl_hmap_set( & cache.table, key, StringCached { transmute(string) str_mem, runes } )
verify( alloc_error == .None, "String cache had a backing allocator error" )
return (result ^)
}

View File

@ -26,6 +26,7 @@ import "core:dynlib"
import "core:io"
import fmt_io "core:fmt"
str_fmt :: fmt_io.printf
str_fmt_alloc :: fmt_io.aprintf
str_fmt_tmp :: fmt_io.tprintf
str_fmt_builder :: fmt_io.sbprintf
import "core:log"
@ -251,11 +252,11 @@ main :: proc()
}
timestamp := str_fmt_tmp("%04d-%02d-%02d_%02d-%02d-%02d", year, month, day, hour, min, sec)
path_logger_finalized = str_clone( str_fmt_tmp( "%s/sectr_%v.log", Path_Logs, timestamp) )
path_logger_finalized = str_fmt_alloc( "%s/sectr_%v.log", Path_Logs, timestamp)
}
logger : sectr.Logger
logger_init( & logger, "Sectr Host", str_fmt_tmp( "%s/sectr.log", Path_Logs ) )
logger_init( & logger, "Sectr Host", str_fmt_alloc( "%s/sectr.log", Path_Logs ) )
context.logger = to_odin_logger( & logger )
{
// Log System Context

View File

@ -163,12 +163,12 @@ project_save :: proc( project : ^ Project, archive : ^ ArchiveData = nil )
}
project_serialize( project, archive )
if ! os.is_dir( project.path ) {
os.make_directory( project.path )
verify( cast(b32) os.is_dir( project.path ), "Failed to create project path for saving" )
if ! os.is_dir( project.path.str ) {
os.make_directory( project.path.str )
verify( cast(b32) os.is_dir( project.path.str ), "Failed to create project path for saving" )
}
os.write_entire_file( str_tmp_from_any( project.path, project.name, ".sectr_proj", sep = ""), archive.data )
os.write_entire_file( str_tmp_from_any( project.path.str, project.name.str, ".sectr_proj", sep = ""), archive.data )
}
project_load :: proc( path : string, project : ^ Project, archive : ^ ArchiveData = nil )

View File

@ -33,7 +33,7 @@ debug_draw_text :: proc( content : string, pos : Vec2, size : f32, color : rl.Co
tint = color );
}
debug_draw_text_world :: proc( content : string, pos : Vec2, size : f32, color : rl.Color = rl.WHITE, font : FontID = Font_Default )
draw_text_string :: proc( content : string, pos : Vec2, size : f32, color : rl.Color = rl.WHITE, font : FontID = Font_Default )
{
state := get_state(); using state
@ -62,6 +62,33 @@ debug_draw_text_world :: proc( content : string, pos : Vec2, size : f32, color :
tint = color );
}
draw_text_string_cached :: proc( content : StringCached, pos : Vec2, size : f32, color : rl.Color = rl.WHITE, font : FontID = Font_Default ) {
state := get_state(); using state
if len( content.str ) == 0 {
return
}
font := font
if font.key == Font_Default.key {
// if len(font) == 0 {
font = default_font
}
pos := world_to_screen_pos(pos)
px_size := size
zoom_adjust := px_size * project.workspace.cam.zoom
runes := content.runes
rl_font := to_rl_Font(font, zoom_adjust )
rl.DrawTextCodepoints( rl_font,
raw_data(runes), cast(i32) len(runes),
position = transmute(rl.Vector2) pos,
fontSize = px_size,
spacing = 0.0,
tint = color );
}
// Raylib's equivalent doesn't take a length for the string (making it a pain in the ass)
// So this is a 1:1 copy except it takes Odin strings
measure_text_size :: proc( text : string, font : FontID, font_size := Font_Use_Default_Size, spacing : f32 ) -> AreaSize

View File

@ -88,7 +88,7 @@ render_mode_2d :: proc()
rl.BeginMode2D( project.workspace.cam )
debug_draw_text_world( "This is text in world space", { 0, 200 }, 16.0 )
draw_text( "This is text in world space", { 0, 200 }, 16.0 )
ImguiRender:
{

View File

@ -231,14 +231,10 @@ update :: proc( delta_time : f64 ) -> b32
}
ui_set_layout( default_layout )
// First Demo
Test_HoverNClick :: false
Test_Draggable :: true
// test_hover_n_click()
// test_draggable()
}
//endregion Imgui Tick

View File

@ -223,7 +223,9 @@ UI_TextAlign :: enum u32 {
UI_Box :: struct {
// Cache ID
key : UI_Key,
label : string,
// label : string,
label : StringCached,
text : StringCached,
// Regenerated per frame.
using links : DLL_NodeFull( UI_Box ), // first, last, prev, next
@ -354,7 +356,7 @@ ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box)
else {
box : UI_Box
box.key = key
box.label = label
box.label = str_intern( label )
set_result, set_error = zpl_hmap_set( curr_cache, cast(u64) key, box )
}

View File

@ -24,7 +24,6 @@ test_draggable :: proc()
ui := ui_context
draggable := ui_widget( "Draggable Box!", UI_BoxFlags { .Mouse_Clickable, .Focusable, .Click_To_Focus } )
if draggable.first_frame {
debug.draggable_box_pos = draggable.style.layout.pos
debug.draggable_box_size = draggable.style.layout.size