Skip to content

Commit

Permalink
implemented generics version
Browse files Browse the repository at this point in the history
  • Loading branch information
vjc1234 committed Apr 2, 2024
1 parent 01776d2 commit 98c1314
Show file tree
Hide file tree
Showing 8 changed files with 368 additions and 354 deletions.
60 changes: 26 additions & 34 deletions node.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,17 @@ func (r *region) fragment(data []byte, ofSize uint32) []byte {
}

// Node represents a node
type Node struct {
type Node[T any] struct {
bset Bit64Set
Type uint8
ValueIndex uint32
prefixRegion region
nodesRegion region
Prefix []byte //24
Nodes //24
Nodes[T] //24
}

func (n *Node) Equals(d *Node) bool {
func (n *Node[T]) Equals(d *Node[T]) bool {

if !bytes.Equal(n.Prefix, d.Prefix) {
fmt.Printf("prefix diff: %s - %s", n.Prefix, d.Prefix)
Expand All @@ -63,7 +63,7 @@ func (n *Node) Equals(d *Node) bool {
return true

}
func (n *Node) Size() int {
func (n *Node[T]) Size() int {
ret := int(unsafe.Sizeof(*n))
ret += len(n.Prefix) //of byte size
for _, n := range n.Nodes {
Expand All @@ -72,13 +72,13 @@ func (n *Node) Size() int {
return ret
}

func (n *Node) Data() []byte {
func (n *Node[T]) Data() []byte {
data := make([]byte, n.Size())
n.write(data, 0, true)
return data
}

func (n *Node) write(data []byte, offset int, includeSelf bool) int {
func (n *Node[T]) write(data []byte, offset int, includeSelf bool) int {

initial := offset

Expand All @@ -93,7 +93,7 @@ func (n *Node) write(data []byte, offset int, includeSelf bool) int {
}

var prefixes = make([][]byte, len(n.Nodes))
var nodes = make([]Nodes, len(n.Nodes))
var nodes = make([]Nodes[T], len(n.Nodes))
for i := range n.Nodes {
node := &n.Nodes[i]
prefixes[i] = node.Prefix
Expand Down Expand Up @@ -127,63 +127,55 @@ func (n *Node) write(data []byte, offset int, includeSelf bool) int {
return offset
}

func (n *Node) Read(data *[]byte) {
func (n *Node[T]) Read(data *[]byte) {

if n.prefixRegion.size > 0 {

//fragment := n.prefixRegion.fragment(data, 1)
r := n.prefixRegion
prefix := unsafe.Slice((*byte)(unsafe.Pointer(&(*data)[r.offset : r.offset+r.size*1][0])), int(n.prefixRegion.size))
n.Prefix = prefix
//make([]byte, len(prefix))
//copy(n.Prefix, prefix)
}

if n.nodesRegion.size > 0 {

//fragment := n.nodesRegion.fragment(data, uint32(unsafe.Sizeof(*n)))
r := n.nodesRegion
nodes := unsafe.Slice((*Node)(unsafe.Pointer(&(*data)[r.offset : r.offset+r.size*uint32(unsafe.Sizeof(*n))][0])), int(n.nodesRegion.size))
nodes := unsafe.Slice((*Node[T])(unsafe.Pointer(&(*data)[r.offset : r.offset+r.size*uint32(unsafe.Sizeof(*n))][0])), int(n.nodesRegion.size))
for i := range nodes {
node := &nodes[i]
node.Read(data)
}
n.Nodes = nodes
//make([]Node, len(nodes))
//copy(n.Nodes, nodes)
}
}

func (n *Node) LoadNode(data []byte) {
func (n *Node[T]) LoadNode(data []byte) {
dest := unsafe.Slice((*byte)(unsafe.Pointer(n)), unsafe.Sizeof(*n))
copy(dest, data)
n.Read(&data)
}

type merger func(prev uint32) uint32

func (n *Node) isValueType() bool {
func (n *Node[T]) isValueType() bool {
return n.Type&NodeTypeValue == NodeTypeValue
}

func (n *Node) isEdgeType() bool {
func (n *Node[T]) isEdgeType() bool {
return n.Type&NodeTypeEdge == NodeTypeEdge
}

func (n *Node) makeEdge() {
func (n *Node[T]) makeEdge() {
n.Type = n.Type | NodeTypeEdge
}

func (n *Node) add(node *Node, merger merger) {
func (n *Node[T]) add(node *Node[T], merger merger) {
if len(n.Nodes) == 0 {
n.Nodes = make([]Node, 0)
n.Nodes = make([]Node[T], 0)
n.makeEdge()
}
n.bset = n.bset.Put(node.Prefix[0])
n.Nodes.add(node, merger)
}

func (n *Node) walk(parent []byte, handler func(key []byte, valueIndex uint32)) {
func (n *Node[T]) walk(parent []byte, handler func(key []byte, valueIndex uint32)) {
prefix := append(parent, n.Prefix...)
if n.isValueType() {
handler(prefix, n.ValueIndex)
Expand All @@ -196,7 +188,7 @@ func (n *Node) walk(parent []byte, handler func(key []byte, valueIndex uint32))
}
}

func (n *Node) matchNodes(input []byte, offset int, handler func(key []byte, valueIndex uint32) bool) bool {
func (n *Node[T]) matchNodes(input []byte, offset int, handler func(key []byte, valueIndex uint32) bool) bool {
hasMatch := false
if n.isEdgeType() {
if !n.bset.IsSet(input[offset]) {
Expand All @@ -213,7 +205,7 @@ func (n *Node) matchNodes(input []byte, offset int, handler func(key []byte, val
return hasMatch
}

func (n *Node) match(input []byte, offset int, handler func(key []byte, valueIndex uint32) bool) bool {
func (n *Node[T]) match(input []byte, offset int, handler func(key []byte, valueIndex uint32) bool) bool {
if offset >= len(input) {
return false
}
Expand Down Expand Up @@ -243,7 +235,7 @@ func (n *Node) match(input []byte, offset int, handler func(key []byte, valueInd
}

// Encode encode node
func (n *Node) Encode(writer io.Writer) error {
func (n *Node[T]) Encode(writer io.Writer) error {
var err error
if err = binary.Write(writer, binary.LittleEndian, controlByte); err == nil {
if err = binary.Write(writer, binary.LittleEndian, n.Type); err == nil {
Expand All @@ -262,7 +254,7 @@ func (n *Node) Encode(writer io.Writer) error {
return err
}

func (n *Node) size() int {
func (n *Node[T]) size() int {
result := 2 + 4 + len(n.Prefix)
if n.isValueType() {
result += 4
Expand All @@ -276,7 +268,7 @@ func (n *Node) size() int {
return result
}

func (n *Node) encodeNodes(writer io.Writer) error {
func (n *Node[T]) encodeNodes(writer io.Writer) error {
var err error
if !n.isEdgeType() {
return err
Expand All @@ -295,7 +287,7 @@ func (n *Node) encodeNodes(writer io.Writer) error {
}

// Decode decode node
func (n *Node) Decode(reader io.Reader) error {
func (n *Node[T]) Decode(reader io.Reader) error {
var err error
var control uint8
if err = binary.Read(reader, binary.LittleEndian, &control); err == nil {
Expand All @@ -321,7 +313,7 @@ func (n *Node) Decode(reader io.Reader) error {
return err
}

func (n *Node) decodeNodes(reader io.Reader) error {
func (n *Node[T]) decodeNodes(reader io.Reader) error {
var err error
if !n.isEdgeType() {
return err
Expand All @@ -332,7 +324,7 @@ func (n *Node) decodeNodes(reader io.Reader) error {
if err = binary.Read(reader, binary.LittleEndian, &bset); err == nil {
n.bset = Bit64Set(bset)
if err = binary.Read(reader, binary.LittleEndian, &nodeLength); err == nil {
n.Nodes = make([]Node, nodeLength)
n.Nodes = make([]Node[T], nodeLength)
for i := range n.Nodes {
if err = n.Nodes[i].Decode(reader); err != nil {
return err
Expand All @@ -343,8 +335,8 @@ func (n *Node) decodeNodes(reader io.Reader) error {
return err
}

func newValueNode(prefix []byte, valueIndex uint32) *Node {
node := &Node{
func newValueNode[T any](prefix []byte, valueIndex uint32) *Node[T] {
node := &Node[T]{
Prefix: prefix,
ValueIndex: valueIndex,
}
Expand Down
22 changes: 11 additions & 11 deletions node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ func TestNode_LoadNode(t *testing.T) {
}

for _, useCase := range useCases {
node := newValueNode([]byte("/"), 0)
node := newValueNode[uint32]([]byte("/"), 0)
for i, keyword := range useCase.keywords {
node.add(newValueNode([]byte(keyword), uint32(i+1)), nil)
node.add(newValueNode[uint32]([]byte(keyword), uint32(i+1)), nil)
}
data := node.Data()

cloned := &Node{}
cloned := &Node[uint32]{}
cloned.LoadNode(data)
assert.True(t, node.Equals(cloned))
if !assertly.AssertValues(t, node, cloned, useCase.description) {
Expand Down Expand Up @@ -70,9 +70,9 @@ func TestNode_Decode(t *testing.T) {
}

for _, useCase := range useCases {
node := newValueNode([]byte("/"), 0)
node := newValueNode[uint32]([]byte("/"), 0)
for i, keyword := range useCase.keywords {
node.add(newValueNode([]byte(keyword), uint32(i+1)), nil)
node.add(newValueNode[uint32]([]byte(keyword), uint32(i+1)), nil)
}
writer := new(bytes.Buffer)
err := node.Encode(writer)
Expand All @@ -82,7 +82,7 @@ func TestNode_Decode(t *testing.T) {
}
assert.EqualValues(t, writer.Len(), node.size(), useCase.description)

cloned := &Node{}
cloned := &Node[uint32]{}
err = cloned.Decode(bytes.NewReader(writer.Bytes()))
if assert.Nil(t, err, useCase.description) {
continue
Expand All @@ -96,7 +96,7 @@ func TestNode_Decode(t *testing.T) {

//test error case
reader := strings.NewReader("test is error")
node := &Node{}
node := &Node[uint32]{}
err := node.Decode(reader)
assert.NotNil(t, err)
}
Expand Down Expand Up @@ -125,12 +125,12 @@ func TestNode_walk(t *testing.T) {
}

for _, useCase := range useCases {
node := newValueNode([]byte(""), 0)
node := newValueNode[uint32]([]byte(""), 0)
var expect = make(map[string]uint32)
var actual = make(map[string]uint32)
for i, keyword := range useCase.keywords {
expect[string(keyword)] = uint32(i + 1)
node.add(newValueNode([]byte(keyword), uint32(i+1)), nil)
node.add(newValueNode[uint32]([]byte(keyword), uint32(i+1)), nil)
}
node.walk([]byte{}, func(key []byte, valueIndex uint32) {
actual[string(key)] = valueIndex
Expand Down Expand Up @@ -252,9 +252,9 @@ func TestNode_match(t *testing.T) {
}

for _, useCase := range useCases {
node := newValueNode([]byte(""), 0)
node := newValueNode[uint32]([]byte(""), 0)
for i, keyword := range useCase.keywords {
node.add(newValueNode([]byte(keyword), uint32(i+1)), func(prev uint32) uint32 {
node.add(newValueNode[uint32]([]byte(keyword), uint32(i+1)), func(prev uint32) uint32 {
return prev
})
}
Expand Down
14 changes: 7 additions & 7 deletions nodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import (
)

// Nodes represents node slice
type Nodes []Node
type Nodes[T any] []Node[T]

func (n *Nodes) add(node *Node, merger merger) {
func (n *Nodes[T]) add(node *Node[T], merger merger) {
index := n.IndexOf(node.Prefix[0])
if index == -1 {
*n = append(*n, *node)
Expand Down Expand Up @@ -49,7 +49,7 @@ func (n *Nodes) add(node *Node, merger merger) {
return
}

edge := Node{Type: NodeTypeEdge, Prefix: node.Prefix[:sharedPrefixIndex+1], Nodes: Nodes{}}
edge := Node[T]{Type: NodeTypeEdge, Prefix: node.Prefix[:sharedPrefixIndex+1], Nodes: Nodes[T]{}}
edge.add(sharedNode, nil)
node.Prefix = node.Prefix[sharedPrefixIndex+1:]
edge.add(node, nil)
Expand All @@ -58,7 +58,7 @@ func (n *Nodes) add(node *Node, merger merger) {
}

// IndexOf returns index of expectMatched byte or -1
func (n Nodes) IndexOf(b byte) int {
func (n Nodes[T]) IndexOf(b byte) int {
lowerBoundIndex := 0
upperBoundIndex := len(n) - 1
loop:
Expand All @@ -77,6 +77,6 @@ loop:
return -1
}

func (n Nodes) Len() int { return len(n) }
func (n Nodes) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
func (n Nodes) Less(i, j int) bool { return n[i].Prefix[0] < n[j].Prefix[0] }
func (n Nodes[T]) Len() int { return len(n) }
func (n Nodes[T]) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
func (n Nodes[T]) Less(i, j int) bool { return n[i].Prefix[0] < n[j].Prefix[0] }
12 changes: 6 additions & 6 deletions nodes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ func TestNodes_add(t *testing.T) {
}

for _, useCase := range useCases {
node := newValueNode([]byte("/"), 0)
node := newValueNode[[]byte]([]byte("/"), 0)
for i, keyword := range useCase.keywords {
node.add(newValueNode([]byte(keyword), uint32(i+1)), useCase.merger)
node.add(newValueNode[[]byte]([]byte(keyword), uint32(i+1)), useCase.merger)
}
expect, err := loadData(path.Join(parent, useCase.expectURI))
if !assert.Nil(t, err, useCase.description) {
Expand Down Expand Up @@ -147,9 +147,9 @@ func TestNodes_IndexOf(t *testing.T) {
}

for _, useCase := range useCases {
nodes := Nodes{}
nodes := Nodes[[]byte]{}
for i := 0; i < len(useCase.prefixes); i++ {
nodes = append(nodes, Node{Prefix: []byte(string(useCase.prefixes[i]))})
nodes = append(nodes, Node[[]byte]{Prefix: []byte(string(useCase.prefixes[i]))})
}
actualIndex := nodes.IndexOf(useCase.search)
assert.Equal(t, useCase.expectIndex, actualIndex, useCase.description)
Expand All @@ -159,10 +159,10 @@ func TestNodes_IndexOf(t *testing.T) {
}

func BenchmarkNodes_IndexOf(b *testing.B) {
nodes := Nodes{}
nodes := Nodes[[]byte]{}
for i := 32; i < 98; i += 3 {
//fmt.Printf("%v\n", i)
nodes.add(&Node{Prefix: []byte{byte(i)}}, func(prev uint32) uint32 {
nodes.add(&Node[[]byte]{Prefix: []byte{byte(i)}}, func(prev uint32) uint32 {
return 0
})
}
Expand Down
Loading

0 comments on commit 98c1314

Please sign in to comment.