-
Notifications
You must be signed in to change notification settings - Fork 276
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
perf(textarea): implement wrapping memoization
* Improve textarea performance by caching the wrap results * Add memoization utility * Fix failing github jobs due to requirement for 1.18 * Address linting issues * Fix redundant docstrings * fix: minor changes to memoization * fix: soft-wrapped hidden line numbers * perf: switch to `uniseg.StringWidth` --------- Co-authored-by: Maas Lalani <[email protected]>
- Loading branch information
1 parent
ec88302
commit 6fe92f9
Showing
9 changed files
with
426 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
package memoization | ||
|
||
import ( | ||
"container/list" | ||
"crypto/sha256" | ||
"fmt" | ||
"sync" | ||
) | ||
|
||
// Hasher is an interface that requires a Hash method. The Hash method is | ||
// expected to return a string representation of the hash of the object. | ||
type Hasher interface { | ||
Hash() string | ||
} | ||
|
||
// entry is a struct that holds a key-value pair. It is used as an element | ||
// in the evictionList of the MemoCache. | ||
type entry[T any] struct { | ||
key string | ||
value T | ||
} | ||
|
||
// MemoCache is a struct that represents a cache with a set capacity. It | ||
// uses an LRU (Least Recently Used) eviction policy. It is safe for | ||
// concurrent use. | ||
type MemoCache[H Hasher, T any] struct { | ||
capacity int | ||
mutex sync.Mutex | ||
cache map[string]*list.Element // The cache holding the results | ||
evictionList *list.List // A list to keep track of the order for LRU | ||
hashableItems map[string]T // This map keeps track of the original hashable items (optional) | ||
} | ||
|
||
// NewMemoCache is a function that creates a new MemoCache with a given | ||
// capacity. It returns a pointer to the created MemoCache. | ||
func NewMemoCache[H Hasher, T any](capacity int) *MemoCache[H, T] { | ||
return &MemoCache[H, T]{ | ||
capacity: capacity, | ||
cache: make(map[string]*list.Element), | ||
evictionList: list.New(), | ||
hashableItems: make(map[string]T), | ||
} | ||
} | ||
|
||
// Capacity is a method that returns the capacity of the MemoCache. | ||
func (m *MemoCache[H, T]) Capacity() int { | ||
return m.capacity | ||
} | ||
|
||
// Size is a method that returns the current size of the MemoCache. It is | ||
// the number of items currently stored in the cache. | ||
func (m *MemoCache[H, T]) Size() int { | ||
m.mutex.Lock() | ||
defer m.mutex.Unlock() | ||
return m.evictionList.Len() | ||
} | ||
|
||
// Get is a method that returns the value associated with the given | ||
// hashable item in the MemoCache. If there is no corresponding value, the | ||
// method returns nil. | ||
func (m *MemoCache[H, T]) Get(h H) (T, bool) { | ||
m.mutex.Lock() | ||
defer m.mutex.Unlock() | ||
|
||
hashedKey := h.Hash() | ||
if element, found := m.cache[hashedKey]; found { | ||
m.evictionList.MoveToFront(element) | ||
return element.Value.(*entry[T]).value, true | ||
} | ||
var result T | ||
return result, false | ||
} | ||
|
||
// Set is a method that sets the value for the given hashable item in the | ||
// MemoCache. If the cache is at capacity, it evicts the least recently | ||
// used item before adding the new item. | ||
func (m *MemoCache[H, T]) Set(h H, value T) { | ||
m.mutex.Lock() | ||
defer m.mutex.Unlock() | ||
|
||
hashedKey := h.Hash() | ||
if element, found := m.cache[hashedKey]; found { | ||
m.evictionList.MoveToFront(element) | ||
element.Value.(*entry[T]).value = value | ||
return | ||
} | ||
|
||
// Check if the cache is at capacity | ||
if m.evictionList.Len() >= m.capacity { | ||
// Evict the least recently used item from the cache | ||
toEvict := m.evictionList.Back() | ||
if toEvict != nil { | ||
evictedEntry := m.evictionList.Remove(toEvict).(*entry[T]) | ||
delete(m.cache, evictedEntry.key) | ||
delete(m.hashableItems, evictedEntry.key) // if you're keeping track of original items | ||
} | ||
} | ||
|
||
// Add the value to the cache and the evictionList | ||
newEntry := &entry[T]{ | ||
key: hashedKey, | ||
value: value, | ||
} | ||
element := m.evictionList.PushFront(newEntry) | ||
m.cache[hashedKey] = element | ||
m.hashableItems[hashedKey] = value // if you're keeping track of original items | ||
} | ||
|
||
// HString is a type that implements the Hasher interface for strings. | ||
type HString string | ||
|
||
// Hash is a method that returns the hash of the string. | ||
func (h HString) Hash() string { | ||
return fmt.Sprintf("%x", sha256.Sum256([]byte(h))) | ||
} | ||
|
||
// HInt is a type that implements the Hasher interface for integers. | ||
type HInt int | ||
|
||
// Hash is a method that returns the hash of the integer. | ||
func (h HInt) Hash() string { | ||
return fmt.Sprintf("%x", sha256.Sum256([]byte(fmt.Sprintf("%d", h)))) | ||
} |
Oops, something went wrong.