diff --git a/internal/set/doc.go b/internal/set/doc.go new file mode 100644 index 00000000..d803514f --- /dev/null +++ b/internal/set/doc.go @@ -0,0 +1,21 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package set provides simple set data structures for uint64 and string types. +// +// The API for every set is: +// type Set(T {}) opaque +// +// // Len reports the number of elements in the set. +// func (Set) Len() int +// +// // Has reports whether an item is in the set. +// func (Set) Has(T) bool +// +// // Set inserts the item into the set. +// func (Set) Set(T) +// +// // Clear removes the item from the set. +// func (Set) Clear(T) +package set diff --git a/internal/set/ints.go b/internal/set/ints.go new file mode 100644 index 00000000..0632e5aa --- /dev/null +++ b/internal/set/ints.go @@ -0,0 +1,73 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package set + +import "math/bits" + +// Int32s represents a set of integers within the range of 0..31. +type Int32s uint32 + +func (bs *Int32s) Len() int { + return bits.OnesCount32(uint32(*bs)) +} +func (bs *Int32s) Has(n uint64) bool { + return uint32(*bs)&(uint32(1)< 0 +} +func (bs *Int32s) Set(n uint64) { + *(*uint32)(bs) |= uint32(1) << n +} +func (bs *Int32s) Clear(n uint64) { + *(*uint32)(bs) &^= uint32(1) << n +} + +// Int64s represents a set of integers within the range of 0..63. +type Int64s uint64 + +func (bs *Int64s) Len() int { + return bits.OnesCount64(uint64(*bs)) +} +func (bs *Int64s) Has(n uint64) bool { + return uint64(*bs)&(uint64(1)< 0 +} +func (bs *Int64s) Set(n uint64) { + *(*uint64)(bs) |= uint64(1) << n +} +func (bs *Int64s) Clear(n uint64) { + *(*uint64)(bs) &^= uint64(1) << n +} + +// Ints represents a set of integers within the range of 0..math.MaxUint64. +type Ints struct { + lo Int64s + hi map[uint64]struct{} +} + +func (bs *Ints) Len() int { + return bs.lo.Len() + len(bs.hi) +} +func (bs *Ints) Has(n uint64) bool { + if n < 64 { + return bs.lo.Has(n) + } + _, ok := bs.hi[n] + return ok +} +func (bs *Ints) Set(n uint64) { + if n < 64 { + bs.lo.Set(n) + return + } + if bs.hi == nil { + bs.hi = make(map[uint64]struct{}) + } + bs.hi[n] = struct{}{} +} +func (bs *Ints) Clear(n uint64) { + if n < 64 { + bs.lo.Clear(n) + return + } + delete(bs.hi, n) +} diff --git a/internal/set/ints_test.go b/internal/set/ints_test.go new file mode 100644 index 00000000..b370d327 --- /dev/null +++ b/internal/set/ints_test.go @@ -0,0 +1,96 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package set + +import ( + "math/rand" + "testing" +) + +const maxLimit = 1024 + +var toSet, toClear [maxLimit]bool + +func init() { + r := rand.New(rand.NewSource(0)) + for i := 0; i < maxLimit; i++ { + toSet[i] = r.Intn(2) == 0 + toClear[i] = r.Intn(2) == 0 + } +} + +func TestInts(t *testing.T) { + type set interface { + Len() int + Has(n uint64) bool + Set(n uint64) + Clear(n uint64) + } + + tests := []struct { + label string + makeSet func() set + limit int + }{ + {label: "Int32s", makeSet: func() set { return new(Int32s) }, limit: 32}, + {label: "Int64s", makeSet: func() set { return new(Int64s) }, limit: 64}, + {label: "Ints", makeSet: func() set { return new(Ints) }, limit: maxLimit}, + } + + for _, tt := range tests { + t.Run(tt.label, func(t *testing.T) { + ns := tt.makeSet() + + // Check that set starts empty. + wantLen := 0 + if ns.Len() != wantLen { + t.Errorf("init: Len() = %d, want %d", ns.Len(), wantLen) + } + for i := 0; i < tt.limit; i++ { + if ns.Has(uint64(i)) { + t.Errorf("init: Has(%d) = true, want false", i) + } + } + + // Set some numbers. + for i, b := range toSet[:tt.limit] { + if b { + ns.Set(uint64(i)) + wantLen++ + } + } + + // Check that integers were set. + if ns.Len() != wantLen { + t.Errorf("after Set: Len() = %d, want %d", ns.Len(), wantLen) + } + for i := 0; i < tt.limit; i++ { + if got := ns.Has(uint64(i)); got != toSet[i] { + t.Errorf("after Set: Has(%d) = %v, want %v", i, got, !got) + } + } + + // Clear some numbers. + for i, b := range toClear[:tt.limit] { + if b { + ns.Clear(uint64(i)) + if toSet[i] { + wantLen-- + } + } + } + + // Check that integers were cleared. + if ns.Len() != wantLen { + t.Errorf("after Clear: Len() = %d, want %d", ns.Len(), wantLen) + } + for i := 0; i < tt.limit; i++ { + if got := ns.Has(uint64(i)); got != toSet[i] && !toClear[i] { + t.Errorf("after Clear: Has(%d) = %v, want %v", i, got, !got) + } + } + }) + } +} diff --git a/internal/set/strings.go b/internal/set/strings.go new file mode 100644 index 00000000..e81dc10b --- /dev/null +++ b/internal/set/strings.go @@ -0,0 +1,25 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package set + +// Strings represents a set of strings. +type Strings map[string]struct{} + +func (ss *Strings) Len() int { + return len(*ss) +} +func (ss *Strings) Has(s string) bool { + _, ok := (*ss)[s] + return ok +} +func (ss *Strings) Set(s string) { + if *ss == nil { + *ss = make(map[string]struct{}) + } + (*ss)[s] = struct{}{} +} +func (ss *Strings) Clear(s string) { + delete(*ss, s) +} diff --git a/internal/set/strings_test.go b/internal/set/strings_test.go new file mode 100644 index 00000000..0b99bf76 --- /dev/null +++ b/internal/set/strings_test.go @@ -0,0 +1,63 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package set + +import ( + "strconv" + "testing" +) + +func TestStrings(t *testing.T) { + var ss Strings + + // Check that set starts empty. + wantLen := 0 + if ss.Len() != wantLen { + t.Errorf("init: Len() = %d, want %d", ss.Len(), wantLen) + } + for i := 0; i < maxLimit; i++ { + if ss.Has(strconv.Itoa(i)) { + t.Errorf("init: Has(%d) = true, want false", i) + } + } + + // Set some strings. + for i, b := range toSet[:maxLimit] { + if b { + ss.Set(strconv.Itoa(i)) + wantLen++ + } + } + + // Check that strings were set. + if ss.Len() != wantLen { + t.Errorf("after Set: Len() = %d, want %d", ss.Len(), wantLen) + } + for i := 0; i < maxLimit; i++ { + if got := ss.Has(strconv.Itoa(i)); got != toSet[i] { + t.Errorf("after Set: Has(%d) = %v, want %v", i, got, !got) + } + } + + // Clear some strings. + for i, b := range toClear[:maxLimit] { + if b { + ss.Clear(strconv.Itoa(i)) + if toSet[i] { + wantLen-- + } + } + } + + // Check that strings were cleared. + if ss.Len() != wantLen { + t.Errorf("after Clear: Len() = %d, want %d", ss.Len(), wantLen) + } + for i := 0; i < maxLimit; i++ { + if got := ss.Has(strconv.Itoa(i)); got != toSet[i] && !toClear[i] { + t.Errorf("after Clear: Has(%d) = %v, want %v", i, got, !got) + } + } +}