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  }