@@ -11,6 +11,7 @@ import (
1111 "math"
1212 "net"
1313 "strings"
14+ "sync"
1415 "time"
1516
1617 "github.com/fastly/compute-sdk-go/internal/abi/prim"
@@ -2441,10 +2442,22 @@ func fastlyDictionaryOpen(
24412442// allows us to use valueBuf scratch space between the guest and host to avoid
24422443// allocations any larger than necessary, without locking.
24432444type Dictionary struct {
2444- h dictionaryHandle
2445- valueBuf [dictionaryValueMaxLen ]byte
2445+ h dictionaryHandle
2446+
2447+ mu sync.Mutex // protects valueBuf
2448+ valueBuf [dictionaryMaxValueLen ]byte
24462449}
24472450
2451+ // Dictionaries are subject to very specific limitations: 255 character keys and 8000 character values, utf-8 encoded.
2452+ // The current storage collation limits utf-8 representations to 3 bytes in length.
2453+ // https://docs.fastly.com/en/guides/about-edge-dictionaries#limitations-and-considerations
2454+ // https://dev.mysql.com/doc/refman/8.4/en/charset-unicode-utf8mb3.html
2455+ // https://en.wikipedia.org/wiki/UTF-8#Encoding
2456+ const (
2457+ dictionaryMaxKeyLen = 255 * 3 // known maximum size for config store keys: 755 bytes, for 255 3-byte utf-8 encoded characters
2458+ dictionaryMaxValueLen = 8000 * 3 // known maximum size for config store values: 24,000 bytes, for 8000 3-byte utf-8 encoded characters
2459+ )
2460+
24482461// OpenDictionary returns a reference to the named dictionary, if it exists.
24492462func OpenDictionary (name string ) (* Dictionary , error ) {
24502463 var d Dictionary
@@ -2481,13 +2494,18 @@ func fastlyDictionaryGet(
24812494 nWritten prim.Pointer [prim.Usize ],
24822495) FastlyStatus
24832496
2484- // Get the value for key, as a byte slice, if it exists.
2485- func (d * Dictionary ) GetBytes (key string ) ([]byte , error ) {
2486- keyBuffer := prim .NewReadBufferFromString (key ).Wstring ()
2487- buf := prim .NewWriteBufferFromBytes (d .valueBuf [:])
2497+ // Get the value for key, if it exists. The returned slice's backing array is
2498+ // shared between multiple calls to getBytesUnlocked.
2499+ func (d * Dictionary ) getBytesUnlocked (key string ) ([]byte , error ) {
2500+ keyBuffer := prim .NewReadBufferFromString (key )
2501+ if keyBuffer .Len () > dictionaryMaxKeyLen {
2502+ return nil , FastlyStatusInval .toError ()
2503+ }
2504+ buf := prim .NewWriteBufferFromBytes (d .valueBuf [:]) // fresh slice of backing array
2505+ keyStr := keyBuffer .Wstring ()
24882506 status := fastlyDictionaryGet (
24892507 d .h ,
2490- keyBuffer .Data , keyBuffer .Len ,
2508+ keyStr .Data , keyStr .Len ,
24912509 prim .ToPointer (buf .Char8Pointer ()), buf .Cap (),
24922510 prim .ToPointer (buf .NPointer ()),
24932511 )
@@ -2497,17 +2515,21 @@ func (d *Dictionary) GetBytes(key string) ([]byte, error) {
24972515 return buf .AsBytes (), nil
24982516}
24992517
2500- // Get the value for key, if it exists.
2501- func (d * Dictionary ) Get (key string ) (string , error ) {
2502- buf , err := d .GetBytes (key )
2518+ // GetBytes returns a slice of newly-allocated memory for the value
2519+ // corresponding to key.
2520+ func (d * Dictionary ) GetBytes (key string ) ([]byte , error ) {
2521+ d .mu .Lock ()
2522+ defer d .mu .Unlock ()
2523+ v , err := d .getBytesUnlocked (key )
25032524 if err != nil {
2504- return "" , err
2525+ return nil , err
25052526 }
2506-
2507- return string (buf ), nil
2527+ p := make ([]byte , len (v ))
2528+ copy (p , v )
2529+ return p , nil
25082530}
25092531
2510- // Has returns whether a value exists .
2532+ // Has returns true if key is found .
25112533func (d * Dictionary ) Has (key string ) (bool , error ) {
25122534 keyBuffer := prim .NewReadBufferFromString (key ).Wstring ()
25132535 var npointer prim.Usize = 0
0 commit comments