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