github.com/go-ego/cedar@v0.10.2/api.go (about) 1 package cedar 2 3 // Status reports the following statistics of the cedar: 4 // keys: number of keys that are in the cedar, 5 // nodes: number of trie nodes (slots in the base array) has been taken, 6 // size: the size of the base array used by the cedar, 7 // capacity: the capicity of the base array used by the cedar. 8 func (da *Cedar) Status() (keys, nodes, size, capacity int) { 9 for i := 0; i < da.Size; i++ { 10 n := da.Array[i] 11 if n.Check >= 0 { 12 nodes++ 13 14 if n.Value >= 0 { 15 keys++ 16 } 17 } 18 } 19 20 return keys, nodes, da.Size, da.Capacity 21 } 22 23 // Jump travels from a node `from` to another node 24 // `to` by following the path `path`. 25 // For example, if the following keys were inserted: 26 // id key 27 // 19 abc 28 // 23 ab 29 // 37 abcd 30 // then 31 // Jump([]byte("ab"), 0) = 23, nil // reach "ab" from root 32 // Jump([]byte("c"), 23) = 19, nil // reach "abc" from "ab" 33 // Jump([]byte("cd"), 23) = 37, nil // reach "abcd" from "ab" 34 func (da *Cedar) Jump(path []byte, from int) (to int, err error) { 35 for _, b := range path { 36 if da.Array[from].Value >= 0 { 37 return from, ErrNoPath 38 } 39 40 to = da.Array[from].base() ^ int(b) 41 if da.Array[to].Check != from { 42 return from, ErrNoPath 43 } 44 from = to 45 } 46 47 return to, nil 48 } 49 50 // Key returns the key of the node with the given `id`. 51 // It will return ErrNoPath, if the node does not exist. 52 func (da *Cedar) Key(id int) (key []byte, err error) { 53 for id > 0 { 54 from := da.Array[id].Check 55 if from < 0 { 56 return nil, ErrNoPath 57 } 58 59 if char := byte(da.Array[from].base() ^ id); char != 0 { 60 key = append(key, char) 61 } 62 id = from 63 } 64 65 if id != 0 || len(key) == 0 { 66 return nil, ErrInvalidKey 67 } 68 69 for i := 0; i < len(key)/2; i++ { 70 key[i], key[len(key)-i-1] = key[len(key)-i-1], key[i] 71 } 72 73 return key, nil 74 } 75 76 // Value returns the value of the node with the given `id`. 77 // It will return ErrNoValue, if the node does not have a value. 78 func (da *Cedar) Value(id int) (value int, err error) { 79 value = da.Array[id].Value 80 if value >= 0 { 81 return value, nil 82 } 83 84 to := da.Array[id].base() 85 if da.Array[to].Check == id && da.Array[to].Value >= 0 { 86 return da.Array[to].Value, nil 87 } 88 89 return 0, ErrNoValue 90 } 91 92 // Insert adds a key-value pair into the cedar. 93 // It will return ErrInvalidValue, if value < 0 or >= ValueLimit. 94 func (da *Cedar) Insert(key []byte, value int) error { 95 if value < 0 || value >= ValueLimit { 96 return ErrInvalidValue 97 } 98 99 p := da.get(key, 0, 0) 100 *p = value 101 102 return nil 103 } 104 105 // Update increases the value associated with the `key`. 106 // The `key` will be inserted if it is not in the cedar. 107 // It will return ErrInvalidValue, if the updated value < 0 or >= ValueLimit. 108 func (da *Cedar) Update(key []byte, value int) error { 109 p := da.get(key, 0, 0) 110 111 // key was not inserted 112 if *p == ValueLimit { 113 *p = value 114 return nil 115 } 116 117 // key was inserted before 118 if *p+value < 0 || *p+value >= ValueLimit { 119 return ErrInvalidValue 120 } 121 *p += value 122 123 return nil 124 } 125 126 // Delete removes a key-value pair from the cedar. 127 // It will return ErrNoPath, if the key has not been added. 128 func (da *Cedar) Delete(key []byte) error { 129 // if the path does not exist, or the end is not a leaf, 130 // nothing to delete 131 to, err := da.Jump(key, 0) 132 if err != nil { 133 return ErrNoPath 134 } 135 136 if da.Array[to].Value < 0 { 137 base := da.Array[to].base() 138 if da.Array[base].Check == to { 139 to = base 140 } 141 } 142 143 for to > 0 { 144 from := da.Array[to].Check 145 base := da.Array[from].base() 146 label := byte(to ^ base) 147 148 // if `to` has sibling, remove `to` from the sibling list, then stop 149 if da.Ninfos[to].Sibling != 0 || da.Ninfos[from].Child != label { 150 // delete the label from the child ring first 151 da.popSibling(from, base, label) 152 // then release the current node `to` to the empty node ring 153 da.pushEnode(to) 154 break 155 } 156 157 // otherwise, just release the current node `to` to the empty node ring 158 da.pushEnode(to) 159 // then check its parent node 160 to = from 161 } 162 163 return nil 164 } 165 166 // Get returns the value associated with the given `key`. 167 // It is equivalent to 168 // id, err1 = Jump(key) 169 // value, err2 = Value(id) 170 // Thus, it may return ErrNoPath or ErrNoValue, 171 func (da *Cedar) Get(key []byte) (value int, err error) { 172 to, err := da.Jump(key, 0) 173 if err != nil { 174 return 0, err 175 } 176 177 return da.Value(to) 178 } 179 180 // PrefixMatch returns a list of at most `num` nodes 181 // which match the prefix of the key. 182 // If `num` is 0, it returns all matches. 183 // For example, if the following keys were inserted: 184 // id key 185 // 19 abc 186 // 23 ab 187 // 37 abcd 188 // then 189 // PrefixMatch([]byte("abc"), 1) = [ 23 ] // match ["ab"] 190 // PrefixMatch([]byte("abcd"), 0) = [ 23, 19, 37] 191 // match ["ab", "abc", "abcd"] 192 func (da *Cedar) PrefixMatch(key []byte, num int) (ids []int) { 193 for from, i := 0, 0; i < len(key); i++ { 194 to, err := da.Jump(key[i:i+1], from) 195 if err != nil { 196 break 197 } 198 199 if _, err := da.Value(to); err == nil { 200 ids = append(ids, to) 201 num-- 202 if num == 0 { 203 return 204 } 205 } 206 207 from = to 208 } 209 210 return 211 } 212 213 // PrefixPredict returns a list of at most `num` nodes 214 // which has the key as their prefix. 215 // These nodes are ordered by their keys. 216 // If `num` is 0, it returns all matches. 217 // For example, if the following keys were inserted: 218 // id key 219 // 19 abc 220 // 23 ab 221 // 37 abcd 222 // then 223 // PrefixPredict([]byte("ab"), 2) = [ 23, 19 ] // predict ["ab", "abc"] 224 // PrefixPredict([]byte("ab"), 0) = [ 23, 19, 37 ] 225 // predict ["ab", "abc", "abcd"] 226 func (da *Cedar) PrefixPredict(key []byte, num int) (ids []int) { 227 root, err := da.Jump(key, 0) 228 if err != nil { 229 return 230 } 231 232 for from, err := da.begin(root); err == nil; from, err = da.next(from, root) { 233 ids = append(ids, from) 234 num-- 235 if num == 0 { 236 return 237 } 238 } 239 240 return 241 } 242 243 func (da *Cedar) begin(from int) (to int, err error) { 244 for c := da.Ninfos[from].Child; c != 0; { 245 to = da.Array[from].base() ^ int(c) 246 c = da.Ninfos[to].Child 247 from = to 248 } 249 250 if da.Array[from].base() > 0 { 251 return da.Array[from].base(), nil 252 } 253 254 return from, nil 255 } 256 257 func (da *Cedar) next(from int, root int) (to int, err error) { 258 c := da.Ninfos[from].Sibling 259 for c == 0 && from != root && da.Array[from].Check >= 0 { 260 from = da.Array[from].Check 261 c = da.Ninfos[from].Sibling 262 } 263 264 if from == root { 265 return 0, ErrNoPath 266 } 267 from = da.Array[da.Array[from].Check].base() ^ int(c) 268 269 return da.begin(from) 270 }