all: switch out defunct set library to different one (#16873)
* keystore, ethash, eth, miner, rpc, whisperv6: tech debt with now defunct set. * whisperv5: swap out gopkg.in/fatih/set.v0 with supported set
This commit is contained in:
parent
eb7f901289
commit
5d30be412b
1
AUTHORS
1
AUTHORS
|
@ -171,3 +171,4 @@ xiekeyang <xiekeyang@users.noreply.github.com>
|
|||
yoza <yoza.is12s@gmail.com>
|
||||
ΞTHΞЯSPHΞЯΞ <{viktor.tron,nagydani,zsfelfoldi}@gmail.com>
|
||||
Максим Чусовлянов <mchusovlianov@gmail.com>
|
||||
Ralph Caraveo <deckarep@gmail.com>
|
||||
|
|
|
@ -27,10 +27,10 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
mapset "github.com/deckarep/golang-set"
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"gopkg.in/fatih/set.v0"
|
||||
)
|
||||
|
||||
// Minimum amount of time between cache reloads. This limit applies if the platform does
|
||||
|
@ -79,7 +79,7 @@ func newAccountCache(keydir string) (*accountCache, chan struct{}) {
|
|||
keydir: keydir,
|
||||
byAddr: make(map[common.Address][]accounts.Account),
|
||||
notify: make(chan struct{}, 1),
|
||||
fileC: fileCache{all: set.NewNonTS()},
|
||||
fileC: fileCache{all: mapset.NewThreadUnsafeSet()},
|
||||
}
|
||||
ac.watcher = newWatcher(ac)
|
||||
return ac, ac.notify
|
||||
|
@ -237,7 +237,7 @@ func (ac *accountCache) scanAccounts() error {
|
|||
log.Debug("Failed to reload keystore contents", "err", err)
|
||||
return err
|
||||
}
|
||||
if creates.Size() == 0 && deletes.Size() == 0 && updates.Size() == 0 {
|
||||
if creates.Cardinality() == 0 && deletes.Cardinality() == 0 && updates.Cardinality() == 0 {
|
||||
return nil
|
||||
}
|
||||
// Create a helper method to scan the contents of the key files
|
||||
|
@ -272,15 +272,15 @@ func (ac *accountCache) scanAccounts() error {
|
|||
// Process all the file diffs
|
||||
start := time.Now()
|
||||
|
||||
for _, p := range creates.List() {
|
||||
for _, p := range creates.ToSlice() {
|
||||
if a := readAccount(p.(string)); a != nil {
|
||||
ac.add(*a)
|
||||
}
|
||||
}
|
||||
for _, p := range deletes.List() {
|
||||
for _, p := range deletes.ToSlice() {
|
||||
ac.deleteByFile(p.(string))
|
||||
}
|
||||
for _, p := range updates.List() {
|
||||
for _, p := range updates.ToSlice() {
|
||||
path := p.(string)
|
||||
ac.deleteByFile(path)
|
||||
if a := readAccount(path); a != nil {
|
||||
|
|
|
@ -24,20 +24,20 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
mapset "github.com/deckarep/golang-set"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
set "gopkg.in/fatih/set.v0"
|
||||
)
|
||||
|
||||
// fileCache is a cache of files seen during scan of keystore.
|
||||
type fileCache struct {
|
||||
all *set.SetNonTS // Set of all files from the keystore folder
|
||||
lastMod time.Time // Last time instance when a file was modified
|
||||
all mapset.Set // Set of all files from the keystore folder
|
||||
lastMod time.Time // Last time instance when a file was modified
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// scan performs a new scan on the given directory, compares against the already
|
||||
// cached filenames, and returns file sets: creates, deletes, updates.
|
||||
func (fc *fileCache) scan(keyDir string) (set.Interface, set.Interface, set.Interface, error) {
|
||||
func (fc *fileCache) scan(keyDir string) (mapset.Set, mapset.Set, mapset.Set, error) {
|
||||
t0 := time.Now()
|
||||
|
||||
// List all the failes from the keystore folder
|
||||
|
@ -51,8 +51,8 @@ func (fc *fileCache) scan(keyDir string) (set.Interface, set.Interface, set.Inte
|
|||
defer fc.mu.Unlock()
|
||||
|
||||
// Iterate all the files and gather their metadata
|
||||
all := set.NewNonTS()
|
||||
mods := set.NewNonTS()
|
||||
all := mapset.NewThreadUnsafeSet()
|
||||
mods := mapset.NewThreadUnsafeSet()
|
||||
|
||||
var newLastMod time.Time
|
||||
for _, fi := range files {
|
||||
|
@ -76,9 +76,9 @@ func (fc *fileCache) scan(keyDir string) (set.Interface, set.Interface, set.Inte
|
|||
t2 := time.Now()
|
||||
|
||||
// Update the tracked files and return the three sets
|
||||
deletes := set.Difference(fc.all, all) // Deletes = previous - current
|
||||
creates := set.Difference(all, fc.all) // Creates = current - previous
|
||||
updates := set.Difference(mods, creates) // Updates = modified - creates
|
||||
deletes := fc.all.Difference(all) // Deletes = previous - current
|
||||
creates := all.Difference(fc.all) // Creates = current - previous
|
||||
updates := mods.Difference(creates) // Updates = modified - creates
|
||||
|
||||
fc.all, fc.lastMod = all, newLastMod
|
||||
t3 := time.Now()
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
"runtime"
|
||||
"time"
|
||||
|
||||
mapset "github.com/deckarep/golang-set"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
|
@ -31,7 +32,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
set "gopkg.in/fatih/set.v0"
|
||||
)
|
||||
|
||||
// Ethash proof-of-work protocol constants.
|
||||
|
@ -177,7 +177,7 @@ func (ethash *Ethash) VerifyUncles(chain consensus.ChainReader, block *types.Blo
|
|||
return errTooManyUncles
|
||||
}
|
||||
// Gather the set of past uncles and ancestors
|
||||
uncles, ancestors := set.New(), make(map[common.Hash]*types.Header)
|
||||
uncles, ancestors := mapset.NewSet(), make(map[common.Hash]*types.Header)
|
||||
|
||||
number, parent := block.NumberU64()-1, block.ParentHash()
|
||||
for i := 0; i < 7; i++ {
|
||||
|
@ -198,7 +198,7 @@ func (ethash *Ethash) VerifyUncles(chain consensus.ChainReader, block *types.Blo
|
|||
for _, uncle := range block.Uncles() {
|
||||
// Make sure every uncle is rewarded only once
|
||||
hash := uncle.Hash()
|
||||
if uncles.Has(hash) {
|
||||
if uncles.Contains(hash) {
|
||||
return errDuplicateUncle
|
||||
}
|
||||
uncles.Add(hash)
|
||||
|
|
18
eth/peer.go
18
eth/peer.go
|
@ -23,11 +23,11 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
mapset "github.com/deckarep/golang-set"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"gopkg.in/fatih/set.v0"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -85,8 +85,8 @@ type peer struct {
|
|||
td *big.Int
|
||||
lock sync.RWMutex
|
||||
|
||||
knownTxs *set.Set // Set of transaction hashes known to be known by this peer
|
||||
knownBlocks *set.Set // Set of block hashes known to be known by this peer
|
||||
knownTxs mapset.Set // Set of transaction hashes known to be known by this peer
|
||||
knownBlocks mapset.Set // Set of block hashes known to be known by this peer
|
||||
queuedTxs chan []*types.Transaction // Queue of transactions to broadcast to the peer
|
||||
queuedProps chan *propEvent // Queue of blocks to broadcast to the peer
|
||||
queuedAnns chan *types.Block // Queue of blocks to announce to the peer
|
||||
|
@ -99,8 +99,8 @@ func newPeer(version int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
|
|||
rw: rw,
|
||||
version: version,
|
||||
id: fmt.Sprintf("%x", p.ID().Bytes()[:8]),
|
||||
knownTxs: set.New(),
|
||||
knownBlocks: set.New(),
|
||||
knownTxs: mapset.NewSet(),
|
||||
knownBlocks: mapset.NewSet(),
|
||||
queuedTxs: make(chan []*types.Transaction, maxQueuedTxs),
|
||||
queuedProps: make(chan *propEvent, maxQueuedProps),
|
||||
queuedAnns: make(chan *types.Block, maxQueuedAnns),
|
||||
|
@ -177,7 +177,7 @@ func (p *peer) SetHead(hash common.Hash, td *big.Int) {
|
|||
// never be propagated to this particular peer.
|
||||
func (p *peer) MarkBlock(hash common.Hash) {
|
||||
// If we reached the memory allowance, drop a previously known block hash
|
||||
for p.knownBlocks.Size() >= maxKnownBlocks {
|
||||
for p.knownBlocks.Cardinality() >= maxKnownBlocks {
|
||||
p.knownBlocks.Pop()
|
||||
}
|
||||
p.knownBlocks.Add(hash)
|
||||
|
@ -187,7 +187,7 @@ func (p *peer) MarkBlock(hash common.Hash) {
|
|||
// will never be propagated to this particular peer.
|
||||
func (p *peer) MarkTransaction(hash common.Hash) {
|
||||
// If we reached the memory allowance, drop a previously known transaction hash
|
||||
for p.knownTxs.Size() >= maxKnownTxs {
|
||||
for p.knownTxs.Cardinality() >= maxKnownTxs {
|
||||
p.knownTxs.Pop()
|
||||
}
|
||||
p.knownTxs.Add(hash)
|
||||
|
@ -470,7 +470,7 @@ func (ps *peerSet) PeersWithoutBlock(hash common.Hash) []*peer {
|
|||
|
||||
list := make([]*peer, 0, len(ps.peers))
|
||||
for _, p := range ps.peers {
|
||||
if !p.knownBlocks.Has(hash) {
|
||||
if !p.knownBlocks.Contains(hash) {
|
||||
list = append(list, p)
|
||||
}
|
||||
}
|
||||
|
@ -485,7 +485,7 @@ func (ps *peerSet) PeersWithoutTx(hash common.Hash) []*peer {
|
|||
|
||||
list := make([]*peer, 0, len(ps.peers))
|
||||
for _, p := range ps.peers {
|
||||
if !p.knownTxs.Has(hash) {
|
||||
if !p.knownTxs.Contains(hash) {
|
||||
list = append(list, p)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
mapset "github.com/deckarep/golang-set"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||
|
@ -35,7 +36,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"gopkg.in/fatih/set.v0"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -67,9 +67,9 @@ type Work struct {
|
|||
signer types.Signer
|
||||
|
||||
state *state.StateDB // apply state changes here
|
||||
ancestors *set.Set // ancestor set (used for checking uncle parent validity)
|
||||
family *set.Set // family set (used for checking uncle invalidity)
|
||||
uncles *set.Set // uncle set
|
||||
ancestors mapset.Set // ancestor set (used for checking uncle parent validity)
|
||||
family mapset.Set // family set (used for checking uncle invalidity)
|
||||
uncles mapset.Set // uncle set
|
||||
tcount int // tx count in cycle
|
||||
gasPool *core.GasPool // available gas used to pack transactions
|
||||
|
||||
|
@ -362,9 +362,9 @@ func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error
|
|||
config: self.config,
|
||||
signer: types.NewEIP155Signer(self.config.ChainID),
|
||||
state: state,
|
||||
ancestors: set.New(),
|
||||
family: set.New(),
|
||||
uncles: set.New(),
|
||||
ancestors: mapset.NewSet(),
|
||||
family: mapset.NewSet(),
|
||||
uncles: mapset.NewSet(),
|
||||
header: header,
|
||||
createdAt: time.Now(),
|
||||
}
|
||||
|
@ -492,13 +492,13 @@ func (self *worker) commitNewWork() {
|
|||
|
||||
func (self *worker) commitUncle(work *Work, uncle *types.Header) error {
|
||||
hash := uncle.Hash()
|
||||
if work.uncles.Has(hash) {
|
||||
if work.uncles.Contains(hash) {
|
||||
return fmt.Errorf("uncle not unique")
|
||||
}
|
||||
if !work.ancestors.Has(uncle.ParentHash) {
|
||||
if !work.ancestors.Contains(uncle.ParentHash) {
|
||||
return fmt.Errorf("uncle's parent unknown (%x)", uncle.ParentHash[0:4])
|
||||
}
|
||||
if work.family.Has(hash) {
|
||||
if work.family.Contains(hash) {
|
||||
return fmt.Errorf("uncle already in family (%x)", hash)
|
||||
}
|
||||
work.uncles.Add(uncle.Hash())
|
||||
|
|
|
@ -25,8 +25,8 @@ import (
|
|||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
mapset "github.com/deckarep/golang-set"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"gopkg.in/fatih/set.v0"
|
||||
)
|
||||
|
||||
const MetadataApi = "rpc"
|
||||
|
@ -46,7 +46,7 @@ const (
|
|||
func NewServer() *Server {
|
||||
server := &Server{
|
||||
services: make(serviceRegistry),
|
||||
codecs: set.New(),
|
||||
codecs: mapset.NewSet(),
|
||||
run: 1,
|
||||
}
|
||||
|
||||
|
|
|
@ -23,8 +23,8 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
|
||||
mapset "github.com/deckarep/golang-set"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"gopkg.in/fatih/set.v0"
|
||||
)
|
||||
|
||||
// API describes the set of methods offered over the RPC interface
|
||||
|
@ -73,7 +73,7 @@ type Server struct {
|
|||
|
||||
run int32
|
||||
codecsMu sync.Mutex
|
||||
codecs *set.Set
|
||||
codecs mapset.Set
|
||||
}
|
||||
|
||||
// rpcRequest represents a raw incoming RPC request
|
||||
|
|
|
@ -29,9 +29,9 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
mapset "github.com/deckarep/golang-set"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"golang.org/x/net/websocket"
|
||||
"gopkg.in/fatih/set.v0"
|
||||
)
|
||||
|
||||
// websocketJSONCodec is a custom JSON codec with payload size enforcement and
|
||||
|
@ -84,7 +84,7 @@ func NewWSServer(allowedOrigins []string, srv *Server) *http.Server {
|
|||
// websocket upgrade process. When a '*' is specified as an allowed origins all
|
||||
// connections are accepted.
|
||||
func wsHandshakeValidator(allowedOrigins []string) func(*websocket.Config, *http.Request) error {
|
||||
origins := set.New()
|
||||
origins := mapset.NewSet()
|
||||
allowAllOrigins := false
|
||||
|
||||
for _, origin := range allowedOrigins {
|
||||
|
@ -97,18 +97,18 @@ func wsHandshakeValidator(allowedOrigins []string) func(*websocket.Config, *http
|
|||
}
|
||||
|
||||
// allow localhost if no allowedOrigins are specified.
|
||||
if len(origins.List()) == 0 {
|
||||
if len(origins.ToSlice()) == 0 {
|
||||
origins.Add("http://localhost")
|
||||
if hostname, err := os.Hostname(); err == nil {
|
||||
origins.Add("http://" + strings.ToLower(hostname))
|
||||
}
|
||||
}
|
||||
|
||||
log.Debug(fmt.Sprintf("Allowed origin(s) for WS RPC interface %v\n", origins.List()))
|
||||
log.Debug(fmt.Sprintf("Allowed origin(s) for WS RPC interface %v\n", origins.ToSlice()))
|
||||
|
||||
f := func(cfg *websocket.Config, req *http.Request) error {
|
||||
origin := strings.ToLower(req.Header.Get("Origin"))
|
||||
if allowAllOrigins || origins.Has(origin) {
|
||||
if allowAllOrigins || origins.Contains(origin) {
|
||||
return nil
|
||||
}
|
||||
log.Warn(fmt.Sprintf("origin '%s' not allowed on WS-RPC interface\n", origin))
|
||||
|
|
22
vendor/gopkg.in/fatih/set.v0/LICENSE.md → vendor/github.com/deckarep/golang-set/LICENSE
generated
vendored
22
vendor/gopkg.in/fatih/set.v0/LICENSE.md → vendor/github.com/deckarep/golang-set/LICENSE
generated
vendored
|
@ -1,20 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
Open Source Initiative OSI - The MIT License (MIT):Licensing
|
||||
|
||||
Copyright (c) 2013 Fatih Arslan
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2013 Ralph Caraveo (deckarep@gmail.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,95 @@
|
|||
[![Build Status](https://travis-ci.org/deckarep/golang-set.svg?branch=master)](https://travis-ci.org/deckarep/golang-set)
|
||||
[![Go Report Card](https://goreportcard.com/badge/github.com/deckarep/golang-set)](https://goreportcard.com/report/github.com/deckarep/golang-set)
|
||||
[![GoDoc](https://godoc.org/github.com/deckarep/golang-set?status.svg)](http://godoc.org/github.com/deckarep/golang-set)
|
||||
|
||||
## golang-set
|
||||
|
||||
|
||||
The missing set collection for the Go language. Until Go has sets built-in...use this.
|
||||
|
||||
Coming from Python one of the things I miss is the superbly wonderful set collection. This is my attempt to mimic the primary features of the set from Python.
|
||||
You can of course argue that there is no need for a set in Go, otherwise the creators would have added one to the standard library. To those I say simply ignore this repository
|
||||
and carry-on and to the rest that find this useful please contribute in helping me make it better by:
|
||||
|
||||
* Helping to make more idiomatic improvements to the code.
|
||||
* Helping to increase the performance of it. ~~(So far, no attempt has been made, but since it uses a map internally, I expect it to be mostly performant.)~~
|
||||
* Helping to make the unit-tests more robust and kick-ass.
|
||||
* Helping to fill in the [documentation.](http://godoc.org/github.com/deckarep/golang-set)
|
||||
* Simply offering feedback and suggestions. (Positive, constructive feedback is appreciated.)
|
||||
|
||||
I have to give some credit for helping seed the idea with this post on [stackoverflow.](http://programmers.stackexchange.com/questions/177428/sets-data-structure-in-golang)
|
||||
|
||||
*Update* - as of 3/9/2014, you can use a compile-time generic version of this package in the [gen](http://clipperhouse.github.io/gen/) framework. This framework allows you to use the golang-set in a completely generic and type-safe way by allowing you to generate a supporting .go file based on your custom types.
|
||||
|
||||
## Features (as of 9/22/2014)
|
||||
|
||||
* a CartesianProduct() method has been added with unit-tests: [Read more about the cartesian product](http://en.wikipedia.org/wiki/Cartesian_product)
|
||||
|
||||
## Features (as of 9/15/2014)
|
||||
|
||||
* a PowerSet() method has been added with unit-tests: [Read more about the Power set](http://en.wikipedia.org/wiki/Power_set)
|
||||
|
||||
## Features (as of 4/22/2014)
|
||||
|
||||
* One common interface to both implementations
|
||||
* Two set implementations to choose from
|
||||
* a thread-safe implementation designed for concurrent use
|
||||
* a non-thread-safe implementation designed for performance
|
||||
* 75 benchmarks for both implementations
|
||||
* 35 unit tests for both implementations
|
||||
* 14 concurrent tests for the thread-safe implementation
|
||||
|
||||
|
||||
|
||||
Please see the unit test file for additional usage examples. The Python set documentation will also do a better job than I can of explaining how a set typically [works.](http://docs.python.org/2/library/sets.html) Please keep in mind
|
||||
however that the Python set is a built-in type and supports additional features and syntax that make it awesome.
|
||||
|
||||
## Examples but not exhaustive:
|
||||
|
||||
```go
|
||||
requiredClasses := mapset.NewSet()
|
||||
requiredClasses.Add("Cooking")
|
||||
requiredClasses.Add("English")
|
||||
requiredClasses.Add("Math")
|
||||
requiredClasses.Add("Biology")
|
||||
|
||||
scienceSlice := []interface{}{"Biology", "Chemistry"}
|
||||
scienceClasses := mapset.NewSetFromSlice(scienceSlice)
|
||||
|
||||
electiveClasses := mapset.NewSet()
|
||||
electiveClasses.Add("Welding")
|
||||
electiveClasses.Add("Music")
|
||||
electiveClasses.Add("Automotive")
|
||||
|
||||
bonusClasses := mapset.NewSet()
|
||||
bonusClasses.Add("Go Programming")
|
||||
bonusClasses.Add("Python Programming")
|
||||
|
||||
//Show me all the available classes I can take
|
||||
allClasses := requiredClasses.Union(scienceClasses).Union(electiveClasses).Union(bonusClasses)
|
||||
fmt.Println(allClasses) //Set{Cooking, English, Math, Chemistry, Welding, Biology, Music, Automotive, Go Programming, Python Programming}
|
||||
|
||||
|
||||
//Is cooking considered a science class?
|
||||
fmt.Println(scienceClasses.Contains("Cooking")) //false
|
||||
|
||||
//Show me all classes that are not science classes, since I hate science.
|
||||
fmt.Println(allClasses.Difference(scienceClasses)) //Set{Music, Automotive, Go Programming, Python Programming, Cooking, English, Math, Welding}
|
||||
|
||||
//Which science classes are also required classes?
|
||||
fmt.Println(scienceClasses.Intersect(requiredClasses)) //Set{Biology}
|
||||
|
||||
//How many bonus classes do you offer?
|
||||
fmt.Println(bonusClasses.Cardinality()) //2
|
||||
|
||||
//Do you have the following classes? Welding, Automotive and English?
|
||||
fmt.Println(allClasses.IsSuperset(mapset.NewSetFromSlice([]interface{}{"Welding", "Automotive", "English"}))) //true
|
||||
```
|
||||
|
||||
Thanks!
|
||||
|
||||
-Ralph
|
||||
|
||||
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/deckarep/golang-set/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
|
||||
|
||||
[![Analytics](https://ga-beacon.appspot.com/UA-42584447-2/deckarep/golang-set)](https://github.com/igrigorik/ga-beacon)
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
Open Source Initiative OSI - The MIT License (MIT):Licensing
|
||||
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2013 Ralph Caraveo (deckarep@gmail.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
package mapset
|
||||
|
||||
// Iterator defines an iterator over a Set, its C channel can be used to range over the Set's
|
||||
// elements.
|
||||
type Iterator struct {
|
||||
C <-chan interface{}
|
||||
stop chan struct{}
|
||||
}
|
||||
|
||||
// Stop stops the Iterator, no further elements will be received on C, C will be closed.
|
||||
func (i *Iterator) Stop() {
|
||||
// Allows for Stop() to be called multiple times
|
||||
// (close() panics when called on already closed channel)
|
||||
defer func() {
|
||||
recover()
|
||||
}()
|
||||
|
||||
close(i.stop)
|
||||
|
||||
// Exhaust any remaining elements.
|
||||
for range i.C {
|
||||
}
|
||||
}
|
||||
|
||||
// newIterator returns a new Iterator instance together with its item and stop channels.
|
||||
func newIterator() (*Iterator, chan<- interface{}, <-chan struct{}) {
|
||||
itemChan := make(chan interface{})
|
||||
stopChan := make(chan struct{})
|
||||
return &Iterator{
|
||||
C: itemChan,
|
||||
stop: stopChan,
|
||||
}, itemChan, stopChan
|
||||
}
|
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
Open Source Initiative OSI - The MIT License (MIT):Licensing
|
||||
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2013 Ralph Caraveo (deckarep@gmail.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
// Package mapset implements a simple and generic set collection.
|
||||
// Items stored within it are unordered and unique. It supports
|
||||
// typical set operations: membership testing, intersection, union,
|
||||
// difference, symmetric difference and cloning.
|
||||
//
|
||||
// Package mapset provides two implementations of the Set
|
||||
// interface. The default implementation is safe for concurrent
|
||||
// access, but a non-thread-safe implementation is also provided for
|
||||
// programs that can benefit from the slight speed improvement and
|
||||
// that can enforce mutual exclusion through other means.
|
||||
package mapset
|
||||
|
||||
// Set is the primary interface provided by the mapset package. It
|
||||
// represents an unordered set of data and a large number of
|
||||
// operations that can be applied to that set.
|
||||
type Set interface {
|
||||
// Adds an element to the set. Returns whether
|
||||
// the item was added.
|
||||
Add(i interface{}) bool
|
||||
|
||||
// Returns the number of elements in the set.
|
||||
Cardinality() int
|
||||
|
||||
// Removes all elements from the set, leaving
|
||||
// the empty set.
|
||||
Clear()
|
||||
|
||||
// Returns a clone of the set using the same
|
||||
// implementation, duplicating all keys.
|
||||
Clone() Set
|
||||
|
||||
// Returns whether the given items
|
||||
// are all in the set.
|
||||
Contains(i ...interface{}) bool
|
||||
|
||||
// Returns the difference between this set
|
||||
// and other. The returned set will contain
|
||||
// all elements of this set that are not also
|
||||
// elements of other.
|
||||
//
|
||||
// Note that the argument to Difference
|
||||
// must be of the same type as the receiver
|
||||
// of the method. Otherwise, Difference will
|
||||
// panic.
|
||||
Difference(other Set) Set
|
||||
|
||||
// Determines if two sets are equal to each
|
||||
// other. If they have the same cardinality
|
||||
// and contain the same elements, they are
|
||||
// considered equal. The order in which
|
||||
// the elements were added is irrelevant.
|
||||
//
|
||||
// Note that the argument to Equal must be
|
||||
// of the same type as the receiver of the
|
||||
// method. Otherwise, Equal will panic.
|
||||
Equal(other Set) bool
|
||||
|
||||
// Returns a new set containing only the elements
|
||||
// that exist only in both sets.
|
||||
//
|
||||
// Note that the argument to Intersect
|
||||
// must be of the same type as the receiver
|
||||
// of the method. Otherwise, Intersect will
|
||||
// panic.
|
||||
Intersect(other Set) Set
|
||||
|
||||
// Determines if every element in this set is in
|
||||
// the other set but the two sets are not equal.
|
||||
//
|
||||
// Note that the argument to IsProperSubset
|
||||
// must be of the same type as the receiver
|
||||
// of the method. Otherwise, IsProperSubset
|
||||
// will panic.
|
||||
IsProperSubset(other Set) bool
|
||||
|
||||
// Determines if every element in the other set
|
||||
// is in this set but the two sets are not
|
||||
// equal.
|
||||
//
|
||||
// Note that the argument to IsSuperset
|
||||
// must be of the same type as the receiver
|
||||
// of the method. Otherwise, IsSuperset will
|
||||
// panic.
|
||||
IsProperSuperset(other Set) bool
|
||||
|
||||
// Determines if every element in this set is in
|
||||
// the other set.
|
||||
//
|
||||
// Note that the argument to IsSubset
|
||||
// must be of the same type as the receiver
|
||||
// of the method. Otherwise, IsSubset will
|
||||
// panic.
|
||||
IsSubset(other Set) bool
|
||||
|
||||
// Determines if every element in the other set
|
||||
// is in this set.
|
||||
//
|
||||
// Note that the argument to IsSuperset
|
||||
// must be of the same type as the receiver
|
||||
// of the method. Otherwise, IsSuperset will
|
||||
// panic.
|
||||
IsSuperset(other Set) bool
|
||||
|
||||
// Iterates over elements and executes the passed func against each element.
|
||||
// If passed func returns true, stop iteration at the time.
|
||||
Each(func(interface{}) bool)
|
||||
|
||||
// Returns a channel of elements that you can
|
||||
// range over.
|
||||
Iter() <-chan interface{}
|
||||
|
||||
// Returns an Iterator object that you can
|
||||
// use to range over the set.
|
||||
Iterator() *Iterator
|
||||
|
||||
// Remove a single element from the set.
|
||||
Remove(i interface{})
|
||||
|
||||
// Provides a convenient string representation
|
||||
// of the current state of the set.
|
||||
String() string
|
||||
|
||||
// Returns a new set with all elements which are
|
||||
// in either this set or the other set but not in both.
|
||||
//
|
||||
// Note that the argument to SymmetricDifference
|
||||
// must be of the same type as the receiver
|
||||
// of the method. Otherwise, SymmetricDifference
|
||||
// will panic.
|
||||
SymmetricDifference(other Set) Set
|
||||
|
||||
// Returns a new set with all elements in both sets.
|
||||
//
|
||||
// Note that the argument to Union must be of the
|
||||
|
||||
// same type as the receiver of the method.
|
||||
// Otherwise, IsSuperset will panic.
|
||||
Union(other Set) Set
|
||||
|
||||
// Pop removes and returns an arbitrary item from the set.
|
||||
Pop() interface{}
|
||||
|
||||
// Returns all subsets of a given set (Power Set).
|
||||
PowerSet() Set
|
||||
|
||||
// Returns the Cartesian Product of two sets.
|
||||
CartesianProduct(other Set) Set
|
||||
|
||||
// Returns the members of the set as a slice.
|
||||
ToSlice() []interface{}
|
||||
}
|
||||
|
||||
// NewSet creates and returns a reference to an empty set. Operations
|
||||
// on the resulting set are thread-safe.
|
||||
func NewSet(s ...interface{}) Set {
|
||||
set := newThreadSafeSet()
|
||||
for _, item := range s {
|
||||
set.Add(item)
|
||||
}
|
||||
return &set
|
||||
}
|
||||
|
||||
// NewSetWith creates and returns a new set with the given elements.
|
||||
// Operations on the resulting set are thread-safe.
|
||||
func NewSetWith(elts ...interface{}) Set {
|
||||
return NewSetFromSlice(elts)
|
||||
}
|
||||
|
||||
// NewSetFromSlice creates and returns a reference to a set from an
|
||||
// existing slice. Operations on the resulting set are thread-safe.
|
||||
func NewSetFromSlice(s []interface{}) Set {
|
||||
a := NewSet(s...)
|
||||
return a
|
||||
}
|
||||
|
||||
// NewThreadUnsafeSet creates and returns a reference to an empty set.
|
||||
// Operations on the resulting set are not thread-safe.
|
||||
func NewThreadUnsafeSet() Set {
|
||||
set := newThreadUnsafeSet()
|
||||
return &set
|
||||
}
|
||||
|
||||
// NewThreadUnsafeSetFromSlice creates and returns a reference to a
|
||||
// set from an existing slice. Operations on the resulting set are
|
||||
// not thread-safe.
|
||||
func NewThreadUnsafeSetFromSlice(s []interface{}) Set {
|
||||
a := NewThreadUnsafeSet()
|
||||
for _, item := range s {
|
||||
a.Add(item)
|
||||
}
|
||||
return a
|
||||
}
|
|
@ -0,0 +1,277 @@
|
|||
/*
|
||||
Open Source Initiative OSI - The MIT License (MIT):Licensing
|
||||
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2013 Ralph Caraveo (deckarep@gmail.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
package mapset
|
||||
|
||||
import "sync"
|
||||
|
||||
type threadSafeSet struct {
|
||||
s threadUnsafeSet
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
func newThreadSafeSet() threadSafeSet {
|
||||
return threadSafeSet{s: newThreadUnsafeSet()}
|
||||
}
|
||||
|
||||
func (set *threadSafeSet) Add(i interface{}) bool {
|
||||
set.Lock()
|
||||
ret := set.s.Add(i)
|
||||
set.Unlock()
|
||||
return ret
|
||||
}
|
||||
|
||||
func (set *threadSafeSet) Contains(i ...interface{}) bool {
|
||||
set.RLock()
|
||||
ret := set.s.Contains(i...)
|
||||
set.RUnlock()
|
||||
return ret
|
||||
}
|
||||
|
||||
func (set *threadSafeSet) IsSubset(other Set) bool {
|
||||
o := other.(*threadSafeSet)
|
||||
|
||||
set.RLock()
|
||||
o.RLock()
|
||||
|
||||
ret := set.s.IsSubset(&o.s)
|
||||
set.RUnlock()
|
||||
o.RUnlock()
|
||||
return ret
|
||||
}
|
||||
|
||||
func (set *threadSafeSet) IsProperSubset(other Set) bool {
|
||||
o := other.(*threadSafeSet)
|
||||
|
||||
set.RLock()
|
||||
defer set.RUnlock()
|
||||
o.RLock()
|
||||
defer o.RUnlock()
|
||||
|
||||
return set.s.IsProperSubset(&o.s)
|
||||
}
|
||||
|
||||
func (set *threadSafeSet) IsSuperset(other Set) bool {
|
||||
return other.IsSubset(set)
|
||||
}
|
||||
|
||||
func (set *threadSafeSet) IsProperSuperset(other Set) bool {
|
||||
return other.IsProperSubset(set)
|
||||
}
|
||||
|
||||
func (set *threadSafeSet) Union(other Set) Set {
|
||||
o := other.(*threadSafeSet)
|
||||
|
||||
set.RLock()
|
||||
o.RLock()
|
||||
|
||||
unsafeUnion := set.s.Union(&o.s).(*threadUnsafeSet)
|
||||
ret := &threadSafeSet{s: *unsafeUnion}
|
||||
set.RUnlock()
|
||||
o.RUnlock()
|
||||
return ret
|
||||
}
|
||||
|
||||
func (set *threadSafeSet) Intersect(other Set) Set {
|
||||
o := other.(*threadSafeSet)
|
||||
|
||||
set.RLock()
|
||||
o.RLock()
|
||||
|
||||
unsafeIntersection := set.s.Intersect(&o.s).(*threadUnsafeSet)
|
||||
ret := &threadSafeSet{s: *unsafeIntersection}
|
||||
set.RUnlock()
|
||||
o.RUnlock()
|
||||
return ret
|
||||
}
|
||||
|
||||
func (set *threadSafeSet) Difference(other Set) Set {
|
||||
o := other.(*threadSafeSet)
|
||||
|
||||
set.RLock()
|
||||
o.RLock()
|
||||
|
||||
unsafeDifference := set.s.Difference(&o.s).(*threadUnsafeSet)
|
||||
ret := &threadSafeSet{s: *unsafeDifference}
|
||||
set.RUnlock()
|
||||
o.RUnlock()
|
||||
return ret
|
||||
}
|
||||
|
||||
func (set *threadSafeSet) SymmetricDifference(other Set) Set {
|
||||
o := other.(*threadSafeSet)
|
||||
|
||||
set.RLock()
|
||||
o.RLock()
|
||||
|
||||
unsafeDifference := set.s.SymmetricDifference(&o.s).(*threadUnsafeSet)
|
||||
ret := &threadSafeSet{s: *unsafeDifference}
|
||||
set.RUnlock()
|
||||
o.RUnlock()
|
||||
return ret
|
||||
}
|
||||
|
||||
func (set *threadSafeSet) Clear() {
|
||||
set.Lock()
|
||||
set.s = newThreadUnsafeSet()
|
||||
set.Unlock()
|
||||
}
|
||||
|
||||
func (set *threadSafeSet) Remove(i interface{}) {
|
||||
set.Lock()
|
||||
delete(set.s, i)
|
||||
set.Unlock()
|
||||
}
|
||||
|
||||
func (set *threadSafeSet) Cardinality() int {
|
||||
set.RLock()
|
||||
defer set.RUnlock()
|
||||
return len(set.s)
|
||||
}
|
||||
|
||||
func (set *threadSafeSet) Each(cb func(interface{}) bool) {
|
||||
set.RLock()
|
||||
for elem := range set.s {
|
||||
if cb(elem) {
|
||||
break
|
||||
}
|
||||
}
|
||||
set.RUnlock()
|
||||
}
|
||||
|
||||
func (set *threadSafeSet) Iter() <-chan interface{} {
|
||||
ch := make(chan interface{})
|
||||
go func() {
|
||||
set.RLock()
|
||||
|
||||
for elem := range set.s {
|
||||
ch <- elem
|
||||
}
|
||||
close(ch)
|
||||
set.RUnlock()
|
||||
}()
|
||||
|
||||
return ch
|
||||
}
|
||||
|
||||
func (set *threadSafeSet) Iterator() *Iterator {
|
||||
iterator, ch, stopCh := newIterator()
|
||||
|
||||
go func() {
|
||||
set.RLock()
|
||||
L:
|
||||
for elem := range set.s {
|
||||
select {
|
||||
case <-stopCh:
|
||||
break L
|
||||
case ch <- elem:
|
||||
}
|
||||
}
|
||||
close(ch)
|
||||
set.RUnlock()
|
||||
}()
|
||||
|
||||
return iterator
|
||||
}
|
||||
|
||||
func (set *threadSafeSet) Equal(other Set) bool {
|
||||
o := other.(*threadSafeSet)
|
||||
|
||||
set.RLock()
|
||||
o.RLock()
|
||||
|
||||
ret := set.s.Equal(&o.s)
|
||||
set.RUnlock()
|
||||
o.RUnlock()
|
||||
return ret
|
||||
}
|
||||
|
||||
func (set *threadSafeSet) Clone() Set {
|
||||
set.RLock()
|
||||
|
||||
unsafeClone := set.s.Clone().(*threadUnsafeSet)
|
||||
ret := &threadSafeSet{s: *unsafeClone}
|
||||
set.RUnlock()
|
||||
return ret
|
||||
}
|
||||
|
||||
func (set *threadSafeSet) String() string {
|
||||
set.RLock()
|
||||
ret := set.s.String()
|
||||
set.RUnlock()
|
||||
return ret
|
||||
}
|
||||
|
||||
func (set *threadSafeSet) PowerSet() Set {
|
||||
set.RLock()
|
||||
ret := set.s.PowerSet()
|
||||
set.RUnlock()
|
||||
return ret
|
||||
}
|
||||
|
||||
func (set *threadSafeSet) Pop() interface{} {
|
||||
set.Lock()
|
||||
defer set.Unlock()
|
||||
return set.s.Pop()
|
||||
}
|
||||
|
||||
func (set *threadSafeSet) CartesianProduct(other Set) Set {
|
||||
o := other.(*threadSafeSet)
|
||||
|
||||
set.RLock()
|
||||
o.RLock()
|
||||
|
||||
unsafeCartProduct := set.s.CartesianProduct(&o.s).(*threadUnsafeSet)
|
||||
ret := &threadSafeSet{s: *unsafeCartProduct}
|
||||
set.RUnlock()
|
||||
o.RUnlock()
|
||||
return ret
|
||||
}
|
||||
|
||||
func (set *threadSafeSet) ToSlice() []interface{} {
|
||||
keys := make([]interface{}, 0, set.Cardinality())
|
||||
set.RLock()
|
||||
for elem := range set.s {
|
||||
keys = append(keys, elem)
|
||||
}
|
||||
set.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
func (set *threadSafeSet) MarshalJSON() ([]byte, error) {
|
||||
set.RLock()
|
||||
b, err := set.s.MarshalJSON()
|
||||
set.RUnlock()
|
||||
|
||||
return b, err
|
||||
}
|
||||
|
||||
func (set *threadSafeSet) UnmarshalJSON(p []byte) error {
|
||||
set.RLock()
|
||||
err := set.s.UnmarshalJSON(p)
|
||||
set.RUnlock()
|
||||
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,337 @@
|
|||
/*
|
||||
Open Source Initiative OSI - The MIT License (MIT):Licensing
|
||||
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2013 Ralph Caraveo (deckarep@gmail.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
package mapset
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type threadUnsafeSet map[interface{}]struct{}
|
||||
|
||||
// An OrderedPair represents a 2-tuple of values.
|
||||
type OrderedPair struct {
|
||||
First interface{}
|
||||
Second interface{}
|
||||
}
|
||||
|
||||
func newThreadUnsafeSet() threadUnsafeSet {
|
||||
return make(threadUnsafeSet)
|
||||
}
|
||||
|
||||
// Equal says whether two 2-tuples contain the same values in the same order.
|
||||
func (pair *OrderedPair) Equal(other OrderedPair) bool {
|
||||
if pair.First == other.First &&
|
||||
pair.Second == other.Second {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (set *threadUnsafeSet) Add(i interface{}) bool {
|
||||
_, found := (*set)[i]
|
||||
if found {
|
||||
return false //False if it existed already
|
||||
}
|
||||
|
||||
(*set)[i] = struct{}{}
|
||||
return true
|
||||
}
|
||||
|
||||
func (set *threadUnsafeSet) Contains(i ...interface{}) bool {
|
||||
for _, val := range i {
|
||||
if _, ok := (*set)[val]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (set *threadUnsafeSet) IsSubset(other Set) bool {
|
||||
_ = other.(*threadUnsafeSet)
|
||||
for elem := range *set {
|
||||
if !other.Contains(elem) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (set *threadUnsafeSet) IsProperSubset(other Set) bool {
|
||||
return set.IsSubset(other) && !set.Equal(other)
|
||||
}
|
||||
|
||||
func (set *threadUnsafeSet) IsSuperset(other Set) bool {
|
||||
return other.IsSubset(set)
|
||||
}
|
||||
|
||||
func (set *threadUnsafeSet) IsProperSuperset(other Set) bool {
|
||||
return set.IsSuperset(other) && !set.Equal(other)
|
||||
}
|
||||
|
||||
func (set *threadUnsafeSet) Union(other Set) Set {
|
||||
o := other.(*threadUnsafeSet)
|
||||
|
||||
unionedSet := newThreadUnsafeSet()
|
||||
|
||||
for elem := range *set {
|
||||
unionedSet.Add(elem)
|
||||
}
|
||||
for elem := range *o {
|
||||
unionedSet.Add(elem)
|
||||
}
|
||||
return &unionedSet
|
||||
}
|
||||
|
||||
func (set *threadUnsafeSet) Intersect(other Set) Set {
|
||||
o := other.(*threadUnsafeSet)
|
||||
|
||||
intersection := newThreadUnsafeSet()
|
||||
// loop over smaller set
|
||||
if set.Cardinality() < other.Cardinality() {
|
||||
for elem := range *set {
|
||||
if other.Contains(elem) {
|
||||
intersection.Add(elem)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for elem := range *o {
|
||||
if set.Contains(elem) {
|
||||
intersection.Add(elem)
|
||||
}
|
||||
}
|
||||
}
|
||||
return &intersection
|
||||
}
|
||||
|
||||
func (set *threadUnsafeSet) Difference(other Set) Set {
|
||||
_ = other.(*threadUnsafeSet)
|
||||
|
||||
difference := newThreadUnsafeSet()
|
||||
for elem := range *set {
|
||||
if !other.Contains(elem) {
|
||||
difference.Add(elem)
|
||||
}
|
||||
}
|
||||
return &difference
|
||||
}
|
||||
|
||||
func (set *threadUnsafeSet) SymmetricDifference(other Set) Set {
|
||||
_ = other.(*threadUnsafeSet)
|
||||
|
||||
aDiff := set.Difference(other)
|
||||
bDiff := other.Difference(set)
|
||||
return aDiff.Union(bDiff)
|
||||
}
|
||||
|
||||
func (set *threadUnsafeSet) Clear() {
|
||||
*set = newThreadUnsafeSet()
|
||||
}
|
||||
|
||||
func (set *threadUnsafeSet) Remove(i interface{}) {
|
||||
delete(*set, i)
|
||||
}
|
||||
|
||||
func (set *threadUnsafeSet) Cardinality() int {
|
||||
return len(*set)
|
||||
}
|
||||
|
||||
func (set *threadUnsafeSet) Each(cb func(interface{}) bool) {
|
||||
for elem := range *set {
|
||||
if cb(elem) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (set *threadUnsafeSet) Iter() <-chan interface{} {
|
||||
ch := make(chan interface{})
|
||||
go func() {
|
||||
for elem := range *set {
|
||||
ch <- elem
|
||||
}
|
||||
close(ch)
|
||||
}()
|
||||
|
||||
return ch
|
||||
}
|
||||
|
||||
func (set *threadUnsafeSet) Iterator() *Iterator {
|
||||
iterator, ch, stopCh := newIterator()
|
||||
|
||||
go func() {
|
||||
L:
|
||||
for elem := range *set {
|
||||
select {
|
||||
case <-stopCh:
|
||||
break L
|
||||
case ch <- elem:
|
||||
}
|
||||
}
|
||||
close(ch)
|
||||
}()
|
||||
|
||||
return iterator
|
||||
}
|
||||
|
||||
func (set *threadUnsafeSet) Equal(other Set) bool {
|
||||
_ = other.(*threadUnsafeSet)
|
||||
|
||||
if set.Cardinality() != other.Cardinality() {
|
||||
return false
|
||||
}
|
||||
for elem := range *set {
|
||||
if !other.Contains(elem) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (set *threadUnsafeSet) Clone() Set {
|
||||
clonedSet := newThreadUnsafeSet()
|
||||
for elem := range *set {
|
||||
clonedSet.Add(elem)
|
||||
}
|
||||
return &clonedSet
|
||||
}
|
||||
|
||||
func (set *threadUnsafeSet) String() string {
|
||||
items := make([]string, 0, len(*set))
|
||||
|
||||
for elem := range *set {
|
||||
items = append(items, fmt.Sprintf("%v", elem))
|
||||
}
|
||||
return fmt.Sprintf("Set{%s}", strings.Join(items, ", "))
|
||||
}
|
||||
|
||||
// String outputs a 2-tuple in the form "(A, B)".
|
||||
func (pair OrderedPair) String() string {
|
||||
return fmt.Sprintf("(%v, %v)", pair.First, pair.Second)
|
||||
}
|
||||
|
||||
func (set *threadUnsafeSet) Pop() interface{} {
|
||||
for item := range *set {
|
||||
delete(*set, item)
|
||||
return item
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (set *threadUnsafeSet) PowerSet() Set {
|
||||
powSet := NewThreadUnsafeSet()
|
||||
nullset := newThreadUnsafeSet()
|
||||
powSet.Add(&nullset)
|
||||
|
||||
for es := range *set {
|
||||
u := newThreadUnsafeSet()
|
||||
j := powSet.Iter()
|
||||
for er := range j {
|
||||
p := newThreadUnsafeSet()
|
||||
if reflect.TypeOf(er).Name() == "" {
|
||||
k := er.(*threadUnsafeSet)
|
||||
for ek := range *(k) {
|
||||
p.Add(ek)
|
||||
}
|
||||
} else {
|
||||
p.Add(er)
|
||||
}
|
||||
p.Add(es)
|
||||
u.Add(&p)
|
||||
}
|
||||
|
||||
powSet = powSet.Union(&u)
|
||||
}
|
||||
|
||||
return powSet
|
||||
}
|
||||
|
||||
func (set *threadUnsafeSet) CartesianProduct(other Set) Set {
|
||||
o := other.(*threadUnsafeSet)
|
||||
cartProduct := NewThreadUnsafeSet()
|
||||
|
||||
for i := range *set {
|
||||
for j := range *o {
|
||||
elem := OrderedPair{First: i, Second: j}
|
||||
cartProduct.Add(elem)
|
||||
}
|
||||
}
|
||||
|
||||
return cartProduct
|
||||
}
|
||||
|
||||
func (set *threadUnsafeSet) ToSlice() []interface{} {
|
||||
keys := make([]interface{}, 0, set.Cardinality())
|
||||
for elem := range *set {
|
||||
keys = append(keys, elem)
|
||||
}
|
||||
|
||||
return keys
|
||||
}
|
||||
|
||||
// MarshalJSON creates a JSON array from the set, it marshals all elements
|
||||
func (set *threadUnsafeSet) MarshalJSON() ([]byte, error) {
|
||||
items := make([]string, 0, set.Cardinality())
|
||||
|
||||
for elem := range *set {
|
||||
b, err := json.Marshal(elem)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
items = append(items, string(b))
|
||||
}
|
||||
|
||||
return []byte(fmt.Sprintf("[%s]", strings.Join(items, ","))), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON recreates a set from a JSON array, it only decodes
|
||||
// primitive types. Numbers are decoded as json.Number.
|
||||
func (set *threadUnsafeSet) UnmarshalJSON(b []byte) error {
|
||||
var i []interface{}
|
||||
|
||||
d := json.NewDecoder(bytes.NewReader(b))
|
||||
d.UseNumber()
|
||||
err := d.Decode(&i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, v := range i {
|
||||
switch t := v.(type) {
|
||||
case []interface{}, map[string]interface{}:
|
||||
continue
|
||||
default:
|
||||
set.Add(t)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,245 +0,0 @@
|
|||
# Set [![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](https://godoc.org/gopkg.in/fatih/set.v0) [![Build Status](http://img.shields.io/travis/fatih/set.svg?style=flat-square)](https://travis-ci.org/fatih/set)
|
||||
|
||||
Set is a basic and simple, hash-based, **Set** data structure implementation
|
||||
in Go (Golang).
|
||||
|
||||
Set provides both threadsafe and non-threadsafe implementations of a generic
|
||||
set data structure. The thread safety encompasses all operations on one set.
|
||||
Operations on multiple sets are consistent in that the elements of each set
|
||||
used was valid at exactly one point in time between the start and the end of
|
||||
the operation. Because it's thread safe, you can use it concurrently with your
|
||||
goroutines.
|
||||
|
||||
For usage see examples below or click on the godoc badge.
|
||||
|
||||
## Install and Usage
|
||||
|
||||
Install the package with:
|
||||
|
||||
```bash
|
||||
go get gopkg.in/fatih/set.v0
|
||||
```
|
||||
|
||||
Import it with:
|
||||
|
||||
```go
|
||||
import "gopkg.in/fatih/set.v0"
|
||||
```
|
||||
|
||||
and use `set` as the package name inside the code.
|
||||
|
||||
## Examples
|
||||
|
||||
#### Initialization of a new Set
|
||||
|
||||
```go
|
||||
|
||||
// create a set with zero items
|
||||
s := set.New()
|
||||
s := set.NewNonTS() // non thread-safe version
|
||||
|
||||
// ... or with some initial values
|
||||
s := set.New("istanbul", "frankfurt", 30.123, "san francisco", 1234)
|
||||
s := set.NewNonTS("kenya", "ethiopia", "sumatra")
|
||||
|
||||
```
|
||||
|
||||
#### Basic Operations
|
||||
|
||||
```go
|
||||
// add items
|
||||
s.Add("istanbul")
|
||||
s.Add("istanbul") // nothing happens if you add duplicate item
|
||||
|
||||
// add multiple items
|
||||
s.Add("ankara", "san francisco", 3.14)
|
||||
|
||||
// remove item
|
||||
s.Remove("frankfurt")
|
||||
s.Remove("frankfurt") // nothing happes if you remove a nonexisting item
|
||||
|
||||
// remove multiple items
|
||||
s.Remove("barcelona", 3.14, "ankara")
|
||||
|
||||
// removes an arbitary item and return it
|
||||
item := s.Pop()
|
||||
|
||||
// create a new copy
|
||||
other := s.Copy()
|
||||
|
||||
// remove all items
|
||||
s.Clear()
|
||||
|
||||
// number of items in the set
|
||||
len := s.Size()
|
||||
|
||||
// return a list of items
|
||||
items := s.List()
|
||||
|
||||
// string representation of set
|
||||
fmt.Printf("set is %s", s.String())
|
||||
|
||||
```
|
||||
|
||||
#### Check Operations
|
||||
|
||||
```go
|
||||
// check for set emptiness, returns true if set is empty
|
||||
s.IsEmpty()
|
||||
|
||||
// check for a single item exist
|
||||
s.Has("istanbul")
|
||||
|
||||
// ... or for multiple items. This will return true if all of the items exist.
|
||||
s.Has("istanbul", "san francisco", 3.14)
|
||||
|
||||
// create two sets for the following checks...
|
||||
s := s.New("1", "2", "3", "4", "5")
|
||||
t := s.New("1", "2", "3")
|
||||
|
||||
|
||||
// check if they are the same
|
||||
if !s.IsEqual(t) {
|
||||
fmt.Println("s is not equal to t")
|
||||
}
|
||||
|
||||
// if s contains all elements of t
|
||||
if s.IsSubset(t) {
|
||||
fmt.Println("t is a subset of s")
|
||||
}
|
||||
|
||||
// ... or if s is a superset of t
|
||||
if t.IsSuperset(s) {
|
||||
fmt.Println("s is a superset of t")
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
#### Set Operations
|
||||
|
||||
|
||||
```go
|
||||
// let us initialize two sets with some values
|
||||
a := set.New("ankara", "berlin", "san francisco")
|
||||
b := set.New("frankfurt", "berlin")
|
||||
|
||||
// creates a new set with the items in a and b combined.
|
||||
// [frankfurt, berlin, ankara, san francisco]
|
||||
c := set.Union(a, b)
|
||||
|
||||
// contains items which is in both a and b
|
||||
// [berlin]
|
||||
c := set.Intersection(a, b)
|
||||
|
||||
// contains items which are in a but not in b
|
||||
// [ankara, san francisco]
|
||||
c := set.Difference(a, b)
|
||||
|
||||
// contains items which are in one of either, but not in both.
|
||||
// [frankfurt, ankara, san francisco]
|
||||
c := set.SymmetricDifference(a, b)
|
||||
|
||||
```
|
||||
|
||||
```go
|
||||
// like Union but saves the result back into a.
|
||||
a.Merge(b)
|
||||
|
||||
// removes the set items which are in b from a and saves the result back into a.
|
||||
a.Separate(b)
|
||||
|
||||
```
|
||||
|
||||
#### Multiple Set Operations
|
||||
|
||||
```go
|
||||
a := set.New("1", "3", "4", "5")
|
||||
b := set.New("2", "3", "4", "5")
|
||||
c := set.New("4", "5", "6", "7")
|
||||
|
||||
// creates a new set with items in a, b and c
|
||||
// [1 2 3 4 5 6 7]
|
||||
u := set.Union(a, b, c)
|
||||
|
||||
// creates a new set with items in a but not in b and c
|
||||
// [1]
|
||||
u := set.Difference(a, b, c)
|
||||
|
||||
// creates a new set with items that are common to a, b and c
|
||||
// [5]
|
||||
u := set.Intersection(a, b, c)
|
||||
```
|
||||
|
||||
#### Helper methods
|
||||
|
||||
The Slice functions below are a convenient way to extract or convert your Set data
|
||||
into basic data types.
|
||||
|
||||
|
||||
```go
|
||||
// create a set of mixed types
|
||||
s := set.New("ankara", "5", "8", "san francisco", 13, 21)
|
||||
|
||||
|
||||
// convert s into a slice of strings (type is []string)
|
||||
// [ankara 5 8 san francisco]
|
||||
t := set.StringSlice(s)
|
||||
|
||||
|
||||
// u contains a slice of ints (type is []int)
|
||||
// [13, 21]
|
||||
u := set.IntSlice(s)
|
||||
|
||||
```
|
||||
|
||||
#### Concurrent safe usage
|
||||
|
||||
Below is an example of a concurrent way that uses set. We call ten functions
|
||||
concurrently and wait until they are finished. It basically creates a new
|
||||
string for each goroutine and adds it to our set.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/fatih/set"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var wg sync.WaitGroup // this is just for waiting until all goroutines finish
|
||||
|
||||
// Initialize our thread safe Set
|
||||
s := set.New()
|
||||
|
||||
// Add items concurrently (item1, item2, and so on)
|
||||
for i := 0; i < 10; i++ {
|
||||
wg.Add(1)
|
||||
go func(i int) {
|
||||
item := "item" + strconv.Itoa(i)
|
||||
fmt.Println("adding", item)
|
||||
s.Add(item)
|
||||
wg.Done()
|
||||
}(i)
|
||||
}
|
||||
|
||||
// Wait until all concurrent calls finished and print our set
|
||||
wg.Wait()
|
||||
fmt.Println(s)
|
||||
}
|
||||
```
|
||||
|
||||
## Credits
|
||||
|
||||
* [Fatih Arslan](https://github.com/fatih)
|
||||
* [Arne Hormann](https://github.com/arnehormann)
|
||||
* [Sam Boyer](https://github.com/sdboyer)
|
||||
* [Ralph Loizzo](https://github.com/friartech)
|
||||
|
||||
## License
|
||||
|
||||
The MIT License (MIT) - see LICENSE.md for more details
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
// Package set provides both threadsafe and non-threadsafe implementations of
|
||||
// a generic set data structure. In the threadsafe set, safety encompasses all
|
||||
// operations on one set. Operations on multiple sets are consistent in that
|
||||
// the elements of each set used was valid at exactly one point in time
|
||||
// between the start and the end of the operation.
|
||||
package set
|
||||
|
||||
// Interface is describing a Set. Sets are an unordered, unique list of values.
|
||||
type Interface interface {
|
||||
New(items ...interface{}) Interface
|
||||
Add(items ...interface{})
|
||||
Remove(items ...interface{})
|
||||
Pop() interface{}
|
||||
Has(items ...interface{}) bool
|
||||
Size() int
|
||||
Clear()
|
||||
IsEmpty() bool
|
||||
IsEqual(s Interface) bool
|
||||
IsSubset(s Interface) bool
|
||||
IsSuperset(s Interface) bool
|
||||
Each(func(interface{}) bool)
|
||||
String() string
|
||||
List() []interface{}
|
||||
Copy() Interface
|
||||
Merge(s Interface)
|
||||
Separate(s Interface)
|
||||
}
|
||||
|
||||
// helpful to not write everywhere struct{}{}
|
||||
var keyExists = struct{}{}
|
||||
|
||||
// Union is the merger of multiple sets. It returns a new set with all the
|
||||
// elements present in all the sets that are passed.
|
||||
//
|
||||
// The dynamic type of the returned set is determined by the first passed set's
|
||||
// implementation of the New() method.
|
||||
func Union(set1, set2 Interface, sets ...Interface) Interface {
|
||||
u := set1.Copy()
|
||||
set2.Each(func(item interface{}) bool {
|
||||
u.Add(item)
|
||||
return true
|
||||
})
|
||||
for _, set := range sets {
|
||||
set.Each(func(item interface{}) bool {
|
||||
u.Add(item)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
return u
|
||||
}
|
||||
|
||||
// Difference returns a new set which contains items which are in in the first
|
||||
// set but not in the others. Unlike the Difference() method you can use this
|
||||
// function separately with multiple sets.
|
||||
func Difference(set1, set2 Interface, sets ...Interface) Interface {
|
||||
s := set1.Copy()
|
||||
s.Separate(set2)
|
||||
for _, set := range sets {
|
||||
s.Separate(set) // seperate is thread safe
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Intersection returns a new set which contains items that only exist in all given sets.
|
||||
func Intersection(set1, set2 Interface, sets ...Interface) Interface {
|
||||
all := Union(set1, set2, sets...)
|
||||
result := Union(set1, set2, sets...)
|
||||
|
||||
all.Each(func(item interface{}) bool {
|
||||
if !set1.Has(item) || !set2.Has(item) {
|
||||
result.Remove(item)
|
||||
}
|
||||
|
||||
for _, set := range sets {
|
||||
if !set.Has(item) {
|
||||
result.Remove(item)
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
// SymmetricDifference returns a new set which s is the difference of items which are in
|
||||
// one of either, but not in both.
|
||||
func SymmetricDifference(s Interface, t Interface) Interface {
|
||||
u := Difference(s, t)
|
||||
v := Difference(t, s)
|
||||
return Union(u, v)
|
||||
}
|
||||
|
||||
// StringSlice is a helper function that returns a slice of strings of s. If
|
||||
// the set contains mixed types of items only items of type string are returned.
|
||||
func StringSlice(s Interface) []string {
|
||||
slice := make([]string, 0)
|
||||
for _, item := range s.List() {
|
||||
v, ok := item.(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
slice = append(slice, v)
|
||||
}
|
||||
return slice
|
||||
}
|
||||
|
||||
// IntSlice is a helper function that returns a slice of ints of s. If
|
||||
// the set contains mixed types of items only items of type int are returned.
|
||||
func IntSlice(s Interface) []int {
|
||||
slice := make([]int, 0)
|
||||
for _, item := range s.List() {
|
||||
v, ok := item.(int)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
slice = append(slice, v)
|
||||
}
|
||||
return slice
|
||||
}
|
|
@ -1,195 +0,0 @@
|
|||
package set
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Provides a common set baseline for both threadsafe and non-ts Sets.
|
||||
type set struct {
|
||||
m map[interface{}]struct{} // struct{} doesn't take up space
|
||||
}
|
||||
|
||||
// SetNonTS defines a non-thread safe set data structure.
|
||||
type SetNonTS struct {
|
||||
set
|
||||
}
|
||||
|
||||
// NewNonTS creates and initialize a new non-threadsafe Set.
|
||||
// It accepts a variable number of arguments to populate the initial set.
|
||||
// If nothing is passed a SetNonTS with zero size is created.
|
||||
func NewNonTS(items ...interface{}) *SetNonTS {
|
||||
s := &SetNonTS{}
|
||||
s.m = make(map[interface{}]struct{})
|
||||
|
||||
// Ensure interface compliance
|
||||
var _ Interface = s
|
||||
|
||||
s.Add(items...)
|
||||
return s
|
||||
}
|
||||
|
||||
// New creates and initalizes a new Set interface. It accepts a variable
|
||||
// number of arguments to populate the initial set. If nothing is passed a
|
||||
// zero size Set based on the struct is created.
|
||||
func (s *set) New(items ...interface{}) Interface {
|
||||
return NewNonTS(items...)
|
||||
}
|
||||
|
||||
// Add includes the specified items (one or more) to the set. The underlying
|
||||
// Set s is modified. If passed nothing it silently returns.
|
||||
func (s *set) Add(items ...interface{}) {
|
||||
if len(items) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
s.m[item] = keyExists
|
||||
}
|
||||
}
|
||||
|
||||
// Remove deletes the specified items from the set. The underlying Set s is
|
||||
// modified. If passed nothing it silently returns.
|
||||
func (s *set) Remove(items ...interface{}) {
|
||||
if len(items) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
delete(s.m, item)
|
||||
}
|
||||
}
|
||||
|
||||
// Pop deletes and return an item from the set. The underlying Set s is
|
||||
// modified. If set is empty, nil is returned.
|
||||
func (s *set) Pop() interface{} {
|
||||
for item := range s.m {
|
||||
delete(s.m, item)
|
||||
return item
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Has looks for the existence of items passed. It returns false if nothing is
|
||||
// passed. For multiple items it returns true only if all of the items exist.
|
||||
func (s *set) Has(items ...interface{}) bool {
|
||||
// assume checked for empty item, which not exist
|
||||
if len(items) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
has := true
|
||||
for _, item := range items {
|
||||
if _, has = s.m[item]; !has {
|
||||
break
|
||||
}
|
||||
}
|
||||
return has
|
||||
}
|
||||
|
||||
// Size returns the number of items in a set.
|
||||
func (s *set) Size() int {
|
||||
return len(s.m)
|
||||
}
|
||||
|
||||
// Clear removes all items from the set.
|
||||
func (s *set) Clear() {
|
||||
s.m = make(map[interface{}]struct{})
|
||||
}
|
||||
|
||||
// IsEmpty reports whether the Set is empty.
|
||||
func (s *set) IsEmpty() bool {
|
||||
return s.Size() == 0
|
||||
}
|
||||
|
||||
// IsEqual test whether s and t are the same in size and have the same items.
|
||||
func (s *set) IsEqual(t Interface) bool {
|
||||
// Force locking only if given set is threadsafe.
|
||||
if conv, ok := t.(*Set); ok {
|
||||
conv.l.RLock()
|
||||
defer conv.l.RUnlock()
|
||||
}
|
||||
|
||||
// return false if they are no the same size
|
||||
if sameSize := len(s.m) == t.Size(); !sameSize {
|
||||
return false
|
||||
}
|
||||
|
||||
equal := true
|
||||
t.Each(func(item interface{}) bool {
|
||||
_, equal = s.m[item]
|
||||
return equal // if false, Each() will end
|
||||
})
|
||||
|
||||
return equal
|
||||
}
|
||||
|
||||
// IsSubset tests whether t is a subset of s.
|
||||
func (s *set) IsSubset(t Interface) (subset bool) {
|
||||
subset = true
|
||||
|
||||
t.Each(func(item interface{}) bool {
|
||||
_, subset = s.m[item]
|
||||
return subset
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// IsSuperset tests whether t is a superset of s.
|
||||
func (s *set) IsSuperset(t Interface) bool {
|
||||
return t.IsSubset(s)
|
||||
}
|
||||
|
||||
// Each traverses the items in the Set, calling the provided function for each
|
||||
// set member. Traversal will continue until all items in the Set have been
|
||||
// visited, or if the closure returns false.
|
||||
func (s *set) Each(f func(item interface{}) bool) {
|
||||
for item := range s.m {
|
||||
if !f(item) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a string representation of s
|
||||
func (s *set) String() string {
|
||||
t := make([]string, 0, len(s.List()))
|
||||
for _, item := range s.List() {
|
||||
t = append(t, fmt.Sprintf("%v", item))
|
||||
}
|
||||
|
||||
return fmt.Sprintf("[%s]", strings.Join(t, ", "))
|
||||
}
|
||||
|
||||
// List returns a slice of all items. There is also StringSlice() and
|
||||
// IntSlice() methods for returning slices of type string or int.
|
||||
func (s *set) List() []interface{} {
|
||||
list := make([]interface{}, 0, len(s.m))
|
||||
|
||||
for item := range s.m {
|
||||
list = append(list, item)
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
// Copy returns a new Set with a copy of s.
|
||||
func (s *set) Copy() Interface {
|
||||
return NewNonTS(s.List()...)
|
||||
}
|
||||
|
||||
// Merge is like Union, however it modifies the current set it's applied on
|
||||
// with the given t set.
|
||||
func (s *set) Merge(t Interface) {
|
||||
t.Each(func(item interface{}) bool {
|
||||
s.m[item] = keyExists
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// it's not the opposite of Merge.
|
||||
// Separate removes the set items containing in t from set s. Please aware that
|
||||
func (s *set) Separate(t Interface) {
|
||||
s.Remove(t.List()...)
|
||||
}
|
|
@ -1,200 +0,0 @@
|
|||
package set
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Set defines a thread safe set data structure.
|
||||
type Set struct {
|
||||
set
|
||||
l sync.RWMutex // we name it because we don't want to expose it
|
||||
}
|
||||
|
||||
// New creates and initialize a new Set. It's accept a variable number of
|
||||
// arguments to populate the initial set. If nothing passed a Set with zero
|
||||
// size is created.
|
||||
func New(items ...interface{}) *Set {
|
||||
s := &Set{}
|
||||
s.m = make(map[interface{}]struct{})
|
||||
|
||||
// Ensure interface compliance
|
||||
var _ Interface = s
|
||||
|
||||
s.Add(items...)
|
||||
return s
|
||||
}
|
||||
|
||||
// New creates and initalizes a new Set interface. It accepts a variable
|
||||
// number of arguments to populate the initial set. If nothing is passed a
|
||||
// zero size Set based on the struct is created.
|
||||
func (s *Set) New(items ...interface{}) Interface {
|
||||
return New(items...)
|
||||
}
|
||||
|
||||
// Add includes the specified items (one or more) to the set. The underlying
|
||||
// Set s is modified. If passed nothing it silently returns.
|
||||
func (s *Set) Add(items ...interface{}) {
|
||||
if len(items) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
s.l.Lock()
|
||||
defer s.l.Unlock()
|
||||
|
||||
for _, item := range items {
|
||||
s.m[item] = keyExists
|
||||
}
|
||||
}
|
||||
|
||||
// Remove deletes the specified items from the set. The underlying Set s is
|
||||
// modified. If passed nothing it silently returns.
|
||||
func (s *Set) Remove(items ...interface{}) {
|
||||
if len(items) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
s.l.Lock()
|
||||
defer s.l.Unlock()
|
||||
|
||||
for _, item := range items {
|
||||
delete(s.m, item)
|
||||
}
|
||||
}
|
||||
|
||||
// Pop deletes and return an item from the set. The underlying Set s is
|
||||
// modified. If set is empty, nil is returned.
|
||||
func (s *Set) Pop() interface{} {
|
||||
s.l.RLock()
|
||||
for item := range s.m {
|
||||
s.l.RUnlock()
|
||||
s.l.Lock()
|
||||
delete(s.m, item)
|
||||
s.l.Unlock()
|
||||
return item
|
||||
}
|
||||
s.l.RUnlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Has looks for the existence of items passed. It returns false if nothing is
|
||||
// passed. For multiple items it returns true only if all of the items exist.
|
||||
func (s *Set) Has(items ...interface{}) bool {
|
||||
// assume checked for empty item, which not exist
|
||||
if len(items) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
s.l.RLock()
|
||||
defer s.l.RUnlock()
|
||||
|
||||
has := true
|
||||
for _, item := range items {
|
||||
if _, has = s.m[item]; !has {
|
||||
break
|
||||
}
|
||||
}
|
||||
return has
|
||||
}
|
||||
|
||||
// Size returns the number of items in a set.
|
||||
func (s *Set) Size() int {
|
||||
s.l.RLock()
|
||||
defer s.l.RUnlock()
|
||||
|
||||
l := len(s.m)
|
||||
return l
|
||||
}
|
||||
|
||||
// Clear removes all items from the set.
|
||||
func (s *Set) Clear() {
|
||||
s.l.Lock()
|
||||
defer s.l.Unlock()
|
||||
|
||||
s.m = make(map[interface{}]struct{})
|
||||
}
|
||||
|
||||
// IsEqual test whether s and t are the same in size and have the same items.
|
||||
func (s *Set) IsEqual(t Interface) bool {
|
||||
s.l.RLock()
|
||||
defer s.l.RUnlock()
|
||||
|
||||
// Force locking only if given set is threadsafe.
|
||||
if conv, ok := t.(*Set); ok {
|
||||
conv.l.RLock()
|
||||
defer conv.l.RUnlock()
|
||||
}
|
||||
|
||||
// return false if they are no the same size
|
||||
if sameSize := len(s.m) == t.Size(); !sameSize {
|
||||
return false
|
||||
}
|
||||
|
||||
equal := true
|
||||
t.Each(func(item interface{}) bool {
|
||||
_, equal = s.m[item]
|
||||
return equal // if false, Each() will end
|
||||
})
|
||||
|
||||
return equal
|
||||
}
|
||||
|
||||
// IsSubset tests whether t is a subset of s.
|
||||
func (s *Set) IsSubset(t Interface) (subset bool) {
|
||||
s.l.RLock()
|
||||
defer s.l.RUnlock()
|
||||
|
||||
subset = true
|
||||
|
||||
t.Each(func(item interface{}) bool {
|
||||
_, subset = s.m[item]
|
||||
return subset
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Each traverses the items in the Set, calling the provided function for each
|
||||
// set member. Traversal will continue until all items in the Set have been
|
||||
// visited, or if the closure returns false.
|
||||
func (s *Set) Each(f func(item interface{}) bool) {
|
||||
s.l.RLock()
|
||||
defer s.l.RUnlock()
|
||||
|
||||
for item := range s.m {
|
||||
if !f(item) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// List returns a slice of all items. There is also StringSlice() and
|
||||
// IntSlice() methods for returning slices of type string or int.
|
||||
func (s *Set) List() []interface{} {
|
||||
s.l.RLock()
|
||||
defer s.l.RUnlock()
|
||||
|
||||
list := make([]interface{}, 0, len(s.m))
|
||||
|
||||
for item := range s.m {
|
||||
list = append(list, item)
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
// Copy returns a new Set with a copy of s.
|
||||
func (s *Set) Copy() Interface {
|
||||
return New(s.List()...)
|
||||
}
|
||||
|
||||
// Merge is like Union, however it modifies the current set it's applied on
|
||||
// with the given t set.
|
||||
func (s *Set) Merge(t Interface) {
|
||||
s.l.Lock()
|
||||
defer s.l.Unlock()
|
||||
|
||||
t.Each(func(item interface{}) bool {
|
||||
s.m[item] = keyExists
|
||||
return true
|
||||
})
|
||||
}
|
|
@ -80,6 +80,12 @@
|
|||
"revision": "346938d642f2ec3594ed81d874461961cd0faa76",
|
||||
"revisionTime": "2016-10-29T20:57:26Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "1xK7ycc1ICRInk/S9iiyB9Rpv50=",
|
||||
"path": "github.com/deckarep/golang-set",
|
||||
"revision": "504e848d77ea4752b3057b8fb46da0e7f746ccf3",
|
||||
"revisionTime": "2018-06-03T19:32:48Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "2Fy1Y6Z3lRRX1891WF/+HT4XS2I=",
|
||||
"path": "github.com/dgrijalva/jwt-go",
|
||||
|
@ -891,12 +897,6 @@
|
|||
"revision": "20d25e2804050c1cd24a7eea1e7a6447dd0e74ec",
|
||||
"revisionTime": "2016-12-08T18:13:25Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "NGg7/qIJVUfXi7xnEyyDLocdi6Y=",
|
||||
"path": "gopkg.in/fatih/set.v0",
|
||||
"revision": "27c40922c40b43fe04554d8223a402af3ea333f3",
|
||||
"revisionTime": "2014-12-10T08:48:24Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "DQXNV0EivoHm4q+bkdahYXrjjfE=",
|
||||
"path": "gopkg.in/karalabe/cookiejar.v2/collections/prque",
|
||||
|
|
|
@ -20,11 +20,11 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
|
||||
mapset "github.com/deckarep/golang-set"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
set "gopkg.in/fatih/set.v0"
|
||||
)
|
||||
|
||||
// Peer represents a whisper protocol peer connection.
|
||||
|
@ -34,7 +34,7 @@ type Peer struct {
|
|||
ws p2p.MsgReadWriter
|
||||
trusted bool
|
||||
|
||||
known *set.Set // Messages already known by the peer to avoid wasting bandwidth
|
||||
known mapset.Set // Messages already known by the peer to avoid wasting bandwidth
|
||||
|
||||
quit chan struct{}
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ func newPeer(host *Whisper, remote *p2p.Peer, rw p2p.MsgReadWriter) *Peer {
|
|||
peer: remote,
|
||||
ws: rw,
|
||||
trusted: false,
|
||||
known: set.New(),
|
||||
known: mapset.NewSet(),
|
||||
quit: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ func (peer *Peer) mark(envelope *Envelope) {
|
|||
|
||||
// marked checks if an envelope is already known to the remote peer.
|
||||
func (peer *Peer) marked(envelope *Envelope) bool {
|
||||
return peer.known.Has(envelope.Hash())
|
||||
return peer.known.Contains(envelope.Hash())
|
||||
}
|
||||
|
||||
// expire iterates over all the known envelopes in the host and removes all
|
||||
|
|
|
@ -26,6 +26,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
mapset "github.com/deckarep/golang-set"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
|
@ -34,7 +35,6 @@ import (
|
|||
"github.com/syndtr/goleveldb/leveldb/errors"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
"golang.org/x/sync/syncmap"
|
||||
set "gopkg.in/fatih/set.v0"
|
||||
)
|
||||
|
||||
type Statistics struct {
|
||||
|
@ -63,7 +63,7 @@ type Whisper struct {
|
|||
|
||||
poolMu sync.RWMutex // Mutex to sync the message and expiration pools
|
||||
envelopes map[common.Hash]*Envelope // Pool of envelopes currently tracked by this node
|
||||
expirations map[uint32]*set.SetNonTS // Message expiration pool
|
||||
expirations map[uint32]mapset.Set // Message expiration pool
|
||||
|
||||
peerMu sync.RWMutex // Mutex to sync the active peer set
|
||||
peers map[*Peer]struct{} // Set of currently active peers
|
||||
|
@ -90,7 +90,7 @@ func New(cfg *Config) *Whisper {
|
|||
privateKeys: make(map[string]*ecdsa.PrivateKey),
|
||||
symKeys: make(map[string][]byte),
|
||||
envelopes: make(map[common.Hash]*Envelope),
|
||||
expirations: make(map[uint32]*set.SetNonTS),
|
||||
expirations: make(map[uint32]mapset.Set),
|
||||
peers: make(map[*Peer]struct{}),
|
||||
messageQueue: make(chan *Envelope, messageQueueLimit),
|
||||
p2pMsgQueue: make(chan *Envelope, messageQueueLimit),
|
||||
|
@ -608,9 +608,9 @@ func (w *Whisper) add(envelope *Envelope) (bool, error) {
|
|||
if !alreadyCached {
|
||||
w.envelopes[hash] = envelope
|
||||
if w.expirations[envelope.Expiry] == nil {
|
||||
w.expirations[envelope.Expiry] = set.NewNonTS()
|
||||
w.expirations[envelope.Expiry] = mapset.NewThreadUnsafeSet()
|
||||
}
|
||||
if !w.expirations[envelope.Expiry].Has(hash) {
|
||||
if !w.expirations[envelope.Expiry].Contains(hash) {
|
||||
w.expirations[envelope.Expiry].Add(hash)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,8 +22,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
mapset "github.com/deckarep/golang-set"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
set "gopkg.in/fatih/set.v0"
|
||||
)
|
||||
|
||||
func TestMultipleTopicCopyInNewMessageFilter(t *testing.T) {
|
||||
|
@ -31,7 +31,7 @@ func TestMultipleTopicCopyInNewMessageFilter(t *testing.T) {
|
|||
privateKeys: make(map[string]*ecdsa.PrivateKey),
|
||||
symKeys: make(map[string][]byte),
|
||||
envelopes: make(map[common.Hash]*Envelope),
|
||||
expirations: make(map[uint32]*set.SetNonTS),
|
||||
expirations: make(map[uint32]mapset.Set),
|
||||
peers: make(map[*Peer]struct{}),
|
||||
messageQueue: make(chan *Envelope, messageQueueLimit),
|
||||
p2pMsgQueue: make(chan *Envelope, messageQueueLimit),
|
||||
|
|
|
@ -22,11 +22,11 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
mapset "github.com/deckarep/golang-set"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
set "gopkg.in/fatih/set.v0"
|
||||
)
|
||||
|
||||
// Peer represents a whisper protocol peer connection.
|
||||
|
@ -41,7 +41,7 @@ type Peer struct {
|
|||
bloomFilter []byte
|
||||
fullNode bool
|
||||
|
||||
known *set.Set // Messages already known by the peer to avoid wasting bandwidth
|
||||
known mapset.Set // Messages already known by the peer to avoid wasting bandwidth
|
||||
|
||||
quit chan struct{}
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ func newPeer(host *Whisper, remote *p2p.Peer, rw p2p.MsgReadWriter) *Peer {
|
|||
ws: rw,
|
||||
trusted: false,
|
||||
powRequirement: 0.0,
|
||||
known: set.New(),
|
||||
known: mapset.NewSet(),
|
||||
quit: make(chan struct{}),
|
||||
bloomFilter: MakeFullNodeBloom(),
|
||||
fullNode: true,
|
||||
|
@ -165,7 +165,7 @@ func (peer *Peer) mark(envelope *Envelope) {
|
|||
|
||||
// marked checks if an envelope is already known to the remote peer.
|
||||
func (peer *Peer) marked(envelope *Envelope) bool {
|
||||
return peer.known.Has(envelope.Hash())
|
||||
return peer.known.Contains(envelope.Hash())
|
||||
}
|
||||
|
||||
// expire iterates over all the known envelopes in the host and removes all
|
||||
|
|
|
@ -26,6 +26,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
mapset "github.com/deckarep/golang-set"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
|
@ -35,7 +36,6 @@ import (
|
|||
"github.com/syndtr/goleveldb/leveldb/errors"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
"golang.org/x/sync/syncmap"
|
||||
set "gopkg.in/fatih/set.v0"
|
||||
)
|
||||
|
||||
// Statistics holds several message-related counter for analytics
|
||||
|
@ -69,7 +69,7 @@ type Whisper struct {
|
|||
|
||||
poolMu sync.RWMutex // Mutex to sync the message and expiration pools
|
||||
envelopes map[common.Hash]*Envelope // Pool of envelopes currently tracked by this node
|
||||
expirations map[uint32]*set.SetNonTS // Message expiration pool
|
||||
expirations map[uint32]mapset.Set // Message expiration pool
|
||||
|
||||
peerMu sync.RWMutex // Mutex to sync the active peer set
|
||||
peers map[*Peer]struct{} // Set of currently active peers
|
||||
|
@ -100,7 +100,7 @@ func New(cfg *Config) *Whisper {
|
|||
privateKeys: make(map[string]*ecdsa.PrivateKey),
|
||||
symKeys: make(map[string][]byte),
|
||||
envelopes: make(map[common.Hash]*Envelope),
|
||||
expirations: make(map[uint32]*set.SetNonTS),
|
||||
expirations: make(map[uint32]mapset.Set),
|
||||
peers: make(map[*Peer]struct{}),
|
||||
messageQueue: make(chan *Envelope, messageQueueLimit),
|
||||
p2pMsgQueue: make(chan *Envelope, messageQueueLimit),
|
||||
|
@ -796,9 +796,9 @@ func (whisper *Whisper) add(envelope *Envelope, isP2P bool) (bool, error) {
|
|||
if !alreadyCached {
|
||||
whisper.envelopes[hash] = envelope
|
||||
if whisper.expirations[envelope.Expiry] == nil {
|
||||
whisper.expirations[envelope.Expiry] = set.NewNonTS()
|
||||
whisper.expirations[envelope.Expiry] = mapset.NewThreadUnsafeSet()
|
||||
}
|
||||
if !whisper.expirations[envelope.Expiry].Has(hash) {
|
||||
if !whisper.expirations[envelope.Expiry].Contains(hash) {
|
||||
whisper.expirations[envelope.Expiry].Add(hash)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue