smallindex.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  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 db
  7. import (
  8. "encoding/binary"
  9. "sort"
  10. "github.com/syncthing/syncthing/lib/sync"
  11. "github.com/syndtr/goleveldb/leveldb"
  12. "github.com/syndtr/goleveldb/leveldb/util"
  13. )
  14. // A smallIndex is an in memory bidirectional []byte to uint32 map. It gives
  15. // fast lookups in both directions and persists to the database. Don't use for
  16. // storing more items than fit comfortably in RAM.
  17. type smallIndex struct {
  18. db *leveldb.DB
  19. prefix []byte
  20. id2val map[uint32]string
  21. val2id map[string]uint32
  22. nextID uint32
  23. mut sync.Mutex
  24. }
  25. func newSmallIndex(db *leveldb.DB, prefix []byte) *smallIndex {
  26. idx := &smallIndex{
  27. db: db,
  28. prefix: prefix,
  29. id2val: make(map[uint32]string),
  30. val2id: make(map[string]uint32),
  31. mut: sync.NewMutex(),
  32. }
  33. idx.load()
  34. return idx
  35. }
  36. // load iterates over the prefix space in the database and populates the in
  37. // memory maps.
  38. func (i *smallIndex) load() {
  39. it := i.db.NewIterator(util.BytesPrefix(i.prefix), nil)
  40. defer it.Release()
  41. for it.Next() {
  42. val := string(it.Value())
  43. id := binary.BigEndian.Uint32(it.Key()[len(i.prefix):])
  44. if val != "" {
  45. // Empty value means the entry has been deleted.
  46. i.id2val[id] = val
  47. i.val2id[val] = id
  48. }
  49. if id >= i.nextID {
  50. i.nextID = id + 1
  51. }
  52. }
  53. }
  54. // ID returns the index number for the given byte slice, allocating a new one
  55. // and persisting this to the database if necessary.
  56. func (i *smallIndex) ID(val []byte) uint32 {
  57. i.mut.Lock()
  58. // intentionally avoiding defer here as we want this call to be as fast as
  59. // possible in the general case (folder ID already exists). The map lookup
  60. // with the conversion of []byte to string is compiler optimized to not
  61. // copy the []byte, which is why we don't assign it to a temp variable
  62. // here.
  63. if id, ok := i.val2id[string(val)]; ok {
  64. i.mut.Unlock()
  65. return id
  66. }
  67. id := i.nextID
  68. i.nextID++
  69. valStr := string(val)
  70. i.val2id[valStr] = id
  71. i.id2val[id] = valStr
  72. key := make([]byte, len(i.prefix)+8) // prefix plus uint32 id
  73. copy(key, i.prefix)
  74. binary.BigEndian.PutUint32(key[len(i.prefix):], id)
  75. i.db.Put(key, val, nil)
  76. i.mut.Unlock()
  77. return id
  78. }
  79. // Val returns the value for the given index number, or (nil, false) if there
  80. // is no such index number.
  81. func (i *smallIndex) Val(id uint32) ([]byte, bool) {
  82. i.mut.Lock()
  83. val, ok := i.id2val[id]
  84. i.mut.Unlock()
  85. if !ok {
  86. return nil, false
  87. }
  88. return []byte(val), true
  89. }
  90. func (i *smallIndex) Delete(val []byte) {
  91. i.mut.Lock()
  92. defer i.mut.Unlock()
  93. // Check the reverse mapping to get the ID for the value.
  94. if id, ok := i.val2id[string(val)]; ok {
  95. // Generate the corresponding database key.
  96. key := make([]byte, len(i.prefix)+8) // prefix plus uint32 id
  97. copy(key, i.prefix)
  98. binary.BigEndian.PutUint32(key[len(i.prefix):], id)
  99. // Put an empty value into the database. This indicates that the
  100. // entry does not exist any more and prevents the ID from being
  101. // reused in the future.
  102. i.db.Put(key, []byte{}, nil)
  103. // Delete reverse mapping.
  104. delete(i.id2val, id)
  105. }
  106. // Delete forward mapping.
  107. delete(i.val2id, string(val))
  108. }
  109. // Values returns the set of values in the index
  110. func (i *smallIndex) Values() []string {
  111. // In principle this method should return [][]byte because all the other
  112. // methods deal in []byte keys. However, in practice, where it's used
  113. // wants a []string and it's easier to just create that here rather than
  114. // having to convert both here and there...
  115. i.mut.Lock()
  116. vals := make([]string, 0, len(i.val2id))
  117. for val := range i.val2id {
  118. vals = append(vals, val)
  119. }
  120. i.mut.Unlock()
  121. sort.Strings(vals)
  122. return vals
  123. }