intern.odin 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. package strings
  2. import "core:runtime"
  3. // custom string entry struct
  4. Intern_Entry :: struct {
  5. len: int,
  6. str: [1]byte, // string is allocated inline with the entry to keep allocations simple
  7. }
  8. // "intern" is a more memory efficient string map
  9. // `allocator` is used to allocate the actual `Intern_Entry` strings
  10. Intern :: struct {
  11. allocator: runtime.Allocator,
  12. entries: map[string]^Intern_Entry,
  13. }
  14. // initialize the entries map and set the allocator for the string entries
  15. intern_init :: proc(m: ^Intern, allocator := context.allocator, map_allocator := context.allocator) {
  16. m.allocator = allocator
  17. m.entries = make(map[string]^Intern_Entry, 16, map_allocator)
  18. }
  19. // free the map and all its content allocated using the `.allocator`
  20. intern_destroy :: proc(m: ^Intern) {
  21. for _, value in m.entries {
  22. free(value, m.allocator)
  23. }
  24. delete(m.entries)
  25. }
  26. // returns the `text` string from the intern map - gets set if it didnt exist yet
  27. // the returned string lives as long as the map entry lives
  28. intern_get :: proc(m: ^Intern, text: string) -> (str: string, err: runtime.Allocator_Error) {
  29. entry := _intern_get_entry(m, text) or_return
  30. #no_bounds_check return string(entry.str[:entry.len]), nil
  31. }
  32. // returns the `text` cstring from the intern map - gets set if it didnt exist yet
  33. // the returned cstring lives as long as the map entry lives
  34. intern_get_cstring :: proc(m: ^Intern, text: string) -> (str: cstring, err: runtime.Allocator_Error) {
  35. entry := _intern_get_entry(m, text) or_return
  36. return cstring(&entry.str[0]), nil
  37. }
  38. // looks up wether the `text` string exists in the map, returns the entry
  39. // sets & allocates the entry if it wasnt set yet
  40. _intern_get_entry :: proc(m: ^Intern, text: string) -> (new_entry: ^Intern_Entry, err: runtime.Allocator_Error) #no_bounds_check {
  41. if prev, ok := m.entries[text]; ok {
  42. return prev, nil
  43. }
  44. if m.allocator.procedure == nil {
  45. m.allocator = context.allocator
  46. }
  47. entry_size := int(offset_of(Intern_Entry, str)) + len(text) + 1
  48. bytes := runtime.mem_alloc(entry_size, align_of(Intern_Entry), m.allocator) or_return
  49. new_entry = (^Intern_Entry)(raw_data(bytes))
  50. new_entry.len = len(text)
  51. copy(new_entry.str[:new_entry.len], text)
  52. new_entry.str[new_entry.len] = 0
  53. key := string(new_entry.str[:new_entry.len])
  54. m.entries[key] = new_entry
  55. return new_entry, nil
  56. }