trie

package module
v0.0.0-...-c4e3432 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 18, 2019 License: Apache-2.0 Imports: 4 Imported by: 0

README

Trieste

Trieste is an Italian coastal and historical frontier city partially enclosed by Slovenia. Why not visit?

Trieste is also a sparse prefix tree library for Go based on ideas from crit-bit and qp tries.

Status

This is currently a prototype.

Building

You can get the dependencies necessary for building this library by installing dep and running:

dep ensure -v

Purpose

This work is motivated by the need for prefix-addressable and Merkle tree storage in Hyperledger Burrow, but if it ever becomes useful might have general applicability. It aims to provide a reasonably compressed and fast sorted, range-iterable, prefix tree with a full-byte branching factor of 256 (technically 257 including a distinguished terminal). It compresses paths in the tree (from root to leaf) by only introducing nodes on the 'critical byte' on which keys differ and it keeps down storage space by implementing a sparse map of child nodes for a branch using a bitset.

Future

Burrow has some uses for a basic in-memory version of this tree to provide sorted caching layers, but further extensions may be of interest:

  • Providing a lazy Merkle tree hash where hashes are stable over prefixes
  • Providing a persistent version where nodes are written immutably to a storage backend
  • Writing to disc or a memory-mapped file to provide a prefix tree database
  • Providing various kinds of proof to facilitate (among other things) blockchain light clients

See the excellent and more mature Tendermint IAVL library with similar aims.

See also the go-ethereum trie package based also on a condensed trie (sometimes called PATRICIA) with support for proofs and Merkle hashes, with a 16 + 1 branching factor, but no sparse array.

Why bother?

Surprisingly few standalone trie libraries exist for Go. IAVL is good but not so efficient for in-memory storage and because of the way balacning happens AVL is order-dependent and does not maintain stable hashes for prefixes.

A common data structure in-memory and on-disc may be possible with some advantages for serialisation. Also it might be nice to have the tree structure as the native database structure rather than implementing an prefix/AVL tree of a B+ tree (or whatever the database layer is).

Documentation

Index

Constants

View Source
const BranchingFactor uint = 257

Branch on byte plus special terminal character

View Source
const TerminalChar = 0

Variables

This section is empty.

Functions

This section is empty.

Types

type Address

type Address interface{}

type Branch

type Branch struct {
	// contains filtered or unexported fields
}

A Branch or internal node consists of an index which is the index of the key byte on which this branch 'decides'. A Branch implements a sparse array of children with one possible slot for each possible character branch. The Leaf node, Leaf[K,V], for a particular key can be found under the subtree rooted at the child in position C when charAt(K[Branch.index]) == C.

func (*Branch) Add

func (tb *Branch) Add(char uint, node *Node)

func (*Branch) Remove

func (tb *Branch) Remove(char uint)

type HeightNode

type HeightNode struct {
	*Node
	Height      int
	ParentIndex int
}

type Leaf

type Leaf struct {
	// contains filtered or unexported fields
}

type Node

type Node struct {
	*Branch
	*Leaf
}

We model Node as a sum type. Either Node should be nil, contain just a non-nil Branch, or contain just a non-nil Leaf

func NewBranch

func NewBranch(index int) *Node

func (*Node) BreadthFirstSearch

func (tn *Node) BreadthFirstSearch(callback func(*HeightNode) error) error

func (*Node) Children

func (tn *Node) Children() []*Node

func (*Node) Descend

func (tn *Node) Descend(key []byte) (branch *Node, char uint, child *Node)

Descend implements the basic traversal of the trie. It returns the deepest directed edge, parent --char--> child, accessible by following key on each critical index along the path of Branches. child may be nil indicating the slot for char on branch is unoccupied. Note: we use the Node container for branch as a convenience for mutation - it is guaranteed to be a Branch Node

func (*Node) IsBranch

func (tn *Node) IsBranch() bool

func (*Node) IsLeaf

func (tn *Node) IsLeaf() bool

func (*Node) String

func (tn *Node) String() string

type Trie

type Trie struct {
	*Node
}

func NewTrie

func NewTrie() *Trie

func (*Trie) Delete

func (trie *Trie) Delete(key []byte) (deleted bool)

func (*Trie) Dump

func (trie *Trie) Dump() string

func (*Trie) Get

func (trie *Trie) Get(key []byte) (value interface{}, exists bool)

func (*Trie) Set

func (trie *Trie) Set(key []byte, value interface{}) (updated bool)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL