intern.odin 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. package strings
  2. import "core:runtime"
  3. import "core:mem"
  4. // Custom string entry struct
  5. Intern_Entry :: struct {
  6. len: int,
  7. str: [1]byte, // string is allocated inline with the entry to keep allocations simple
  8. }
  9. /*
  10. Intern is a more memory efficient string map
  11. Uses Specified Allocator for `Intern_Entry` strings
  12. Fields:
  13. - allocator: The allocator used for the Intern_Entry strings
  14. - entries: A map of strings to interned string entries
  15. */
  16. Intern :: struct {
  17. allocator: runtime.Allocator,
  18. entries: map[string]^Intern_Entry,
  19. }
  20. /*
  21. Initializes the entries map and sets the allocator for the string entries
  22. *Allocates Using Provided Allocators*
  23. Inputs:
  24. - m: A pointer to the Intern struct to be initialized
  25. - allocator: The allocator for the Intern_Entry strings (Default: context.allocator)
  26. - map_allocator: The allocator for the map of entries (Default: context.allocator)
  27. Returns:
  28. - err: An allocator error if one occured, `nil` otherwise
  29. */
  30. intern_init :: proc(m: ^Intern, allocator := context.allocator, map_allocator := context.allocator, loc := #caller_location) -> (err: mem.Allocator_Error) {
  31. m.allocator = allocator
  32. m.entries = make(map[string]^Intern_Entry, 16, map_allocator, loc) or_return
  33. return nil
  34. }
  35. /*
  36. Frees the map and all its content allocated using the `.allocator`.
  37. Inputs:
  38. - m: A pointer to the Intern struct to be destroyed
  39. */
  40. intern_destroy :: proc(m: ^Intern) {
  41. for _, value in m.entries {
  42. free(value, m.allocator)
  43. }
  44. delete(m.entries)
  45. }
  46. /*
  47. Returns an interned copy of the given text, adding it to the map if not already present.
  48. *Allocate using the Intern's Allocator (First time string is seen only)*
  49. Inputs:
  50. - m: A pointer to the Intern struct
  51. - text: The string to be interned
  52. NOTE: The returned string lives as long as the map entry lives.
  53. Returns:
  54. - str: The interned string
  55. - err: An allocator error if one occured, `nil` otherwise
  56. */
  57. intern_get :: proc(m: ^Intern, text: string) -> (str: string, err: runtime.Allocator_Error) {
  58. entry := _intern_get_entry(m, text) or_return
  59. #no_bounds_check return string(entry.str[:entry.len]), nil
  60. }
  61. /*
  62. Returns an interned copy of the given text as a cstring, adding it to the map if not already present.
  63. *Allocate using the Intern's Allocator (First time string is seen only)*
  64. Inputs:
  65. - m: A pointer to the Intern struct
  66. - text: The string to be interned
  67. NOTE: The returned cstring lives as long as the map entry lives
  68. Returns:
  69. - str: The interned cstring
  70. - err: An allocator error if one occured, `nil` otherwise
  71. */
  72. intern_get_cstring :: proc(m: ^Intern, text: string) -> (str: cstring, err: runtime.Allocator_Error) {
  73. entry := _intern_get_entry(m, text) or_return
  74. return cstring(&entry.str[0]), nil
  75. }
  76. /*
  77. Internal function to lookup whether the text string exists in the map, returns the entry
  78. Sets and allocates the entry if it wasn't set yet
  79. *Allocate using the Intern's Allocator (First time string is seen only)*
  80. Inputs:
  81. - m: A pointer to the Intern struct
  82. - text: The string to be looked up or interned
  83. Returns:
  84. - new_entry: The interned cstring
  85. - err: An allocator error if one occured, `nil` otherwise
  86. */
  87. _intern_get_entry :: proc(m: ^Intern, text: string) -> (new_entry: ^Intern_Entry, err: runtime.Allocator_Error) #no_bounds_check {
  88. if prev, ok := m.entries[text]; ok {
  89. return prev, nil
  90. }
  91. if m.allocator.procedure == nil {
  92. m.allocator = context.allocator
  93. }
  94. entry_size := int(offset_of(Intern_Entry, str)) + len(text) + 1
  95. bytes := runtime.mem_alloc(entry_size, align_of(Intern_Entry), m.allocator) or_return
  96. new_entry = (^Intern_Entry)(raw_data(bytes))
  97. new_entry.len = len(text)
  98. copy(new_entry.str[:new_entry.len], text)
  99. new_entry.str[new_entry.len] = 0
  100. key := string(new_entry.str[:new_entry.len])
  101. m.entries[key] = new_entry
  102. return new_entry, nil
  103. }