mirror of
				https://github.com/minio/minio.git
				synced 2025-10-30 00:05:02 -04:00 
			
		
		
		
	In memory object storage now uses patricia tree
This commit is contained in:
		
							parent
							
								
									0ed2aeb439
								
							
						
					
					
						commit
						e0b9a59ad4
					
				
							
								
								
									
										5
									
								
								Godeps/Godeps.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										5
									
								
								Godeps/Godeps.json
									
									
									
										generated
									
									
									
								
							| @ -18,6 +18,11 @@ | |||||||
| 			"ImportPath": "github.com/gorilla/mux", | 			"ImportPath": "github.com/gorilla/mux", | ||||||
| 			"Rev": "e444e69cbd2e2e3e0749a2f3c717cec491552bbf" | 			"Rev": "e444e69cbd2e2e3e0749a2f3c717cec491552bbf" | ||||||
| 		}, | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "github.com/tchap/go-patricia/patricia", | ||||||
|  | 			"Comment": "v1.0.1", | ||||||
|  | 			"Rev": "f64d0a63cd3363481c898faa9339de04d12213f9" | ||||||
|  | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			"ImportPath": "gopkg.in/check.v1", | 			"ImportPath": "gopkg.in/check.v1", | ||||||
| 			"Rev": "64131543e7896d5bcc6bd5a76287eb75ea96c673" | 			"Rev": "64131543e7896d5bcc6bd5a76287eb75ea96c673" | ||||||
|  | |||||||
							
								
								
									
										230
									
								
								Godeps/_workspace/src/github.com/tchap/go-patricia/patricia/children.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										230
									
								
								Godeps/_workspace/src/github.com/tchap/go-patricia/patricia/children.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,230 @@ | |||||||
|  | // Copyright (c) 2014 The go-patricia AUTHORS | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by The MIT License | ||||||
|  | // that can be found in the LICENSE file. | ||||||
|  | 
 | ||||||
|  | package patricia | ||||||
|  | 
 | ||||||
|  | // Max prefix length that is kept in a single trie node. | ||||||
|  | var MaxPrefixPerNode = 10 | ||||||
|  | 
 | ||||||
|  | // Max children to keep in a node in the sparse mode. | ||||||
|  | const MaxChildrenPerSparseNode = 8 | ||||||
|  | 
 | ||||||
