github.com/vuuihc/gocedar@v0.1.0/api.go (about) 1 // Copyright 2016 Evans. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package cedar 6 7 import ( 8 "errors" 9 ) 10 11 var ( 12 // ErrNoKey not have key error 13 ErrNoKey = errors.New("cedar: not have key") 14 // ErrNoVal not have value error 15 ErrNoVal = errors.New("cedar: not have val") 16 // ErrInvalidKey invalid key error 17 ErrInvalidKey = errors.New("cedar: invalid key") 18 // ErrInvalidVal invalid value error 19 ErrInvalidVal = errors.New("cedar: invalid val") 20 ) 21 22 func isReduced(reduced ...bool) bool { 23 if len(reduced) > 0 && !reduced[0] { 24 return false 25 } 26 27 return true 28 } 29 30 func (cd *Cedar) get(key []byte, from, pos int) *int { 31 to := cd.getNode(key, from, pos) 32 return &cd.array[to].baseV 33 } 34 35 // getNode get the follow node by key, split by update() 36 func (cd *Cedar) getNode(key []byte, from, pos int) int { 37 for ; pos < len(key); pos++ { 38 if cd.Reduced { 39 value := cd.array[from].baseV 40 if value >= 0 && value != ValLimit { 41 to := cd.follow(from, 0) 42 cd.array[to].baseV = value 43 } 44 } 45 46 from = cd.follow(from, key[pos]) 47 } 48 49 to := from 50 if cd.array[from].baseV < 0 || !cd.Reduced { 51 to = cd.follow(from, 0) 52 } 53 54 return to 55 } 56 57 // Jump jump a node `from` to another node by following the `path`, split by find() 58 func (cd *Cedar) Jump(key []byte, from int) (to int, err error) { 59 // pos := 0 60 // recursively matching the key. 61 for _, k := range key { 62 if cd.array[from].baseV >= 0 && cd.Reduced { 63 return from, ErrNoKey 64 } 65 66 to = cd.array[from].base(cd.Reduced) ^ int(k) 67 if cd.array[to].check != from { 68 return from, ErrNoKey 69 } 70 from = to 71 } 72 73 return to, nil 74 } 75 76 // Find key from double array trie, with `from` as the cursor to traverse the nodes. 77 func (cd *Cedar) Find(key []byte, from int) (int, error) { 78 to, err := cd.Jump(key, from) 79 if cd.Reduced { 80 if cd.array[from].baseV >= 0 { 81 if err == nil && to != 0 { 82 return cd.array[to].baseV, nil 83 } 84 return 0, ErrNoKey 85 } 86 } 87 88 // return the value of the node if `check` is correctly marked fpr the ownership, 89 // otherwise it means no value is stored. 90 n := cd.array[cd.array[to].base(cd.Reduced)] 91 if n.check != to { 92 return 0, ErrNoKey 93 } 94 return n.baseV, nil 95 } 96 97 // Value get the path value 98 func (cd *Cedar) Value(path int) (val int, err error) { 99 val = cd.array[path].baseV 100 if val >= 0 { 101 return val, nil 102 } 103 104 to := cd.array[path].base(cd.Reduced) 105 if cd.array[to].check == path && cd.array[to].baseV >= 0 { 106 return cd.array[to].baseV, nil 107 } 108 109 return 0, ErrNoVal 110 } 111 112 // Insert the key for the value on []byte 113 func (cd *Cedar) Insert(key []byte, val int) error { 114 if val < 0 || val >= ValLimit { 115 return ErrInvalidVal 116 } 117 118 p := cd.get(key, 0, 0) 119 *p = val 120 121 return nil 122 } 123 124 // Update the key for the value, it is public interface that works on []byte 125 func (cd *Cedar) Update(key []byte, value int) error { 126 p := cd.get(key, 0, 0) 127 128 if *p == ValLimit && cd.Reduced { 129 *p = value 130 return nil 131 } 132 133 *p += value 134 return nil 135 } 136 137 // Delete the key from the trie, the internal interface that works on []byte 138 func (cd *Cedar) Delete(key []byte) error { 139 // move the cursor to the right place and use erase__ to delete it. 140 to, err := cd.Jump(key, 0) 141 if err != nil { 142 return ErrNoKey 143 } 144 145 if cd.array[to].baseV < 0 && cd.Reduced { 146 base := cd.array[to].base(cd.Reduced) 147 if cd.array[base].check == to { 148 to = base 149 } 150 } 151 152 if !cd.Reduced { 153 to = cd.array[to].base(cd.Reduced) 154 } 155 156 from := to 157 for to > 0 { 158 if cd.Reduced { 159 from = cd.array[to].check 160 } 161 base := cd.array[from].base(cd.Reduced) 162 label := byte(to ^ base) 163 164 hasSibling := cd.nInfos[to].sibling != 0 || cd.nInfos[from].child != label 165 // if the node has siblings, then remove `e` from the sibling. 166 if hasSibling { 167 cd.popSibling(from, base, label) 168 } 169 170 // maintain the data structures. 171 cd.pushENode(to) 172 // traverse to the parent. 173 to = from 174 175 // if it has sibling then this layer has more than one nodes, then we are done. 176 if hasSibling { 177 break 178 } 179 } 180 181 return nil 182 } 183 184 // Get get the key value on []byte 185 func (cd *Cedar) Get(key []byte) (value int, err error) { 186 to, err := cd.Jump(key, 0) 187 if err != nil { 188 return 0, err 189 } 190 191 return cd.Value(to) 192 } 193 194 // ExactMatch to check if `key` is in the dictionary. 195 func (cd *Cedar) ExactMatch(key []byte) (int, bool) { 196 from := 0 197 val, err := cd.Find(key, from) 198 if err != nil { 199 return 0, false 200 } 201 return val, true 202 } 203 204 // PrefixMatch return the collection of the common prefix 205 // in the dictionary with the `key` 206 func (cd *Cedar) PrefixMatch(key []byte, n ...int) (ids []int) { 207 num := 0 208 if len(n) > 0 { 209 num = n[0] 210 } 211 212 for from, i := 0, 0; i < len(key); i++ { 213 to, err := cd.Jump(key[i:i+1], from) 214 if err != nil { 215 break 216 } 217 218 _, err = cd.Value(to) 219 if err == nil { 220 ids = append(ids, to) 221 num-- 222 if num == 0 { 223 return 224 } 225 } 226 227 from = to 228 } 229 230 return 231 } 232 233 // PrefixPredict eturn the list of words in the dictionary 234 // that has `key` as their prefix 235 func (cd *Cedar) PrefixPredict(key []byte, n ...int) (ids []int) { 236 num := 0 237 if len(n) > 0 { 238 num = n[0] 239 } 240 241 root, err := cd.Jump(key, 0) 242 if err != nil { 243 return 244 } 245 246 for from, err := cd.begin(root); err == nil; from, err = cd.next(from, root) { 247 ids = append(ids, from) 248 num-- 249 if num == 0 { 250 return 251 } 252 } 253 254 return 255 } 256 257 // To get the cursor of the first leaf node starting by `from` 258 func (cd *Cedar) begin(from int) (to int, err error) { 259 // recursively traversing down to look for the first leaf. 260 for c := cd.nInfos[from].child; c != 0; { 261 from = cd.array[from].base(cd.Reduced) ^ int(c) 262 c = cd.nInfos[from].child 263 } 264 265 if cd.array[from].base() > 0 { 266 return cd.array[from].base(), nil 267 } 268 269 // To return the value of the leaf. 270 return from, nil 271 } 272 273 // To move the cursor from one leaf to the next for the common prefix predict. 274 func (cd *Cedar) next(from int, root int) (to int, err error) { 275 c := cd.nInfos[from].sibling 276 if !cd.Reduced { 277 c = cd.nInfos[cd.array[from].base(cd.Reduced)].sibling 278 } 279 280 // traversing up until there is a sibling or it has reached the root. 281 for c == 0 && from != root && cd.array[from].check >= 0 { 282 from = cd.array[from].check 283 c = cd.nInfos[from].sibling 284 } 285 286 if from == root || cd.array[from].check < 0 { 287 return 0, ErrNoKey 288 } 289 290 // it has a sibling so we leverage on `begin` to traverse the subtree down again. 291 from = cd.array[cd.array[from].check].base(cd.Reduced) ^ int(c) 292 return cd.begin(from) 293 }