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  }