Skip to content

Commit

Permalink
Clean up Set, add Set.AddAll
Browse files Browse the repository at this point in the history
  • Loading branch information
nickg committed May 17, 2020
1 parent aab3068 commit 15b028b
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 21 deletions.
81 changes: 61 additions & 20 deletions set.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,75 +5,116 @@ import (
)

// Set defines a unique set of IPv4 addresses using a simple []uint32
type Set struct {
Raw []uint32
}
type Set []uint32

// Len returns a length, part of the Sort.Interface
func (m Set) Len() int {
return len(m.Raw)
return len(m)
}

// Swap is part of the Sort.Interface
func (m Set) Swap(i, j int) {
m.Raw[i], m.Raw[j] = m.Raw[j], m.Raw[i]
m[i], m[j] = m[j], m[i]
}

// Less is part of the Sort.Interface
func (m Set) Less(i, j int) bool {
return m.Raw[i] < m.Raw[j]
return m[i] < m[j]
}

// Contains matches a dotted IPv4 address with an internal list
func (m Set) Contains(ipv4dots string) bool {
if len(m.Raw) == 0 {
if len(m) == 0 {
return false
}
x, err := FromDots(ipv4dots)
if err != nil {
return false
}
i := sort.Search(len(m.Raw), func(i int) bool { return m.Raw[i] >= x })
return (i < len(m.Raw) && m.Raw[i] == x)
i := sort.Search(len(m), func(i int) bool { return m[i] >= x })
return (i < len(m) && m[i] == x)
}

// Add an element to the set
// Add an element to the set.
func (m *Set) Add(ipv4dots string) bool {
x, err := FromDots(ipv4dots)
if err != nil {
return false
}
i := sort.Search(len(m.Raw), func(i int) bool { return m.Raw[i] >= x })
if i == len(m.Raw) {
orig := *m
i := sort.Search(len(orig), func(i int) bool { return orig[i] >= x })
if i == len(orig) {
// goes at end
m.Raw = append(m.Raw, x)
*m = append(orig, x)
return true
}
if m.Raw[i] == x {
if orig[i] == x {
// already exists
return false
}
// splice
// add one extra elemnt
m.Raw = append(m.Raw, 0)
orig = append(orig, 0)
// shift right
copy(m.Raw[i+1:], m.Raw[i:])
copy(orig[i+1:], orig[i:])
// insert new element in hole
m.Raw[i] = x
orig[i] = x
*m = orig
return true
}

// AddAll adds many IPv4 at once
func (m *Set) AddAll(ipv4dots []string) bool {
in := *m
for _, val := range ipv4dots {
if bval, err := FromDots(val); err == nil {
in = append(in, bval)
}
}
*m = in
m.sort()
return true
}

func (m *Set) sort() {
in := *m
sort.Sort(in)

// inplace sort
// https://github.com/golang/go/wiki/SliceTricks#in-place-deduplicate-comparable
j := 0
for i := 1; i < len(in); i++ {
if in[j] == in[i] {
continue
}
j++
// preserve the original data
// in[i], in[j] = in[j], in[i]
// only set what is required
in[j] = in[i]
}
in = in[:j+1]
}

// Valid return true if the internal storage is in sorted form and unique
func (m Set) Valid() bool {
if len(m.Raw) == 0 {
if len(m) == 0 {
return true
}
last := m.Raw[0]
for _, val := range m.Raw[1:] {
last := m[0]
for _, val := range m[1:] {
if val <= last {
return false
}
last = val
}
return true
}

func (m Set) ToDots() []string {
out := make([]string, len(m))
for i, val := range m {
out[i] = ToDots(val)
}
return out
}
19 changes: 18 additions & 1 deletion set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,26 @@ func TestAdd(t *testing.T) {
t.Errorf("Accepted junk input")
}

s.Raw = append(s.Raw, uint32(0))
// intentionally have data be unsorted
s = append(s, uint32(0))
if s.Valid() {
t.Errorf("have s.Valid() == true, want false")
}

s.sort()
if !s.Valid() {
t.Errorf("sorting did not work")
}
}

func TestAddAll(t *testing.T) {
s := Set{}
s.AddAll([]string{"127.0.0.1", "10.0.0.1"})
if !s.Valid() {
t.Fatalf("AddAll is not valid")
}
out := s.ToDots()
if out[0] != "10.0.0.1" || out[1] != "127.0.0.1" {
t.Fatalf("Dump failed: %v", out)
}
}

0 comments on commit 15b028b

Please sign in to comment.