-
Notifications
You must be signed in to change notification settings - Fork 386
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'dev/zivkovicmilos/lazy-genesis-sigs' of https://github.…
…com/gnolang/gno into dev/zivkovicmilos/lazy-genesis-sigs
- Loading branch information
Showing
33 changed files
with
1,025 additions
and
82 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
// Package addrset provides a specialized set data structure for managing unique Gno addresses. | ||
// | ||
// It is built on top of an AVL tree for efficient operations and maintains addresses in sorted order. | ||
// This package is particularly useful when you need to: | ||
// - Track a collection of unique addresses (e.g., for whitelists, participants, etc.) | ||
// - Efficiently check address membership | ||
// - Support pagination when displaying addresses | ||
// | ||
// Example usage: | ||
// | ||
// import ( | ||
// "std" | ||
// "gno.land/p/moul/addrset" | ||
// ) | ||
// | ||
// func MyHandler() { | ||
// // Create a new address set | ||
// var set addrset.Set | ||
// | ||
// // Add some addresses | ||
// addr1 := std.Address("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") | ||
// addr2 := std.Address("g1sss5g0rkqr88k4u648yd5d3l9t4d8vvqwszqth") | ||
// | ||
// set.Add(addr1) // returns true (newly added) | ||
// set.Add(addr2) // returns true (newly added) | ||
// set.Add(addr1) // returns false (already exists) | ||
// | ||
// // Check membership | ||
// if set.Has(addr1) { | ||
// // addr1 is in the set | ||
// } | ||
// | ||
// // Get size | ||
// size := set.Size() // returns 2 | ||
// | ||
// // Iterate with pagination (10 items per page, starting at offset 0) | ||
// set.IterateByOffset(0, 10, func(addr std.Address) bool { | ||
// // Process addr | ||
// return false // continue iteration | ||
// }) | ||
// | ||
// // Remove an address | ||
// set.Remove(addr1) // returns true (was present) | ||
// set.Remove(addr1) // returns false (not present) | ||
// } | ||
package addrset | ||
|
||
import ( | ||
"std" | ||
|
||
"gno.land/p/demo/avl" | ||
) | ||
|
||
type Set struct { | ||
tree avl.Tree | ||
} | ||
|
||
// Add inserts an address into the set. | ||
// Returns true if the address was newly added, false if it already existed. | ||
func (s *Set) Add(addr std.Address) bool { | ||
return !s.tree.Set(string(addr), nil) | ||
} | ||
|
||
// Remove deletes an address from the set. | ||
// Returns true if the address was found and removed, false if it didn't exist. | ||
func (s *Set) Remove(addr std.Address) bool { | ||
_, removed := s.tree.Remove(string(addr)) | ||
return removed | ||
} | ||
|
||
// Has checks if an address exists in the set. | ||
func (s *Set) Has(addr std.Address) bool { | ||
return s.tree.Has(string(addr)) | ||
} | ||
|
||
// Size returns the number of addresses in the set. | ||
func (s *Set) Size() int { | ||
return s.tree.Size() | ||
} | ||
|
||
// IterateByOffset walks through addresses starting at the given offset. | ||
// The callback should return true to stop iteration. | ||
func (s *Set) IterateByOffset(offset int, count int, cb func(addr std.Address) bool) { | ||
s.tree.IterateByOffset(offset, count, func(key string, _ interface{}) bool { | ||
return cb(std.Address(key)) | ||
}) | ||
} | ||
|
||
// ReverseIterateByOffset walks through addresses in reverse order starting at the given offset. | ||
// The callback should return true to stop iteration. | ||
func (s *Set) ReverseIterateByOffset(offset int, count int, cb func(addr std.Address) bool) { | ||
s.tree.ReverseIterateByOffset(offset, count, func(key string, _ interface{}) bool { | ||
return cb(std.Address(key)) | ||
}) | ||
} | ||
|
||
// Tree returns the underlying AVL tree for advanced usage. | ||
func (s *Set) Tree() avl.ITree { | ||
return &s.tree | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
package addrset | ||
|
||
import ( | ||
"std" | ||
"testing" | ||
|
||
"gno.land/p/demo/uassert" | ||
) | ||
|
||
func TestSet(t *testing.T) { | ||
addr1 := std.Address("addr1") | ||
addr2 := std.Address("addr2") | ||
addr3 := std.Address("addr3") | ||
|
||
tests := []struct { | ||
name string | ||
actions func(s *Set) | ||
size int | ||
has map[std.Address]bool | ||
addrs []std.Address // for iteration checks | ||
}{ | ||
{ | ||
name: "empty set", | ||
actions: func(s *Set) {}, | ||
size: 0, | ||
has: map[std.Address]bool{addr1: false}, | ||
}, | ||
{ | ||
name: "single address", | ||
actions: func(s *Set) { | ||
s.Add(addr1) | ||
}, | ||
size: 1, | ||
has: map[std.Address]bool{ | ||
addr1: true, | ||
addr2: false, | ||
}, | ||
addrs: []std.Address{addr1}, | ||
}, | ||
{ | ||
name: "multiple addresses", | ||
actions: func(s *Set) { | ||
s.Add(addr1) | ||
s.Add(addr2) | ||
s.Add(addr3) | ||
}, | ||
size: 3, | ||
has: map[std.Address]bool{ | ||
addr1: true, | ||
addr2: true, | ||
addr3: true, | ||
}, | ||
addrs: []std.Address{addr1, addr2, addr3}, | ||
}, | ||
{ | ||
name: "remove address", | ||
actions: func(s *Set) { | ||
s.Add(addr1) | ||
s.Add(addr2) | ||
s.Remove(addr1) | ||
}, | ||
size: 1, | ||
has: map[std.Address]bool{ | ||
addr1: false, | ||
addr2: true, | ||
}, | ||
addrs: []std.Address{addr2}, | ||
}, | ||
{ | ||
name: "duplicate adds", | ||
actions: func(s *Set) { | ||
uassert.True(t, s.Add(addr1)) // first add returns true | ||
uassert.False(t, s.Add(addr1)) // second add returns false | ||
uassert.True(t, s.Remove(addr1)) // remove existing returns true | ||
uassert.False(t, s.Remove(addr1)) // remove non-existing returns false | ||
}, | ||
size: 0, | ||
has: map[std.Address]bool{ | ||
addr1: false, | ||
}, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
var set Set | ||
|
||
// Execute test actions | ||
tt.actions(&set) | ||
|
||
// Check size | ||
uassert.Equal(t, tt.size, set.Size()) | ||
|
||
// Check existence | ||
for addr, expected := range tt.has { | ||
uassert.Equal(t, expected, set.Has(addr)) | ||
} | ||
|
||
// Check iteration if addresses are specified | ||
if tt.addrs != nil { | ||
collected := []std.Address{} | ||
set.IterateByOffset(0, 10, func(addr std.Address) bool { | ||
collected = append(collected, addr) | ||
return false | ||
}) | ||
|
||
// Check length | ||
uassert.Equal(t, len(tt.addrs), len(collected)) | ||
|
||
// Check each address | ||
for i, addr := range tt.addrs { | ||
uassert.Equal(t, addr, collected[i]) | ||
} | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestSetIterationLimits(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
addrs []std.Address | ||
offset int | ||
limit int | ||
expected int | ||
}{ | ||
{ | ||
name: "zero offset full list", | ||
addrs: []std.Address{"a1", "a2", "a3"}, | ||
offset: 0, | ||
limit: 10, | ||
expected: 3, | ||
}, | ||
{ | ||
name: "offset with limit", | ||
addrs: []std.Address{"a1", "a2", "a3", "a4"}, | ||
offset: 1, | ||
limit: 2, | ||
expected: 2, | ||
}, | ||
{ | ||
name: "offset beyond size", | ||
addrs: []std.Address{"a1", "a2"}, | ||
offset: 3, | ||
limit: 1, | ||
expected: 0, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
var set Set | ||
for _, addr := range tt.addrs { | ||
set.Add(addr) | ||
} | ||
|
||
// Test forward iteration | ||
count := 0 | ||
set.IterateByOffset(tt.offset, tt.limit, func(addr std.Address) bool { | ||
count++ | ||
return false | ||
}) | ||
uassert.Equal(t, tt.expected, count) | ||
|
||
// Test reverse iteration | ||
count = 0 | ||
set.ReverseIterateByOffset(tt.offset, tt.limit, func(addr std.Address) bool { | ||
count++ | ||
return false | ||
}) | ||
uassert.Equal(t, tt.expected, count) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module gno.land/p/moul/addrset |
Oops, something went wrong.