Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Fuzzy generics

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

Fuzzy generics

Avatar for Alexey Palazhchenko

Alexey Palazhchenko

February 05, 2022
Tweet

More Decks by Alexey Palazhchenko

Other Decks in Programming

Transcript

  1. Generics • Generics do not work there; let's go back

    to interfaces • Let me extract a small example for a talk
  2. Generics • Generics do not work there; let's go back

    to interfaces • Let me extract a small example for a talk • Hm, that's not that bad
  3. BSON Document • Ordered map • Keys – string, values

    – any BSON value, including other documents
  4. BSON Document • Ordered map • Keys – string, values

    – any BSON value, including other documents • The central data structure in FerretDB
  5. Interfaces type BSONType interface { bsontype() // sealed } type

    Int int func (Int) bsontype() {} type String string func (String) bsontype() {}
  6. Interfaces type BSONType interface { bsontype() // sealed } type

    Int int func (Int) bsontype() {} type String string func (String) bsontype() {} type Document struct { m map[string]BSONType keys []string } func (*Document) bsontype() {}
  7. Interfaces func (d *Document) Set(key String, value BSONType) { if

    _, ok := d.m[key]; !ok { d.keys = append(d.keys, key) } d.m[key] = value }
  8. Generics type BSONType interface { int | string | *Document

    } type Document struct { m map[string]any keys []string }
  9. Generics func (d *Document) Set[T BSONType](key string, value T) {

    if _, ok := d.m[key]; !ok { d.keys = append(d.keys, key) } d.m[key] = value }
  10. Generics func (d *Document) Set[T BSONType](key string, value T) {

    if _, ok := d.m[key]; !ok { d.keys = append(d.keys, key) } d.m[key] = value } syntax error: method must have no type parameters
  11. Generics func DocumentSet[T BSONType](d *Document, key string, value T) {

    if _, ok := d.m[key]; !ok { d.keys = append(d.keys, key) } d.m[key] = value }
  12. Interfaces func MakeDocument(pairs ...BSONType) (*Document, error) { l := len(pairs)

    if l%2 != 0 { return nil, fmt.Errorf("%d arguments", l)
  13. Generics func MakeDocument[T BSONType](key string, value T) *Document func MakeDocument2[T1,

    T2 BSONType](key1 string, value1 T1, key2 string, value2 T2) *Document
  14. Generics func MakeDocument[T BSONType](key string, value T) *Document func MakeDocument2[T1,

    T2 BSONType](key1 string, value1 T1, key2 string, value2 T2) *Document func MakeDocument3[T1, T2, T3 BSONType](key1 string, value1 T1, key2 string, value2 T2, key3 string, value3 T3) *Document
  15. Interfaces func (d *Document) Get(key String) BSONType { return d.m[key]

    } assert.Equal(t, Int(42), d.Get("foo")) assert.Equal(t, nil, d.Get("baz"))
  16. Generics func DocumentGet[T BSONType](d *Document, key string) T { v,

    _ := d.m[key].(T) return v } assert.Equal(t, 42, DocumentGet[int](d, "foo")) assert.Equal(t, "", DocumentGet[string](d, "foo"))
  17. Fuzzing • testing/quick on steroids • MongoDB binary protocol parsing

    packages • FerretDB had fuzzing tests even before unit tests
  18. Table-driven tests type testCase struct { name string b []byte

    v bsontype err error } • Unmarshal b to (v2, err2) • compare with v or err • Marshal v2 to b2 • compare with b
  19. Table-driven tests func TestDocument(t *testing.T) { for _, tc :=

    range testCases { tc := tc t.Run(tc.name, func(t *testing.T) {
  20. Fuzzing func FuzzDocument(f *testing.F) { for _, tc := range

    testCases { f.Add(tc.b) } f.Fuzz(func(t *testing.T, b []byte) {
  21. When b2 != b • b might be larger than

    needed • The unread portion of b should be removed
  22. When b2 != b • b might be larger than

    needed • The unread portion of b should be removed • Some bytes might be insigni fi cant
  23. When b2 != b • b might be larger than

    needed • The unread portion of b should be removed • Some bytes might be insigni fi cant • Use canonization (json.Compact, etc)
  24. Fuzzing issues • No subtests (testing.F.Run) • Unnamed seed values

    • Hanging detection is unreliable with unset GOMAXPROCS
  25. Fuzzing • Found a few bugs in `go test -fuzz`

    • Found many many bugs in FerretDB
  26. Fuzzing • Found a few bugs in `go test -fuzz`

    • Found many many bugs in FerretDB • De fi nitely use it for parsing
  27. Links • https://github.com/AlekSi/ generics-vs-interfaces • https://github.com/akutz/
 go-generics-the-hard-way • https://go.dev/doc/tutorial/ •

    https://go.dev/doc/fuzz/ • https://www.ferretdb.io • https://github.com/FerretDB ⭐ • https://github.com/AlekSi • @paaleksey