smallindex.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. // Copyright (C) 2018 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. package olddb
  7. import (
  8. "encoding/binary"
  9. "slices"
  10. "github.com/syncthing/syncthing/internal/db/olddb/backend"
  11. "github.com/syncthing/syncthing/lib/sync"
  12. )
  13. // A smallIndex is an in memory bidirectional []byte to uint32 map. It gives
  14. // fast lookups in both directions and persists to the database. Don't use for
  15. // storing more items than fit comfortably in RAM.
  16. type smallIndex struct {
  17. db backend.Backend
  18. prefix []byte
  19. id2val map[uint32]string
  20. val2id map[string]uint32
  21. nextID uint32
  22. mut sync.Mutex
  23. }
  24. func newSmallIndex(db backend.Backend, prefix []byte) *smallIndex {
  25. idx := &smallIndex{
  26. db: db,
  27. prefix: prefix,
  28. id2val: make(map[uint32]string),
  29. val2id: make(map[string]uint32),
  30. mut: sync.NewMutex(),
  31. }
  32. idx.load()
  33. return idx
  34. }
  35. // load iterates over the prefix space in the database and populates the in
  36. // memory maps.
  37. func (i *smallIndex) load() {
  38. it, err := i.db.NewPrefixIterator(i.prefix)
  39. if err != nil {
  40. panic("loading small index: " + err.Error())
  41. }
  42. defer it.Release()
  43. for it.Next() {
  44. val := string(it.Value())
  45. id := binary.BigEndian.Uint32(it.Key()[len(i.prefix):])
  46. if val != "" {
  47. // Empty value means the entry has been deleted.
  48. i.id2val[id] = val
  49. i.val2id[val] = id
  50. }
  51. if id >= i.nextID {
  52. i.nextID = id + 1
  53. }
  54. }
  55. }
  56. // ID returns the index number for the given byte slice, allocating a new one
  57. // and persisting this to the database if necessary.
  58. func (i *smallIndex) ID(val []byte) (uint32, error) {
  59. i.mut.Lock()
  60. // intentionally avoiding defer here as we want this call to be as fast as
  61. // possible in the general case (folder ID already exists). The map lookup
  62. // with the conversion of []byte to string is compiler optimized to not
  63. // copy the []byte, which is why we don't assign it to a temp variable
  64. // here.
  65. if id, ok := i.val2id[string(val)]; ok {
  66. i.mut.Unlock()
  67. return id, nil
  68. }
  69. panic("missing ID")
  70. }
  71. // Val returns the value for the given index number, or (nil, false) if there
  72. // is no such index number.
  73. func (i *smallIndex) Val(id uint32) ([]byte, bool) {
  74. i.mut.Lock()
  75. val, ok := i.id2val[id]
  76. i.mut.Unlock()
  77. if !ok {
  78. return nil, false
  79. }
  80. return []byte(val), true
  81. }
  82. // Values returns the set of values in the index
  83. func (i *smallIndex) Values() []string {
  84. // In principle this method should return [][]byte because all the other
  85. // methods deal in []byte keys. However, in practice, where it's used
  86. // wants a []string and it's easier to just create that here rather than
  87. // having to convert both here and there...
  88. i.mut.Lock()
  89. vals := make([]string, 0, len(i.val2id))
  90. for val := range i.val2id {
  91. vals = append(vals, val)
  92. }
  93. i.mut.Unlock()
  94. slices.Sort(vals)
  95. return vals
  96. }