|  | type childList interface { | ||||||
|  | 	length() int | ||||||
|  | 	head() *Trie | ||||||
|  | 	add(child *Trie) childList | ||||||
|  | 	replace(b byte, child *Trie) | ||||||
|  | 	remove(child *Trie) | ||||||
|  | 	next(b byte) *Trie | ||||||
|  | 	walk(prefix *Prefix, visitor VisitorFunc) error | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type sparseChildList struct { | ||||||
|  | 	children []*Trie | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func newSparseChildList() childList { | ||||||
|  | 	return &sparseChildList{ | ||||||
|  | 		children: make([]*Trie, 0, MaxChildrenPerSparseNode), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (list *sparseChildList) length() int { | ||||||
|  | 	return len(list.children) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (list *sparseChildList) head() *Trie { | ||||||
|  | 	return list.children[0] | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (list *sparseChildList) add(child *Trie) childList { | ||||||
|  | 	// Search for an empty spot and insert the child if possible. | ||||||
|  | 	if len(list.children) != cap(list.children) { | ||||||
|  | 		list.children = append(list.children, child) | ||||||
|  | 		return list | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Otherwise we have to transform to the dense list type. | ||||||
|  | 	return newDenseChildList(list, child) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (list *sparseChildList) replace(b byte, child *Trie) { | ||||||
|  | 	// Seek the child and replace it. | ||||||
|  | 	for i, node := range list.children { | ||||||
|  | 		if node.prefix[0] == b { | ||||||
|  | 			list.children[i] = child | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (list *sparseChildList) remove(child *Trie) { | ||||||
|  | 	for i, node := range list.children { | ||||||
|  | 		if node.prefix[0] == child.prefix[0] { | ||||||
|  | 			list.children = append(list.children[:i], list.children[i+1:]...) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// This is not supposed to be reached. | ||||||
|  | 	panic("removing non-existent child") | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (list *sparseChildList) next(b byte) *Trie { | ||||||
|  | 	for _, child := range list.children { | ||||||
|  | 		if child.prefix[0] == b { | ||||||
|  | 			return child | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (list *sparseChildList) walk(prefix *Prefix, visitor VisitorFunc) error { | ||||||
|  | 	for _, child := range list.children { | ||||||
|  | 		*prefix = append(*prefix, child.prefix...) | ||||||
|  | 		if child.item != nil { | ||||||
|  | 			err := visitor(*prefix, child.item) | ||||||
|  | 			if err != nil { | ||||||
|  | 				if err == SkipSubtree { | ||||||
|  | 					*prefix = (*prefix)[:len(*prefix)-len(child.prefix)] | ||||||
|  | 					continue | ||||||
|  | 				} | ||||||
|  | 				*prefix = (*prefix)[:len(*prefix)-len(child.prefix)] | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		err := child.children.walk(prefix, visitor) | ||||||
|  | 		*prefix = (*prefix)[:len(*prefix)-len(child.prefix)] | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type denseChildList struct { | ||||||
|  | 	min      int | ||||||
|  | 	max      int | ||||||
|  | 	children []*Trie | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func newDenseChildList(list *sparseChildList, child *Trie) childList { | ||||||
|  | 	var ( | ||||||
|  | 		min int = 255 | ||||||
|  | 		max int = 0 | ||||||
|  | 	) | ||||||
|  | 	for _, child := range list.children { | ||||||
|  | 		b := int(child.prefix[0]) | ||||||
|  | 		if b < min { | ||||||
|  | 			min = b | ||||||
|  | 		} | ||||||
|  | 		if b > max { | ||||||
|  | 			max = b | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	b := int(child.prefix[0]) | ||||||
|  | 	if b < min { | ||||||
|  | 		min = b | ||||||
|  | 	} | ||||||
|  | 	if b > max { | ||||||
|  | 		max = b | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	children := make([]*Trie, max-min+1) | ||||||
|  | 	for _, child := range list.children { | ||||||
|  | 		children[int(child.prefix[0])-min] = child | ||||||
|  | 	} | ||||||
|  | 	children[int(child.prefix[0])-min] = child | ||||||
|  | 
 | ||||||
|  | 	return &denseChildList{min, max, children} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (list *denseChildList) length() int { | ||||||
|  | 	return list.max - list.min + 1 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (list *denseChildList) head() *Trie { | ||||||
|  | 	return list.children[0] | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (list *denseChildList) add(child *Trie) childList { | ||||||
|  | 	b := int(child.prefix[0]) | ||||||
|  | 
 | ||||||
|  | 	switch { | ||||||
|  | 	case list.min <= b && b <= list.max: | ||||||
|  | 		if list.children[b-list.min] != nil { | ||||||
|  | 			panic("dense child list collision detected") | ||||||
|  | 		} | ||||||
|  | 		list.children[b-list.min] = child | ||||||
|  | 
 | ||||||
|  | 	case b < list.min: | ||||||
|  | 		children := make([]*Trie, list.max-b+1) | ||||||
|  | 		children[0] = child | ||||||
|  | 		copy(children[list.min-b:], list.children) | ||||||
|  | 		list.children = children | ||||||
|  | 		list.min = b | ||||||
|  | 
 | ||||||
|  | 	default: // b > list.max | ||||||
|  | 		children := make([]*Trie, b-list.min+1) | ||||||
|  | 		children[b-list.min] = child | ||||||
|  | 		copy(children, list.children) | ||||||
|  | 		list.children = children | ||||||
|  | 		list.max = b | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return list | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (list *denseChildList) replace(b byte, child *Trie) { | ||||||
|  | 	list.children[int(b)-list.min] = nil | ||||||
|  | 	list.children[int(child.prefix[0])-list.min] = child | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (list *denseChildList) remove(child *Trie) { | ||||||
|  | 	i := int(child.prefix[0]) - list.min | ||||||
|  | 	if list.children[i] == nil { | ||||||
|  | 		// This is not supposed to be reached. | ||||||
|  | 		panic("removing non-existent child") | ||||||
|  | 	} | ||||||
|  | 	list.children[i] = nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (list *denseChildList) next(b byte) *Trie { | ||||||
|  | 	i := int(b) | ||||||
|  | 	if i < list.min || list.max < i { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	return list.children[i-list.min] | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (list *denseChildList) walk(prefix *Prefix, visitor VisitorFunc) error { | ||||||
|  | 	for _, child := range list.children { | ||||||
|  | 		if child == nil { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		*prefix = append(*prefix, child.prefix...) | ||||||
|  | 		if child.item != nil { | ||||||
|  | 			if err := visitor(*prefix, child.item); err != nil { | ||||||
|  | 				if err == SkipSubtree { | ||||||
|  | 					*prefix = (*prefix)[:len(*prefix)-len(child.prefix)] | ||||||
|  | 					continue | ||||||
|  | 				} | ||||||
|  | 				*prefix = (*prefix)[:len(*prefix)-len(child.prefix)] | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		err := child.children.walk(prefix, visitor) | ||||||
|  | 		*prefix = (*prefix)[:len(*prefix)-len(child.prefix)] | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										432
									
								
								Godeps/_workspace/src/github.com/tchap/go-patricia/patricia/patricia.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										432
									
								
								Godeps/_workspace/src/github.com/tchap/go-patricia/patricia/patricia.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,432 @@ | |||||||
|  | // Copyright (c) 2014 The go-patricia AUTHORS | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by The MIT License | ||||||
|  | // that can be found in the LICENSE file. | ||||||
|  | 
 | ||||||
|  | package patricia | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | // Trie | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | 
 | ||||||
|  | type ( | ||||||
|  | 	Prefix      []byte | ||||||
|  | 	Item        interface{} | ||||||
|  | 	VisitorFunc func(prefix Prefix, item Item) error | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Trie is a generic patricia trie that allows fast retrieval of items by prefix. | ||||||
|  | // and other funky stuff. | ||||||
|  | // | ||||||
|  | // Trie is not thread-safe. | ||||||
|  | type Trie struct { | ||||||
|  | 	prefix Prefix | ||||||
|  | 	item   Item | ||||||
|  | 
 | ||||||
|  | 	children childList | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Public API ------------------------------------------------------------------ | ||||||
|  | 
 | ||||||
|  | // Trie constructor. | ||||||
|  | func NewTrie() *Trie { | ||||||
|  | 	return &Trie{ | ||||||
|  | 		children: newSparseChildList(), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Item returns the item stored in the root of this trie. | ||||||
|  | func (trie *Trie) Item() Item { | ||||||
|  | 	return trie.item | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Insert inserts a new item into the trie using the given prefix. Insert does | ||||||
|  | // not replace existing items. It returns false if an item was already in place. | ||||||
|  | func (trie *Trie) Insert(key Prefix, item Item) (inserted bool) { | ||||||
|  | 	return trie.put(key, item, false) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Set works much like Insert, but it always sets the item, possibly replacing | ||||||
|  | // the item previously inserted. | ||||||
|  | func (trie *Trie) Set(key Prefix, item Item) { | ||||||
|  | 	trie.put(key, item, true) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Get returns the item located at key. | ||||||
|  | // | ||||||
|  | // This method is a bit dangerous, because Get can as well end up in an internal | ||||||
|  | // node that is not really representing any user-defined value. So when nil is | ||||||
|  | // a valid value being used, it is not possible to tell if the value was inserted | ||||||
|  | // into the tree by the user or not. A possible workaround for this is not to use | ||||||
|  | // nil interface as a valid value, even using zero value of any type is enough | ||||||
|  | // to prevent this bad behaviour. | ||||||
|  | func (trie *Trie) Get(key Prefix) (item Item) { | ||||||
|  | 	_, node, found, leftover := trie.findSubtree(key) | ||||||
|  | 	if !found || len(leftover) != 0 { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	return node.item | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Match returns what Get(prefix) != nil would return. The same warning as for | ||||||
|  | // Get applies here as well. | ||||||
|  | func (trie *Trie) Match(prefix Prefix) (matchedExactly bool) { | ||||||
|  | 	return trie.Get(prefix) != nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // MatchSubtree returns true when there is a subtree representing extensions | ||||||
|  | // to key, that is if there are any keys in the tree which have key as prefix. | ||||||
|  | func (trie *Trie) MatchSubtree(key Prefix) (matched bool) { | ||||||
|  | 	_, _, matched, _ = trie.findSubtree(key) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Visit calls visitor on every node containing a non-nil item. | ||||||
|  | // | ||||||
|  | // If an error is returned from visitor, the function stops visiting the tree | ||||||
|  | // and returns that error, unless it is a special error - SkipSubtree. In that | ||||||
|  | // case Visit skips the subtree represented by the current node and continues | ||||||
|  | // elsewhere. | ||||||
|  | func (trie *Trie) Visit(visitor VisitorFunc) error { | ||||||
|  | 	return trie.walk(nil, visitor) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // VisitSubtree works much like Visit, but it only visits nodes matching prefix. | ||||||
|  | func (trie *Trie) VisitSubtree(prefix Prefix, visitor VisitorFunc) error { | ||||||
|  | 	// Nil prefix not allowed. | ||||||
|  | 	if prefix == nil { | ||||||
|  | 		panic(ErrNilPrefix) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Empty trie must be handled explicitly. | ||||||
|  | 	if trie.prefix == nil { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Locate the relevant subtree. | ||||||
|  | 	_, root, found, leftover := trie.findSubtree(prefix) | ||||||
|  | 	if !found { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	prefix = append(prefix, leftover...) | ||||||
|  | 
 | ||||||
|  | 	// Visit it. | ||||||
|  | 	return root.walk(prefix, visitor) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // VisitPrefixes visits only nodes that represent prefixes of key. | ||||||
|  | // To say the obvious, returning SkipSubtree from visitor makes no sense here. | ||||||
|  | func (trie *Trie) VisitPrefixes(key Prefix, visitor VisitorFunc) error { | ||||||
|  | 	// Nil key not allowed. | ||||||
|  | 	if key == nil { | ||||||
|  | 		panic(ErrNilPrefix) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Empty trie must be handled explicitly. | ||||||
|  | 	if trie.prefix == nil { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Walk the path matching key prefixes. | ||||||
|  | 	node := trie | ||||||
|  | 	prefix := key | ||||||
|  | 	offset := 0 | ||||||
|  | 	for { | ||||||
|  | 		// Compute what part of prefix matches. | ||||||
|  | 		common := node.longestCommonPrefixLength(key) | ||||||
|  | 		key = key[common:] | ||||||
|  | 		offset += common | ||||||
|  | 
 | ||||||
|  | 		// Partial match means that there is no subtree matching prefix. | ||||||
|  | 		if common < len(node.prefix) { | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// Call the visitor. | ||||||
|  | 		if item := node.item; item != nil { | ||||||
|  | 			if err := visitor(prefix[:offset], item); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if len(key) == 0 { | ||||||
|  | 			// This node represents key, we are finished. | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// There is some key suffix left, move to the children. | ||||||
|  | 		child := node.children.next(key[0]) | ||||||
|  | 		if child == nil { | ||||||
|  | 			// There is nowhere to continue, return. | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		node = child | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Delete deletes the item represented by the given prefix. | ||||||
|  | // | ||||||
|  | // True is returned if the matching node was found and deleted. | ||||||
|  | func (trie *Trie) Delete(key Prefix) (deleted bool) { | ||||||
|  | 	// Nil prefix not allowed. | ||||||
|  | 	if key == nil { | ||||||
|  | 		panic(ErrNilPrefix) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Empty trie must be handled explicitly. | ||||||
|  | 	if trie.prefix == nil { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Find the relevant node. | ||||||
|  | 	parent, node, _, leftover := trie.findSubtree(key) | ||||||
|  | 	if len(leftover) != 0 { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// If the item is already set to nil, there is nothing to do. | ||||||
|  | 	if node.item == nil { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Delete the item. | ||||||
|  | 	node.item = nil | ||||||
|  | 
 | ||||||
|  | 	// Compact since that might be possible now. | ||||||
|  | 	if compacted := node.compact(); compacted != node { | ||||||
|  | 		if parent == nil { | ||||||
|  | 			*node = *compacted | ||||||
|  | 		} else { | ||||||
|  | 			parent.children.replace(node.prefix[0], compacted) | ||||||
|  | 			*parent = *parent.compact() | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // DeleteSubtree finds the subtree exactly matching prefix and deletes it. | ||||||
|  | // | ||||||
|  | // True is returned if the subtree was found and deleted. | ||||||
|  | func (trie *Trie) DeleteSubtree(prefix Prefix) (deleted bool) { | ||||||
|  | 	// Nil prefix not allowed. | ||||||
|  | 	if prefix == nil { | ||||||
|  | 		panic(ErrNilPrefix) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Empty trie must be handled explicitly. | ||||||
|  | 	if trie.prefix == nil { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Locate the relevant subtree. | ||||||
|  | 	parent, root, found, _ := trie.findSubtree(prefix) | ||||||
|  | 	if !found { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// If we are in the root of the trie, reset the trie. | ||||||
|  | 	if parent == nil { | ||||||
|  | 		root.prefix = nil | ||||||
|  | 		root.children = newSparseChildList() | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Otherwise remove the root node from its parent. | ||||||
|  | 	parent.children.remove(root) | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Internal helper methods ----------------------------------------------------- | ||||||
|  | 
 | ||||||
|  | func (trie *Trie) put(key Prefix, item Item, replace bool) (inserted bool) { | ||||||
|  | 	// Nil prefix not allowed. | ||||||
|  | 	if key == nil { | ||||||
|  | 		panic(ErrNilPrefix) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var ( | ||||||
|  | 		common int | ||||||
|  | 		node   *Trie = trie | ||||||
|  | 		child  *Trie | ||||||
|  | 	) | ||||||
|  | 
 | ||||||
|  | 	if node.prefix == nil { | ||||||
|  | 		if len(key) <= MaxPrefixPerNode { | ||||||
|  | 			node.prefix = key | ||||||
|  | 			goto InsertItem | ||||||
|  | 		} | ||||||
|  | 		node.prefix = key[:MaxPrefixPerNode] | ||||||
|  | 		key = key[MaxPrefixPerNode:] | ||||||
|  | 		goto AppendChild | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for { | ||||||
|  | 		// Compute the longest common prefix length. | ||||||
|  | 		common = node.longestCommonPrefixLength(key) | ||||||
|  | 		key = key[common:] | ||||||
|  | 
 | ||||||
|  | 		// Only a part matches, split. | ||||||
|  | 		if common < len(node.prefix) { | ||||||
|  | 			goto SplitPrefix | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// common == len(node.prefix) since never (common > len(node.prefix)) | ||||||
|  | 		// common == len(former key) <-> 0 == len(key) | ||||||
|  | 		// -> former key == node.prefix | ||||||
|  | 		if len(key) == 0 { | ||||||
|  | 			goto InsertItem | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// Check children for matching prefix. | ||||||
|  | 		child = node.children.next(key[0]) | ||||||
|  | 		if child == nil { | ||||||
|  | 			goto AppendChild | ||||||
|  | 		} | ||||||
|  | 		node = child | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | SplitPrefix: | ||||||
|  | 	// Split the prefix if necessary. | ||||||
|  | 	child = new(Trie) | ||||||
|  | 	*child = *node | ||||||
|  | 	*node = *NewTrie() | ||||||
|  | 	node.prefix = child.prefix[:common] | ||||||
|  | 	child.prefix = child.prefix[common:] | ||||||
|  | 	child = child.compact() | ||||||
|  | 	node.children = node.children.add(child) | ||||||
|  | 
 | ||||||
|  | AppendChild: | ||||||
|  | 	// Keep appending children until whole prefix is inserted. | ||||||
|  | 	// This loop starts with empty node.prefix that needs to be filled. | ||||||
|  | 	for len(key) != 0 { | ||||||
|  | 		child := NewTrie() | ||||||
|  | 		if len(key) <= MaxPrefixPerNode { | ||||||
|  | 			child.prefix = key | ||||||
|  | 			node.children = node.children.add(child) | ||||||
|  | 			node = child | ||||||
|  | 			goto InsertItem | ||||||
|  | 		} else { | ||||||
|  | 			child.prefix = key[:MaxPrefixPerNode] | ||||||
|  | 			key = key[MaxPrefixPerNode:] | ||||||
|  | 			node.children = node.children.add(child) | ||||||
|  | 			node = child | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | InsertItem: | ||||||
|  | 	// Try to insert the item if possible. | ||||||
|  | 	if replace || node.item == nil { | ||||||
|  | 		node.item = item | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (trie *Trie) compact() *Trie { | ||||||
|  | 	// Only a node with a single child can be compacted. | ||||||
|  | 	if trie.children.length() != 1 { | ||||||
|  | 		return trie | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	child := trie.children.head() | ||||||
|  | 
 | ||||||
|  | 	// If any item is set, we cannot compact since we want to retain | ||||||
|  | 	// the ability to do searching by key. This makes compaction less usable, | ||||||
|  | 	// but that simply cannot be avoided. | ||||||
|  | 	if trie.item != nil || child.item != nil { | ||||||
|  | 		return trie | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Make sure the combined prefixes fit into a single node. | ||||||
|  | 	if len(trie.prefix)+len(child.prefix) > MaxPrefixPerNode { | ||||||
|  | 		return trie | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Concatenate the prefixes, move the items. | ||||||
|  | 	child.prefix = append(trie.prefix, child.prefix...) | ||||||
|  | 	if trie.item != nil { | ||||||
|  | 		child.item = trie.item | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return child | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (trie *Trie) findSubtree(prefix Prefix) (parent *Trie, root *Trie, found bool, leftover Prefix) { | ||||||
|  | 	// Find the subtree matching prefix. | ||||||
|  | 	root = trie | ||||||
|  | 	for { | ||||||
|  | 		// Compute what part of prefix matches. | ||||||
|  | 		common := root.longestCommonPrefixLength(prefix) | ||||||
|  | 		prefix = prefix[common:] | ||||||
|  | 
 | ||||||
|  | 		// We used up the whole prefix, subtree found. | ||||||
|  | 		if len(prefix) == 0 { | ||||||
|  | 			found = true | ||||||
|  | 			leftover = root.prefix[common:] | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// Partial match means that there is no subtree matching prefix. | ||||||
|  | 		if common < len(root.prefix) { | ||||||
|  | 			leftover = root.prefix[common:] | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// There is some prefix left, move to the children. | ||||||
|  | 		child := root.children.next(prefix[0]) | ||||||
|  | 		if child == nil { | ||||||
|  | 			// There is nowhere to continue, there is no subtree matching prefix. | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		parent = root | ||||||
|  | 		root = child | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (trie *Trie) walk(actualRootPrefix Prefix, visitor VisitorFunc) error { | ||||||
|  | 	var prefix Prefix | ||||||
|  | 	// Allocate a bit more space for prefix at the beginning. | ||||||
|  | 	if actualRootPrefix == nil { | ||||||
|  | 		prefix = make(Prefix, 32+len(trie.prefix)) | ||||||
|  | 		copy(prefix, trie.prefix) | ||||||
|  | 		prefix = prefix[:len(trie.prefix)] | ||||||
|  | 	} else { | ||||||
|  | 		prefix = make(Prefix, 32+len(actualRootPrefix)) | ||||||
|  | 		copy(prefix, actualRootPrefix) | ||||||
|  | 		prefix = prefix[:len(actualRootPrefix)] | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Visit the root first. Not that this works for empty trie as well since | ||||||
|  | 	// in that case item == nil && len(children) == 0. | ||||||
|  | 	if trie.item != nil { | ||||||
|  | 		if err := visitor(prefix, trie.item); err != nil { | ||||||
|  | 			if err == SkipSubtree { | ||||||
|  | 				return nil | ||||||
|  | 			} | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Then continue to the children. | ||||||
|  | 	return trie.children.walk(&prefix, visitor) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (trie *Trie) longestCommonPrefixLength(prefix Prefix) (i int) { | ||||||
|  | 	for ; i < len(prefix) && i < len(trie.prefix) && prefix[i] == trie.prefix[i]; i++ { | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Errors ---------------------------------------------------------------------- | ||||||
|  | 
 | ||||||
|  | var ( | ||||||
|  | 	SkipSubtree  = errors.New("Skip this subtree") | ||||||
|  | 	ErrNilPrefix = errors.New("Nil prefix passed into a method call") | ||||||
|  | ) | ||||||
							
								
								
									
										161
									
								
								Godeps/_workspace/src/github.com/tchap/go-patricia/patricia/patricia_dense_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								Godeps/_workspace/src/github.com/tchap/go-patricia/patricia/patricia_dense_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,161 @@ | |||||||
|  | // Copyright (c) 2014 The go-patricia AUTHORS | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by The MIT License | ||||||
|  | // that can be found in the LICENSE file. | ||||||
|  | 
 | ||||||
|  | package patricia | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Tests ----------------------------------------------------------------------- | ||||||
|  | 
 | ||||||
|  | func TestTrie_InsertDense(t *testing.T) { | ||||||
|  | 	trie := NewTrie() | ||||||
|  | 
 | ||||||
|  | 	data := []testData{ | ||||||
|  | 		{"aba", 0, success}, | ||||||
|  | 		{"abb", 1, success}, | ||||||
|  | 		{"abc", 2, success}, | ||||||
|  | 		{"abd", 3, success}, | ||||||
|  | 		{"abe", 4, success}, | ||||||
|  | 		{"abf", 5, success}, | ||||||
|  | 		{"abg", 6, success}, | ||||||
|  | 		{"abh", 7, success}, | ||||||
|  | 		{"abi", 8, success}, | ||||||
|  | 		{"abj", 9, success}, | ||||||
|  | 		{"abk", 0, success}, | ||||||
|  | 		{"abl", 1, success}, | ||||||
|  | 		{"abm", 2, success}, | ||||||
|  | 		{"abn", 3, success}, | ||||||
|  | 		{"abo", 4, success}, | ||||||
|  | 		{"abp", 5, success}, | ||||||
|  | 		{"abq", 6, success}, | ||||||
|  | 		{"abr", 7, success}, | ||||||
|  | 		{"abs", 8, success}, | ||||||
|  | 		{"abt", 9, success}, | ||||||
|  | 		{"abu", 0, success}, | ||||||
|  | 		{"abv", 1, success}, | ||||||
|  | 		{"abw", 2, success}, | ||||||
|  | 		{"abx", 3, success}, | ||||||
|  | 		{"aby", 4, success}, | ||||||
|  | 		{"abz", 5, success}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, v := range data { | ||||||
|  | 		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) | ||||||
|  | 		if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal { | ||||||
|  | 			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestTrie_InsertDensePreceeding(t *testing.T) { | ||||||
|  | 	trie := NewTrie() | ||||||
|  | 	start := byte(70) | ||||||
|  | 	// create a dense node | ||||||
|  | 	for i := byte(0); i <= MaxChildrenPerSparseNode; i++ { | ||||||
|  | 		if !trie.Insert(Prefix([]byte{start + i}), true) { | ||||||
|  | 			t.Errorf("insert failed, prefix=%v", start+i) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	// insert some preceeding keys | ||||||
|  | 	for i := byte(1); i < start; i *= i + 1 { | ||||||
|  | 		if !trie.Insert(Prefix([]byte{start - i}), true) { | ||||||
|  | 			t.Errorf("insert failed, prefix=%v", start-i) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestTrie_InsertDenseDuplicatePrefixes(t *testing.T) { | ||||||
|  | 	trie := NewTrie() | ||||||
|  | 
 | ||||||
|  | 	data := []testData{ | ||||||
|  | 		{"aba", 0, success}, | ||||||
|  | 		{"abb", 1, success}, | ||||||
|  | 		{"abc", 2, success}, | ||||||
|  | 		{"abd", 3, success}, | ||||||
|  | 		{"abe", 4, success}, | ||||||
|  | 		{"abf", 5, success}, | ||||||
|  | 		{"abg", 6, success}, | ||||||
|  | 		{"abh", 7, success}, | ||||||
|  | 		{"abi", 8, success}, | ||||||
|  | 		{"abj", 9, success}, | ||||||
|  | 		{"abk", 0, success}, | ||||||
|  | 		{"abl", 1, success}, | ||||||
|  | 		{"abm", 2, success}, | ||||||
|  | 		{"abn", 3, success}, | ||||||
|  | 		{"abo", 4, success}, | ||||||
|  | 		{"abp", 5, success}, | ||||||
|  | 		{"abq", 6, success}, | ||||||
|  | 		{"abr", 7, success}, | ||||||
|  | 		{"abs", 8, success}, | ||||||
|  | 		{"abt", 9, success}, | ||||||
|  | 		{"abu", 0, success}, | ||||||
|  | 		{"abv", 1, success}, | ||||||
|  | 		{"abw", 2, success}, | ||||||
|  | 		{"abx", 3, success}, | ||||||
|  | 		{"aby", 4, success}, | ||||||
|  | 		{"abz", 5, success}, | ||||||
|  | 		{"aba", 0, failure}, | ||||||
|  | 		{"abb", 1, failure}, | ||||||
|  | 		{"abc", 2, failure}, | ||||||
|  | 		{"abd", 3, failure}, | ||||||
|  | 		{"abe", 4, failure}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, v := range data { | ||||||
|  | 		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) | ||||||
|  | 		if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal { | ||||||
|  | 			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestTrie_DeleteDense(t *testing.T) { | ||||||
|  | 	trie := NewTrie() | ||||||
|  | 
 | ||||||
|  | 	data := []testData{ | ||||||
|  | 		{"aba", 0, success}, | ||||||
|  | 		{"abb", 1, success}, | ||||||
|  | 		{"abc", 2, success}, | ||||||
|  | 		{"abd", 3, success}, | ||||||
|  | 		{"abe", 4, success}, | ||||||
|  | 		{"abf", 5, success}, | ||||||
|  | 		{"abg", 6, success}, | ||||||
|  | 		{"abh", 7, success}, | ||||||
|  | 		{"abi", 8, success}, | ||||||
|  | 		{"abj", 9, success}, | ||||||
|  | 		{"abk", 0, success}, | ||||||
|  | 		{"abl", 1, success}, | ||||||
|  | 		{"abm", 2, success}, | ||||||
|  | 		{"abn", 3, success}, | ||||||
|  | 		{"abo", 4, success}, | ||||||
|  | 		{"abp", 5, success}, | ||||||
|  | 		{"abq", 6, success}, | ||||||
|  | 		{"abr", 7, success}, | ||||||
|  | 		{"abs", 8, success}, | ||||||
|  | 		{"abt", 9, success}, | ||||||
|  | 		{"abu", 0, success}, | ||||||
|  | 		{"abv", 1, success}, | ||||||
|  | 		{"abw", 2, success}, | ||||||
|  | 		{"abx", 3, success}, | ||||||
|  | 		{"aby", 4, success}, | ||||||
|  | 		{"abz", 5, success}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, v := range data { | ||||||
|  | 		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) | ||||||
|  | 		if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal { | ||||||
|  | 			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, v := range data { | ||||||
|  | 		t.Logf("DELETE word=%v, success=%v", v.key, v.retVal) | ||||||
|  | 		if ok := trie.Delete([]byte(v.key)); ok != v.retVal { | ||||||
|  | 			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										659
									
								
								Godeps/_workspace/src/github.com/tchap/go-patricia/patricia/patricia_sparse_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										659
									
								
								Godeps/_workspace/src/github.com/tchap/go-patricia/patricia/patricia_sparse_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,659 @@ | |||||||
|  | // Copyright (c) 2014 The go-patricia AUTHORS | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by The MIT License | ||||||
|  | // that can be found in the LICENSE file. | ||||||
|  | 
 | ||||||
|  | package patricia | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"strings" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	success = true | ||||||
|  | 	failure = false | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type testData struct { | ||||||
|  | 	key    string | ||||||
|  | 	value  interface{} | ||||||
|  | 	retVal bool | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Tests ----------------------------------------------------------------------- | ||||||
|  | 
 | ||||||
|  | func TestTrie_InsertDifferentPrefixes(t *testing.T) { | ||||||
|  | 	trie := NewTrie() | ||||||
|  | 
 | ||||||
|  | 	data := []testData{ | ||||||
|  | 		{"Pepaneeeeeeeeeeeeee", "Pepan Zdepan", success}, | ||||||
|  | 		{"Honzooooooooooooooo", "Honza Novak", success}, | ||||||
|  | 		{"Jenikuuuuuuuuuuuuuu", "Jenik Poustevnicek", success}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, v := range data { | ||||||
|  | 		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) | ||||||
|  | 		if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal { | ||||||
|  | 			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestTrie_InsertDuplicatePrefixes(t *testing.T) { | ||||||
|  | 	trie := NewTrie() | ||||||
|  | 
 | ||||||
|  | 	data := []testData{ | ||||||
|  | 		{"Pepan", "Pepan Zdepan", success}, | ||||||
|  | 		{"Pepan", "Pepan Zdepan", failure}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, v := range data { | ||||||
|  | 		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) | ||||||
|  | 		if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal { | ||||||
|  | 			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestTrie_InsertVariousPrefixes(t *testing.T) { | ||||||
|  | 	trie := NewTrie() | ||||||
|  | 
 | ||||||
|  | 	data := []testData{ | ||||||
|  | 		{"Pepan", "Pepan Zdepan", success}, | ||||||
|  | 		{"Pepin", "Pepin Omacka", success}, | ||||||
|  | 		{"Honza", "Honza Novak", success}, | ||||||
|  | 		{"Jenik", "Jenik Poustevnicek", success}, | ||||||
|  | 		{"Pepan", "Pepan Dupan", failure}, | ||||||
|  | 		{"Karel", "Karel Pekar", success}, | ||||||
|  | 		{"Jenik", "Jenik Poustevnicek", failure}, | ||||||
|  | 		{"Pepanek", "Pepanek Zemlicka", success}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, v := range data { | ||||||
|  | 		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) | ||||||
|  | 		if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal { | ||||||
|  | 			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestTrie_InsertAndMatchPrefix(t *testing.T) { | ||||||
|  | 	trie := NewTrie() | ||||||
|  | 	t.Log("INSERT prefix=by week") | ||||||
|  | 	trie.Insert(Prefix("by week"), 2) | ||||||
|  | 	t.Log("INSERT prefix=by") | ||||||
|  | 	trie.Insert(Prefix("by"), 1) | ||||||
|  | 
 | ||||||
|  | 	if !trie.Match(Prefix("by")) { | ||||||
|  | 		t.Error("MATCH prefix=by, expected=true, got=false") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestTrie_SetGet(t *testing.T) { | ||||||
|  | 	trie := NewTrie() | ||||||
|  | 
 | ||||||
|  | 	data := []testData{ | ||||||
|  | 		{"Pepan", "Pepan Zdepan", success}, | ||||||
|  | 		{"Pepin", "Pepin Omacka", success}, | ||||||
|  | 		{"Honza", "Honza Novak", success}, | ||||||
|  | 		{"Jenik", "Jenik Poustevnicek", success}, | ||||||
|  | 		{"Pepan", "Pepan Dupan", failure}, | ||||||
|  | 		{"Karel", "Karel Pekar", success}, | ||||||
|  | 		{"Jenik", "Jenik Poustevnicek", failure}, | ||||||
|  | 		{"Pepanek", "Pepanek Zemlicka", success}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, v := range data { | ||||||
|  | 		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) | ||||||
|  | 		if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal { | ||||||
|  | 			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, v := range data { | ||||||
|  | 		t.Logf("SET %q to 10", v.key) | ||||||
|  | 		trie.Set(Prefix(v.key), 10) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, v := range data { | ||||||
|  | 		value := trie.Get(Prefix(v.key)) | ||||||
|  | 		t.Logf("GET %q => %v", v.key, value) | ||||||
|  | 		if value.(int) != 10 { | ||||||
|  | 			t.Errorf("Unexpected return value, != 10", value) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if value := trie.Get(Prefix("random crap")); value != nil { | ||||||
|  | 		t.Errorf("Unexpected return value, %v != <nil>", value) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestTrie_Match(t *testing.T) { | ||||||
|  | 	trie := NewTrie() | ||||||
|  | 
 | ||||||
|  | 	data := []testData{ | ||||||
|  | 		{"Pepan", "Pepan Zdepan", success}, | ||||||
|  | 		{"Pepin", "Pepin Omacka", success}, | ||||||
|  | 		{"Honza", "Honza Novak", success}, | ||||||
|  | 		{"Jenik", "Jenik Poustevnicek", success}, | ||||||
|  | 		{"Pepan", "Pepan Dupan", failure}, | ||||||
|  | 		{"Karel", "Karel Pekar", success}, | ||||||
|  | 		{"Jenik", "Jenik Poustevnicek", failure}, | ||||||
|  | 		{"Pepanek", "Pepanek Zemlicka", success}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, v := range data { | ||||||
|  | 		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) | ||||||
|  | 		if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal { | ||||||
|  | 			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, v := range data { | ||||||
|  | 		matched := trie.Match(Prefix(v.key)) | ||||||
|  | 		t.Logf("MATCH %q => %v", v.key, matched) | ||||||
|  | 		if !matched { | ||||||
|  | 			t.Errorf("Inserted key %q was not matched", v.key) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if trie.Match(Prefix("random crap")) { | ||||||
|  | 		t.Errorf("Key that was not inserted matched: %q", "random crap") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestTrie_MatchFalsePositive(t *testing.T) { | ||||||
|  | 	trie := NewTrie() | ||||||
|  | 
 | ||||||
|  | 	if ok := trie.Insert(Prefix("A"), 1); !ok { | ||||||
|  | 		t.Fatal("INSERT prefix=A, item=1 not ok") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	resultMatchSubtree := trie.MatchSubtree(Prefix("A extra")) | ||||||
|  | 	resultMatch := trie.Match(Prefix("A extra")) | ||||||
|  | 
 | ||||||
|  | 	if resultMatchSubtree != false { | ||||||
|  | 		t.Error("MatchSubtree returned false positive") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if resultMatch != false { | ||||||
|  | 		t.Error("Match returned false positive") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestTrie_MatchSubtree(t *testing.T) { | ||||||
|  | 	trie := NewTrie() | ||||||
|  | 
 | ||||||
|  | 	data := []testData{ | ||||||
|  | 		{"Pepan", "Pepan Zdepan", success}, | ||||||
|  | 		{"Pepin", "Pepin Omacka", success}, | ||||||
|  | 		{"Honza", "Honza Novak", success}, | ||||||
|  | 		{"Jenik", "Jenik Poustevnicek", success}, | ||||||
|  | 		{"Pepan", "Pepan Dupan", failure}, | ||||||
|  | 		{"Karel", "Karel Pekar", success}, | ||||||
|  | 		{"Jenik", "Jenik Poustevnicek", failure}, | ||||||
|  | 		{"Pepanek", "Pepanek Zemlicka", success}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, v := range data { | ||||||
|  | 		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) | ||||||
|  | 		if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal { | ||||||
|  | 			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, v := range data { | ||||||
|  | 		key := Prefix(v.key[:3]) | ||||||
|  | 		matched := trie.MatchSubtree(key) | ||||||
|  | 		t.Logf("MATCH_SUBTREE %q => %v", key, matched) | ||||||
|  | 		if !matched { | ||||||
|  | 			t.Errorf("Subtree %q was not matched", v.key) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestTrie_Visit(t *testing.T) { | ||||||
|  | 	trie := NewTrie() | ||||||
|  | 
 | ||||||
|  | 	data := []testData{ | ||||||
|  | 		{"Pepa", 0, success}, | ||||||
|  | 		{"Pepa Zdepa", 1, success}, | ||||||
|  | 		{"Pepa Kuchar", 2, success}, | ||||||
|  | 		{"Honza", 3, success}, | ||||||
|  | 		{"Jenik", 4, success}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, v := range data { | ||||||
|  | 		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) | ||||||
|  | 		if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal { | ||||||
|  | 			t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err := trie.Visit(func(prefix Prefix, item Item) error { | ||||||
|  | 		name := data[item.(int)].key | ||||||
|  | 		t.Logf("VISITING prefix=%q, item=%v", prefix, item) | ||||||
|  | 		if !strings.HasPrefix(string(prefix), name) { | ||||||
|  | 			t.Errorf("Unexpected prefix encountered, %q not a prefix of %q", prefix, name) | ||||||
|  | 		} | ||||||
|  | 		return nil | ||||||
|  | 	}); err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestTrie_VisitSkipSubtree(t *testing.T) { | ||||||
|  | 	trie := NewTrie() | ||||||
|  | 
 | ||||||
|  | 	data := []testData{ | ||||||
|  | 		{"Pepa", 0, success}, | ||||||
|  | 		{"Pepa Zdepa", 1, success}, | ||||||
|  | 		{"Pepa Kuchar", 2, success}, | ||||||
|  | 		{"Honza", 3, success}, | ||||||
|  | 		{"Jenik", 4, success}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, v := range data { | ||||||
|  | 		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) | ||||||
|  | 		if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal { | ||||||
|  | 			t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err := trie.Visit(func(prefix Prefix, item Item) error { | ||||||
|  | 		t.Logf("VISITING prefix=%q, item=%v", prefix, item) | ||||||
|  | 		if item.(int) == 0 { | ||||||
|  | 			t.Logf("SKIP %q", prefix) | ||||||
|  | 			return SkipSubtree | ||||||
|  | 		} | ||||||
|  | 		if strings.HasPrefix(string(prefix), "Pepa") { | ||||||
|  | 			t.Errorf("Unexpected prefix encountered, %q", prefix) | ||||||
|  | 		} | ||||||
|  | 		return nil | ||||||
|  | 	}); err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestTrie_VisitReturnError(t *testing.T) { | ||||||
|  | 	trie := NewTrie() | ||||||
|  | 
 | ||||||
|  | 	data := []testData{ | ||||||
|  | 		{"Pepa", 0, success}, | ||||||
|  | 		{"Pepa Zdepa", 1, success}, | ||||||
|  | 		{"Pepa Kuchar", 2, success}, | ||||||
|  | 		{"Honza", 3, success}, | ||||||
|  | 		{"Jenik", 4, success}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, v := range data { | ||||||
|  | 		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) | ||||||
|  | 		if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal { | ||||||
|  | 			t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	someErr := errors.New("Something exploded") | ||||||
|  | 	if err := trie.Visit(func(prefix Prefix, item Item) error { | ||||||
|  | 		t.Logf("VISITING prefix=%q, item=%v", prefix, item) | ||||||
|  | 		if item.(int) == 0 { | ||||||
|  | 			return someErr | ||||||
|  | 		} | ||||||
|  | 		if item.(int) != 0 { | ||||||
|  | 			t.Errorf("Unexpected prefix encountered, %q", prefix) | ||||||
|  | 		} | ||||||
|  | 		return nil | ||||||
|  | 	}); err != nil && err != someErr { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestTrie_VisitSubtree(t *testing.T) { | ||||||
|  | 	trie := NewTrie() | ||||||
|  | 
 | ||||||
|  | 	data := []testData{ | ||||||
|  | 		{"Pepa", 0, success}, | ||||||
|  | 		{"Pepa Zdepa", 1, success}, | ||||||
|  | 		{"Pepa Kuchar", 2, success}, | ||||||
|  | 		{"Honza", 3, success}, | ||||||
|  | 		{"Jenik", 4, success}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, v := range data { | ||||||
|  | 		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) | ||||||
|  | 		if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal { | ||||||
|  | 			t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var counter int | ||||||
|  | 	subtreePrefix := []byte("Pep") | ||||||
|  | 	t.Log("VISIT Pep") | ||||||
|  | 	if err := trie.VisitSubtree(subtreePrefix, func(prefix Prefix, item Item) error { | ||||||
|  | 		t.Logf("VISITING prefix=%q, item=%v", prefix, item) | ||||||
|  | 		if !bytes.HasPrefix(prefix, subtreePrefix) { | ||||||
|  | 			t.Errorf("Unexpected prefix encountered, %q does not extend %q", | ||||||
|  | 				prefix, subtreePrefix) | ||||||
|  | 		} | ||||||
|  | 		if len(prefix) > len(data[item.(int)].key) { | ||||||
|  | 			t.Fatalf("Something is rather fishy here, prefix=%q", prefix) | ||||||
|  | 		} | ||||||
|  | 		counter++ | ||||||
|  | 		return nil | ||||||
|  | 	}); err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if counter != 3 { | ||||||
|  | 		t.Error("Unexpected number of nodes visited") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestTrie_VisitPrefixes(t *testing.T) { | ||||||
|  | 	trie := NewTrie() | ||||||
|  | 
 | ||||||
|  | 	data := []testData{ | ||||||
|  | 		{"P", 0, success}, | ||||||
|  | 		{"Pe", 1, success}, | ||||||
|  | 		{"Pep", 2, success}, | ||||||
|  | 		{"Pepa", 3, success}, | ||||||
|  | 		{"Pepa Zdepa", 4, success}, | ||||||
|  | 		{"Pepa Kuchar", 5, success}, | ||||||
|  | 		{"Honza", 6, success}, | ||||||
|  | 		{"Jenik", 7, success}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, v := range data { | ||||||
|  | 		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) | ||||||
|  | 		if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal { | ||||||
|  | 			t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var counter int | ||||||
|  | 	word := []byte("Pepa") | ||||||
|  | 	if err := trie.VisitPrefixes(word, func(prefix Prefix, item Item) error { | ||||||
|  | 		t.Logf("VISITING prefix=%q, item=%v", prefix, item) | ||||||
|  | 		if !bytes.HasPrefix(word, prefix) { | ||||||
|  | 			t.Errorf("Unexpected prefix encountered, %q is not a prefix of %q", | ||||||
|  | 				prefix, word) | ||||||
|  | 		} | ||||||
|  | 		counter++ | ||||||
|  | 		return nil | ||||||
|  | 	}); err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if counter != 4 { | ||||||
|  | 		t.Error("Unexpected number of nodes visited") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestParticiaTrie_Delete(t *testing.T) { | ||||||
|  | 	trie := NewTrie() | ||||||
|  | 
 | ||||||
|  | 	data := []testData{ | ||||||
|  | 		{"Pepan", "Pepan Zdepan", success}, | ||||||
|  | 		{"Honza", "Honza Novak", success}, | ||||||
|  | 		{"Jenik", "Jenik Poustevnicek", success}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, v := range data { | ||||||
|  | 		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) | ||||||
|  | 		if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal { | ||||||
|  | 			t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, v := range data { | ||||||
|  | 		t.Logf("DELETE word=%v, success=%v", v.key, v.retVal) | ||||||
|  | 		if ok := trie.Delete([]byte(v.key)); ok != v.retVal { | ||||||
|  | 			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestParticiaTrie_DeleteNonExistent(t *testing.T) { | ||||||
|  | 	trie := NewTrie() | ||||||
|  | 
 | ||||||
|  | 	insertData := []testData{ | ||||||
|  | 		{"Pepan", "Pepan Zdepan", success}, | ||||||
|  | 		{"Honza", "Honza Novak", success}, | ||||||
|  | 		{"Jenik", "Jenik Poustevnicek", success}, | ||||||
|  | 	} | ||||||
|  | 	deleteData := []testData{ | ||||||
|  | 		{"Pepan", "Pepan Zdepan", success}, | ||||||
|  | 		{"Honza", "Honza Novak", success}, | ||||||
|  | 		{"Pepan", "Pepan Zdepan", failure}, | ||||||
|  | 		{"Jenik", "Jenik Poustevnicek", success}, | ||||||
|  | 		{"Honza", "Honza Novak", failure}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, v := range insertData { | ||||||
|  | 		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) | ||||||
|  | 		if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal { | ||||||
|  | 			t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, v := range deleteData { | ||||||
|  | 		t.Logf("DELETE word=%v, success=%v", v.key, v.retVal) | ||||||
|  | 		if ok := trie.Delete([]byte(v.key)); ok != v.retVal { | ||||||
|  | 			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestParticiaTrie_DeleteSubtree(t *testing.T) { | ||||||
|  | 	trie := NewTrie() | ||||||
|  | 
 | ||||||
|  | 	insertData := []testData{ | ||||||
|  | 		{"P", 0, success}, | ||||||
|  | 		{"Pe", 1, success}, | ||||||
|  | 		{"Pep", 2, success}, | ||||||
|  | 		{"Pepa", 3, success}, | ||||||
|  | 		{"Pepa Zdepa", 4, success}, | ||||||
|  | 		{"Pepa Kuchar", 5, success}, | ||||||
|  | 		{"Honza", 6, success}, | ||||||
|  | 		{"Jenik", 7, success}, | ||||||
|  | 	} | ||||||
|  | 	deleteData := []testData{ | ||||||
|  | 		{"Pe", -1, success}, | ||||||
|  | 		{"Pe", -1, failure}, | ||||||
|  | 		{"Honzik", -1, failure}, | ||||||
|  | 		{"Honza", -1, success}, | ||||||
|  | 		{"Honza", -1, failure}, | ||||||
|  | 		{"Pep", -1, failure}, | ||||||
|  | 		{"P", -1, success}, | ||||||
|  | 		{"Nobody", -1, failure}, | ||||||
|  | 		{"", -1, success}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, v := range insertData { | ||||||
|  | 		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) | ||||||
|  | 		if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal { | ||||||
|  | 			t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, v := range deleteData { | ||||||
|  | 		t.Logf("DELETE_SUBTREE prefix=%v, success=%v", v.key, v.retVal) | ||||||
|  | 		if ok := trie.DeleteSubtree([]byte(v.key)); ok != v.retVal { | ||||||
|  | 			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* | ||||||
|  | func TestTrie_Dump(t *testing.T) { | ||||||
|  | 	trie := NewTrie() | ||||||
|  | 
 | ||||||
|  | 	data := []testData{ | ||||||
|  | 		{"Honda", nil, success}, | ||||||
|  | 		{"Honza", nil, success}, | ||||||
|  | 		{"Jenik", nil, success}, | ||||||
|  | 		{"Pepan", nil, success}, | ||||||
|  | 		{"Pepin", nil, success}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for i, v := range data { | ||||||
|  | 		if _, ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal { | ||||||
|  | 			t.Logf("INSERT %v %v", v.key, v.value) | ||||||
|  | 			t.Fatalf("Unexpected return value, expected=%v, got=%v", i, ok) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	dump := ` | ||||||
|  | +--+--+ Hon +--+--+ da | ||||||
|  |    |           | | ||||||
|  |    |           +--+ za | ||||||
|  |    | | ||||||
|  |    +--+ Jenik | ||||||
|  |    | | ||||||
|  |    +--+ Pep +--+--+ an | ||||||
|  |                | | ||||||
|  |                +--+ in | ||||||
|  | ` | ||||||
|  | 
 | ||||||
|  | 	var buf bytes.Buffer | ||||||
|  | 	trie.Dump(buf) | ||||||
|  | 
 | ||||||
|  | 	if !bytes.Equal(buf.Bytes(), dump) { | ||||||
|  | 		t.Logf("DUMP") | ||||||
|  | 		t.Fatalf("Unexpected dump generated, expected\n\n%v\ngot\n\n%v", dump, buf.String()) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | func TestTrie_compact(t *testing.T) { | ||||||
|  | 	trie := NewTrie() | ||||||
|  | 
 | ||||||
|  | 	trie.Insert(Prefix("a"), 0) | ||||||
|  | 	trie.Insert(Prefix("ab"), 0) | ||||||
|  | 	trie.Insert(Prefix("abc"), 0) | ||||||
|  | 	trie.Insert(Prefix("abcd"), 0) | ||||||
|  | 	trie.Insert(Prefix("abcde"), 0) | ||||||
|  | 	trie.Insert(Prefix("abcdef"), 0) | ||||||
|  | 	trie.Insert(Prefix("abcdefg"), 0) | ||||||
|  | 	trie.Insert(Prefix("abcdefgi"), 0) | ||||||
|  | 	trie.Insert(Prefix("abcdefgij"), 0) | ||||||
|  | 	trie.Insert(Prefix("abcdefgijk"), 0) | ||||||
|  | 
 | ||||||
|  | 	trie.Delete(Prefix("abcdef")) | ||||||
|  | 	trie.Delete(Prefix("abcde")) | ||||||
|  | 	trie.Delete(Prefix("abcdefg")) | ||||||
|  | 
 | ||||||
|  | 	trie.Delete(Prefix("a")) | ||||||
|  | 	trie.Delete(Prefix("abc")) | ||||||
|  | 	trie.Delete(Prefix("ab")) | ||||||
|  | 
 | ||||||
|  | 	trie.Visit(func(prefix Prefix, item Item) error { | ||||||
|  | 		// 97 ~~ 'a', | ||||||
|  | 		for ch := byte(97); ch <= 107; ch++ { | ||||||
|  | 			if c := bytes.Count(prefix, []byte{ch}); c > 1 { | ||||||
|  | 				t.Errorf("%q appeared in %q %v times", ch, prefix, c) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		return nil | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestTrie_longestCommonPrefixLenght(t *testing.T) { | ||||||
|  | 	trie := NewTrie() | ||||||
|  | 	trie.prefix = []byte("1234567890") | ||||||
|  | 
 | ||||||
|  | 	switch { | ||||||
|  | 	case trie.longestCommonPrefixLength([]byte("")) != 0: | ||||||
|  | 		t.Fail() | ||||||
|  | 	case trie.longestCommonPrefixLength([]byte("12345")) != 5: | ||||||
|  | 		t.Fail() | ||||||
|  | 	case trie.longestCommonPrefixLength([]byte("123789")) != 3: | ||||||
|  | 		t.Fail() | ||||||
|  | 	case trie.longestCommonPrefixLength([]byte("12345678901")) != 10: | ||||||
|  | 		t.Fail() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Examples -------------------------------------------------------------------- | ||||||
|  | 
 | ||||||
|  | func ExampleTrie() { | ||||||
|  | 	// Create a new tree. | ||||||
|  | 	trie := NewTrie() | ||||||
|  | 
 | ||||||
|  | 	// Insert some items. | ||||||
|  | 	trie.Insert(Prefix("Pepa Novak"), 1) | ||||||
|  | 	trie.Insert(Prefix("Pepa Sindelar"), 2) | ||||||
|  | 	trie.Insert(Prefix("Karel Macha"), 3) | ||||||
|  | 	trie.Insert(Prefix("Karel Hynek Macha"), 4) | ||||||
|  | 
 | ||||||
|  | 	// Just check if some things are present in the tree. | ||||||
|  | 	key := Prefix("Pepa Novak") | ||||||
|  | 	fmt.Printf("%q present? %v\n", key, trie.Match(key)) | ||||||
|  | 	key = Prefix("Karel") | ||||||
|  | 	fmt.Printf("Anybody called %q here? %v\n", key, trie.MatchSubtree(key)) | ||||||
|  | 
 | ||||||
|  | 	// Walk the tree. | ||||||
|  | 	trie.Visit(printItem) | ||||||
|  | 	// "Pepa Novak": 1 | ||||||
|  | 	// "Pepa Sindelar": 2 | ||||||
|  | 	// "Karel Macha": 3 | ||||||
|  | 	// "Karel Hynek Macha": 4 | ||||||
|  | 
 | ||||||
|  | 	// Walk a subtree. | ||||||
|  | 	trie.VisitSubtree(Prefix("Pepa"), printItem) | ||||||
|  | 	// "Pepa Novak": 1 | ||||||
|  | 	// "Pepa Sindelar": 2 | ||||||
|  | 
 | ||||||
|  | 	// Modify an item, then fetch it from the tree. | ||||||
|  | 	trie.Set(Prefix("Karel Hynek Macha"), 10) | ||||||
|  | 	key = Prefix("Karel Hynek Macha") | ||||||
|  | 	fmt.Printf("%q: %v\n", key, trie.Get(key)) | ||||||
|  | 	// "Karel Hynek Macha": 10 | ||||||
|  | 
 | ||||||
|  | 	// Walk prefixes. | ||||||
|  | 	prefix := Prefix("Karel Hynek Macha je kouzelnik") | ||||||
|  | 	trie.VisitPrefixes(prefix, printItem) | ||||||
|  | 	// "Karel Hynek Macha": 10 | ||||||
|  | 
 | ||||||
|  | 	// Delete some items. | ||||||
|  | 	trie.Delete(Prefix("Pepa Novak")) | ||||||
|  | 	trie.Delete(Prefix("Karel Macha")) | ||||||
|  | 
 | ||||||
|  | 	// Walk again. | ||||||
|  | 	trie.Visit(printItem) | ||||||
|  | 	// "Pepa Sindelar": 2 | ||||||
|  | 	// "Karel Hynek Macha": 10 | ||||||
|  | 
 | ||||||
|  | 	// Delete a subtree. | ||||||
|  | 	trie.DeleteSubtree(Prefix("Pepa")) | ||||||
|  | 
 | ||||||
|  | 	// Print what is left. | ||||||
|  | 	trie.Visit(printItem) | ||||||
|  | 	// "Karel Hynek Macha": 10 | ||||||
|  | 
 | ||||||
|  | 	// Output: | ||||||
|  | 	// "Pepa Novak" present? true | ||||||
|  | 	// Anybody called "Karel" here? true | ||||||
|  | 	// "Pepa Novak": 1 | ||||||
|  | 	// "Pepa Sindelar": 2 | ||||||
|  | 	// "Karel Macha": 3 | ||||||
|  | 	// "Karel Hynek Macha": 4 | ||||||
|  | 	// "Pepa Novak": 1 | ||||||
|  | 	// "Pepa Sindelar": 2 | ||||||
|  | 	// "Karel Hynek Macha": 10 | ||||||
|  | 	// "Karel Hynek Macha": 10 | ||||||
|  | 	// "Pepa Sindelar": 2 | ||||||
|  | 	// "Karel Hynek Macha": 10 | ||||||
|  | 	// "Karel Hynek Macha": 10 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Helpers --------------------------------------------------------------------- | ||||||
|  | 
 | ||||||
|  | func printItem(prefix Prefix, item Item) error { | ||||||
|  | 	fmt.Printf("%q: %v\n", prefix, item) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										78
									
								
								Godeps/_workspace/src/github.com/tchap/go-patricia/patricia/patricia_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								Godeps/_workspace/src/github.com/tchap/go-patricia/patricia/patricia_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,78 @@ | |||||||
|  | // Copyright (c) 2014 The go-patricia AUTHORS | ||||||
|  | // | ||||||
|  | // Use of this source code is governed by The MIT License | ||||||
|  | // that can be found in the LICENSE file. | ||||||
|  | 
 | ||||||
|  | package patricia | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"crypto/rand" | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Tests ----------------------------------------------------------------------- | ||||||
|  | 
 | ||||||
|  | func TestTrie_GetNonexistentPrefix(t *testing.T) { | ||||||
|  | 	trie := NewTrie() | ||||||
|  | 
 | ||||||
|  | 	data := []testData{ | ||||||
|  | 		{"aba", 0, success}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, v := range data { | ||||||
|  | 		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) | ||||||
|  | 		if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal { | ||||||
|  | 			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	t.Logf("GET prefix=baa, expect item=nil") | ||||||
|  | 	if item := trie.Get(Prefix("baa")); item != nil { | ||||||
|  | 		t.Errorf("Unexpected return value, expected=<nil>, got=%v", item) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestTrie_RandomKitchenSink(t *testing.T) { | ||||||
|  | 	if testing.Short() { | ||||||
|  | 		t.Skip() | ||||||
|  | 	} | ||||||
|  | 	const count, size = 750000, 16 | ||||||
|  | 	b := make([]byte, count+size+1) | ||||||
|  | 	if _, err := rand.Read(b); err != nil { | ||||||
|  | 		t.Fatal("error generating random bytes", err) | ||||||
|  | 	} | ||||||
|  | 	m := make(map[string]string) | ||||||
|  | 	for i := 0; i < count; i++ { | ||||||
|  | 		m[string(b[i:i+size])] = string(b[i+1 : i+size+1]) | ||||||
|  | 	} | ||||||
|  | 	trie := NewTrie() | ||||||
|  | 	getAndDelete := func(k, v string) { | ||||||
|  | 		i := trie.Get(Prefix(k)) | ||||||
|  | 		if i == nil { | ||||||
|  | 			t.Fatalf("item not found, prefix=%v", []byte(k)) | ||||||
|  | 		} else if s, ok := i.(string); !ok { | ||||||
|  | 			t.Fatalf("unexpected item type, expecting=%v, got=%v", reflect.TypeOf(k), reflect.TypeOf(i)) | ||||||
|  | 		} else if s != v { | ||||||
|  | 			t.Fatalf("unexpected item, expecting=%v, got=%v", []byte(k), []byte(s)) | ||||||
|  | 		} else if !trie.Delete(Prefix(k)) { | ||||||
|  | 			t.Fatalf("delete failed, prefix=%v", []byte(k)) | ||||||
|  | 		} else if i = trie.Get(Prefix(k)); i != nil { | ||||||
|  | 			t.Fatalf("unexpected item, expecting=<nil>, got=%v", i) | ||||||
|  | 		} else if trie.Delete(Prefix(k)) { | ||||||
|  | 			t.Fatalf("extra delete succeeded, prefix=%v", []byte(k)) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	for k, v := range m { | ||||||
|  | 		if !trie.Insert(Prefix(k), v) { | ||||||
|  | 			t.Fatalf("insert failed, prefix=%v", []byte(k)) | ||||||
|  | 		} | ||||||
|  | 		if byte(k[size/2]) < 128 { | ||||||
|  | 			getAndDelete(k, v) | ||||||
|  | 			delete(m, k) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	for k, v := range m { | ||||||
|  | 		getAndDelete(k, v) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										18
									
								
								gateway.go
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								gateway.go
									
									
									
									
									
								
							| @ -4,6 +4,7 @@ import ( | |||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"github.com/gorilla/mux" | 	"github.com/gorilla/mux" | ||||||
|  | 	"github.com/tchap/go-patricia/patricia" | ||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| ) | ) | ||||||
| @ -134,11 +135,15 @@ func (bucket SynchronizedBucket) Get(context Context, path string) ([]byte, erro | |||||||
| 		path:        path, | 		path:        path, | ||||||
| 		callback:    callback, | 		callback:    callback, | ||||||
| 	} | 	} | ||||||
| 	switch response := <-callback; response.(type) { | 	response := <-callback | ||||||
| 	case []byte: | 
 | ||||||
| 		return response.([]byte), nil | 	switch response.(type) { | ||||||
| 	case error: | 	case error: | ||||||
| 		return nil, response.(error) | 		return nil, response.(error) | ||||||
|  | 	case nil: | ||||||
|  | 		return nil, errors.New("Object not found") | ||||||
|  | 	case interface{}: | ||||||
|  | 		return response.([]byte), nil | ||||||
| 	default: | 	default: | ||||||
| 		return nil, errors.New("Unexpected error, service failed") | 		return nil, errors.New("Unexpected error, service failed") | ||||||
| 	} | 	} | ||||||
| @ -167,16 +172,17 @@ func (bucket *SynchronizedBucket) closeChannel() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func InMemoryStorageDriver(bucket string, input chan ObjectRequest) { | func InMemoryStorageDriver(bucket string, input chan ObjectRequest) { | ||||||
| 	objects := make(map[string][]byte) | 	objects := patricia.NewTrie() | ||||||
| 	for request := range input { | 	for request := range input { | ||||||
|  | 		prefix := patricia.Prefix(request.path) | ||||||
| 		fmt.Println("objects:", objects) | 		fmt.Println("objects:", objects) | ||||||
| 		switch request.requestType { | 		switch request.requestType { | ||||||
| 		case "GET": | 		case "GET": | ||||||
| 			fmt.Println("GET: " + request.path) | 			fmt.Println("GET: " + request.path) | ||||||
| 			request.callback <- objects[request.path] | 			request.callback <- objects.Get(prefix) | ||||||
| 		case "PUT": | 		case "PUT": | ||||||
| 			fmt.Println("PUT: " + request.path) | 			fmt.Println("PUT: " + request.path) | ||||||
| 			objects[request.path] = request.object | 			objects.Insert(prefix, request.object) | ||||||
| 			request.callback <- nil | 			request.callback <- nil | ||||||
| 		default: | 		default: | ||||||
| 			request.callback <- errors.New("Unexpected message") | 			request.callback <- errors.New("Unexpected message") | ||||||
|  | |||||||
| @ -117,7 +117,8 @@ func (s *MySuite) TestInMemoryBucketOperations(c *C) { | |||||||
| 	// get missing value | 	// get missing value | ||||||
| 	nilResult, err := bucket.Get(context, "foo") | 	nilResult, err := bucket.Get(context, "foo") | ||||||
| 	c.Assert(nilResult, IsNil) | 	c.Assert(nilResult, IsNil) | ||||||
| 	c.Assert(err, IsNil) | 	c.Assert(err, Not(IsNil)) | ||||||
|  | 	c.Assert(err.Error(), Equals, "Object not found") | ||||||
| 
 | 
 | ||||||
| 	// add new value | 	// add new value | ||||||
| 	err = bucket.Put(context, "foo", []byte("bar")) | 	err = bucket.Put(context, "foo", []byte("bar")) | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user