You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

115 lines
3.0 KiB

  1. package diskv
  2. import (
  3. "sync"
  4. "github.com/google/btree"
  5. )
  6. // Index is a generic interface for things that can
  7. // provide an ordered list of keys.
  8. type Index interface {
  9. Initialize(less LessFunction, keys <-chan string)
  10. Insert(key string)
  11. Delete(key string)
  12. Keys(from string, n int) []string
  13. }
  14. // LessFunction is used to initialize an Index of keys in a specific order.
  15. type LessFunction func(string, string) bool
  16. // btreeString is a custom data type that satisfies the BTree Less interface,
  17. // making the strings it wraps sortable by the BTree package.
  18. type btreeString struct {
  19. s string
  20. l LessFunction
  21. }
  22. // Less satisfies the BTree.Less interface using the btreeString's LessFunction.
  23. func (s btreeString) Less(i btree.Item) bool {
  24. return s.l(s.s, i.(btreeString).s)
  25. }
  26. // BTreeIndex is an implementation of the Index interface using google/btree.
  27. type BTreeIndex struct {
  28. sync.RWMutex
  29. LessFunction
  30. *btree.BTree
  31. }
  32. // Initialize populates the BTree tree with data from the keys channel,
  33. // according to the passed less function. It's destructive to the BTreeIndex.
  34. func (i *BTreeIndex) Initialize(less LessFunction, keys <-chan string) {
  35. i.Lock()
  36. defer i.Unlock()
  37. i.LessFunction = less
  38. i.BTree = rebuild(less, keys)
  39. }
  40. // Insert inserts the given key (only) into the BTree tree.
  41. func (i *BTreeIndex) Insert(key string) {
  42. i.Lock()
  43. defer i.Unlock()
  44. if i.BTree == nil || i.LessFunction == nil {
  45. panic("uninitialized index")
  46. }
  47. i.BTree.ReplaceOrInsert(btreeString{s: key, l: i.LessFunction})
  48. }
  49. // Delete removes the given key (only) from the BTree tree.
  50. func (i *BTreeIndex) Delete(key string) {
  51. i.Lock()
  52. defer i.Unlock()
  53. if i.BTree == nil || i.LessFunction == nil {
  54. panic("uninitialized index")
  55. }
  56. i.BTree.Delete(btreeString{s: key, l: i.LessFunction})
  57. }
  58. // Keys yields a maximum of n keys in order. If the passed 'from' key is empty,
  59. // Keys will return the first n keys. If the passed 'from' key is non-empty, the
  60. // first key in the returned slice will be the key that immediately follows the
  61. // passed key, in key order.
  62. func (i *BTreeIndex) Keys(from string, n int) []string {
  63. i.RLock()
  64. defer i.RUnlock()
  65. if i.BTree == nil || i.LessFunction == nil {
  66. panic("uninitialized index")
  67. }
  68. if i.BTree.Len() <= 0 {
  69. return []string{}
  70. }
  71. btreeFrom := btreeString{s: from, l: i.LessFunction}
  72. skipFirst := true
  73. if len(from) <= 0 || !i.BTree.Has(btreeFrom) {
  74. // no such key, so fabricate an always-smallest item
  75. btreeFrom = btreeString{s: "", l: func(string, string) bool { return true }}
  76. skipFirst = false
  77. }
  78. keys := []string{}
  79. iterator := func(i btree.Item) bool {
  80. keys = append(keys, i.(btreeString).s)
  81. return len(keys) < n
  82. }
  83. i.BTree.AscendGreaterOrEqual(btreeFrom, iterator)
  84. if skipFirst && len(keys) > 0 {
  85. keys = keys[1:]
  86. }
  87. return keys
  88. }
  89. // rebuildIndex does the work of regenerating the index
  90. // with the given keys.
  91. func rebuild(less LessFunction, keys <-chan string) *btree.BTree {
  92. tree := btree.New(2)
  93. for key := range keys {
  94. tree.ReplaceOrInsert(btreeString{s: key, l: less})
  95. }
  96. return tree
  97. }