Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add intern.Table.Query #385

Merged
merged 2 commits into from
Dec 10, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 28 additions & 10 deletions internal/intern/intern.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,23 +76,41 @@ type Table struct {
//
// This function may be called by multiple goroutines concurrently.
func (t *Table) Intern(s string) ID {
if char6, ok := encodeChar6(s); ok {
return char6
}

// Fast path for strings that have already been interned. In the common case
// all strings are interned, so we can take a read lock to avoid needing
// to trap to the scheduler on concurrent access (all calls to Intern() will
// still contend mu.readCount, because RLock atomically increments it).
if id, ok := t.Query(s); ok {
return id
}

// Outline the fallback for when we haven't interned, to promote inlining
// of Intern().
return t.internSlow(s)
}

// Query will query whether s has already been interned.
//
// If s has never been interned, returns false. This is useful for e.g. querying
// an intern-keyed map using a string: a failed query indicates that the string
// has never been seen before, so searching the map will be futile.
//
// If s is small enough to be inlined in an ID, it is treated as always being
// interned.
func (t *Table) Query(s string) (ID, bool) {
if char6, ok := encodeChar6(s); ok {
// This also handles s == "".
return char6, true
}

t.mu.RLock()
id, ok := t.index[s]
t.mu.RUnlock()
if ok {
// We never delete from this map, so if we see ok here, that cannot be
// changed by another goroutine.
return id
}

return id, ok
}

func (t *Table) internSlow(s string) ID {
// Intern tables are expected to be long-lived. Avoid holding onto a larger
// buffer that s is an internal pointer to by cloning it.
s = strings.Clone(s)
Expand All @@ -116,7 +134,7 @@ func (t *Table) Intern(s string) ID {
t.table = append(t.table, s)

// The first ID will have value 1. ID 0 is reserved for "".
id = ID(len(t.table))
id := ID(len(t.table))
if id < 0 {
panic(fmt.Sprintf("internal/intern: %d interning IDs exhausted", len(t.table)))
}
Expand Down
Loading