diff --git a/libnetwork/controller.go b/libnetwork/controller.go index aebc86a5f9..cd5ad761c9 100644 --- a/libnetwork/controller.go +++ b/libnetwork/controller.go @@ -94,7 +94,7 @@ type Controller struct { extKeyListener net.Listener watchCh chan *Endpoint unWatchCh chan *Endpoint - svcRecords map[string]svcInfo + svcRecords map[string]*svcInfo nmap map[string]*netWatch serviceBindings map[serviceKey]*service defOsSbox osl.Sandbox @@ -120,7 +120,7 @@ func New(cfgOptions ...config.Option) (*Controller, error) { id: stringid.GenerateRandomID(), cfg: config.New(cfgOptions...), sandboxes: sandboxTable{}, - svcRecords: make(map[string]svcInfo), + svcRecords: make(map[string]*svcInfo), serviceBindings: make(map[serviceKey]*service), agentInitDone: make(chan struct{}), networkLocker: locker.New(), diff --git a/libnetwork/drivers/overlay/peerdb.go b/libnetwork/drivers/overlay/peerdb.go index 56f58f10d0..946d77f6e7 100644 --- a/libnetwork/drivers/overlay/peerdb.go +++ b/libnetwork/drivers/overlay/peerdb.go @@ -60,8 +60,8 @@ func (p *peerEntryDB) UnMarshalDB() peerEntry { } type peerMap struct { - // set of peerEntry, note they have to be objects and not pointers to maintain the proper equality checks - mp *setmatrix.SetMatrix + // set of peerEntry, note the values have to be objects and not pointers to maintain the proper equality checks + mp setmatrix.SetMatrix[peerEntryDB] sync.Mutex } @@ -122,7 +122,7 @@ func (d *driver) peerDbNetworkWalk(nid string, f func(*peerKey, *peerEntry) bool for _, pKeyStr := range pMap.mp.Keys() { entryDBList, ok := pMap.mp.Get(pKeyStr) if ok { - peerEntryDB := entryDBList[0].(peerEntryDB) + peerEntryDB := entryDBList[0] mp[pKeyStr] = peerEntryDB.UnMarshalDB() } } @@ -170,11 +170,8 @@ func (d *driver) peerDbAdd(nid, eid string, peerIP net.IP, peerIPMask net.IPMask d.peerDb.Lock() pMap, ok := d.peerDb.mp[nid] if !ok { - d.peerDb.mp[nid] = &peerMap{ - mp: setmatrix.NewSetMatrix(), - } - - pMap = d.peerDb.mp[nid] + pMap = &peerMap{} + d.peerDb.mp[nid] = pMap } d.peerDb.Unlock() diff --git a/libnetwork/internal/setmatrix/setmatrix.go b/libnetwork/internal/setmatrix/setmatrix.go index 31bf7e78b4..b124f9db8e 100644 --- a/libnetwork/internal/setmatrix/setmatrix.go +++ b/libnetwork/internal/setmatrix/setmatrix.go @@ -3,29 +3,21 @@ package setmatrix import ( "sync" - mapset "github.com/deckarep/golang-set" + mapset "github.com/deckarep/golang-set/v2" ) // SetMatrix is a map of Sets. -type SetMatrix struct { - matrix map[string]mapset.Set +// The zero value is an empty set matrix ready to use. +// +// SetMatrix values are safe for concurrent use. +type SetMatrix[T comparable] struct { + matrix map[string]mapset.Set[T] mu sync.Mutex } -// NewSetMatrix creates a new set matrix object. -func NewSetMatrix() *SetMatrix { - s := &SetMatrix{} - s.init() - return s -} - -func (s *SetMatrix) init() { - s.matrix = make(map[string]mapset.Set) -} - // Get returns the members of the set for a specific key as a slice. -func (s *SetMatrix) Get(key string) ([]interface{}, bool) { +func (s *SetMatrix[T]) Get(key string) ([]T, bool) { s.mu.Lock() defer s.mu.Unlock() set, ok := s.matrix[key] @@ -36,7 +28,7 @@ func (s *SetMatrix) Get(key string) ([]interface{}, bool) { } // Contains is used to verify if an element is in a set for a specific key. -func (s *SetMatrix) Contains(key string, value interface{}) (containsElement, setExists bool) { +func (s *SetMatrix[T]) Contains(key string, value T) (containsElement, setExists bool) { s.mu.Lock() defer s.mu.Unlock() set, ok := s.matrix[key] @@ -48,13 +40,15 @@ func (s *SetMatrix) Contains(key string, value interface{}) (containsElement, se // Insert inserts the value in the set of a key and returns whether the value is // inserted (was not already in the set) and the number of elements in the set. -func (s *SetMatrix) Insert(key string, value interface{}) (insetrted bool, cardinality int) { +func (s *SetMatrix[T]) Insert(key string, value T) (inserted bool, cardinality int) { s.mu.Lock() defer s.mu.Unlock() set, ok := s.matrix[key] if !ok { - s.matrix[key] = mapset.NewSet() - s.matrix[key].Add(value) + if s.matrix == nil { + s.matrix = make(map[string]mapset.Set[T]) + } + s.matrix[key] = mapset.NewThreadUnsafeSet(value) return true, 1 } @@ -62,7 +56,7 @@ func (s *SetMatrix) Insert(key string, value interface{}) (insetrted bool, cardi } // Remove removes the value in the set for a specific key. -func (s *SetMatrix) Remove(key string, value interface{}) (removed bool, cardinality int) { +func (s *SetMatrix[T]) Remove(key string, value T) (removed bool, cardinality int) { s.mu.Lock() defer s.mu.Unlock() set, ok := s.matrix[key] @@ -83,7 +77,7 @@ func (s *SetMatrix) Remove(key string, value interface{}) (removed bool, cardina } // Cardinality returns the number of elements in the set for a key. -func (s *SetMatrix) Cardinality(key string) (cardinality int, ok bool) { +func (s *SetMatrix[T]) Cardinality(key string) (cardinality int, ok bool) { s.mu.Lock() defer s.mu.Unlock() set, ok := s.matrix[key] @@ -96,7 +90,7 @@ func (s *SetMatrix) Cardinality(key string) (cardinality int, ok bool) { // String returns the string version of the set. // The empty string is returned if there is no set for key. -func (s *SetMatrix) String(key string) (v string, ok bool) { +func (s *SetMatrix[T]) String(key string) (v string, ok bool) { s.mu.Lock() defer s.mu.Unlock() set, ok := s.matrix[key] @@ -107,7 +101,7 @@ func (s *SetMatrix) String(key string) (v string, ok bool) { } // Keys returns all the keys in the map. -func (s *SetMatrix) Keys() []string { +func (s *SetMatrix[T]) Keys() []string { s.mu.Lock() defer s.mu.Unlock() keys := make([]string, 0, len(s.matrix)) diff --git a/libnetwork/internal/setmatrix/setmatrix_test.go b/libnetwork/internal/setmatrix/setmatrix_test.go index 08dba15caa..b14c04de1e 100644 --- a/libnetwork/internal/setmatrix/setmatrix_test.go +++ b/libnetwork/internal/setmatrix/setmatrix_test.go @@ -9,7 +9,7 @@ import ( ) func TestSetSerialInsertDelete(t *testing.T) { - s := NewSetMatrix() + var s SetMatrix[string] b, i := s.Insert("a", "1") if !b || i != 1 { @@ -135,7 +135,7 @@ func TestSetSerialInsertDelete(t *testing.T) { } } -func insertDeleteRotuine(ctx context.Context, endCh chan int, s *SetMatrix, key, value string) { +func insertDeleteRotuine(ctx context.Context, endCh chan int, s *SetMatrix[string], key, value string) { for { select { case <-ctx.Done(): @@ -158,14 +158,14 @@ func insertDeleteRotuine(ctx context.Context, endCh chan int, s *SetMatrix, key, } func TestSetParallelInsertDelete(t *testing.T) { - s := NewSetMatrix() + var s SetMatrix[string] parallelRoutines := 6 endCh := make(chan int) // Let the routines running and competing for 10s ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() for i := 0; i < parallelRoutines; i++ { - go insertDeleteRotuine(ctx, endCh, s, "key-"+strconv.Itoa(i%3), strconv.Itoa(i)) + go insertDeleteRotuine(ctx, endCh, &s, "key-"+strconv.Itoa(i%3), strconv.Itoa(i)) } for parallelRoutines > 0 { v := <-endCh diff --git a/libnetwork/libnetwork_internal_test.go b/libnetwork/libnetwork_internal_test.go index 5d6f92d1d7..146b25fa80 100644 --- a/libnetwork/libnetwork_internal_test.go +++ b/libnetwork/libnetwork_internal_test.go @@ -11,7 +11,6 @@ import ( "github.com/docker/docker/libnetwork/datastore" "github.com/docker/docker/libnetwork/discoverapi" "github.com/docker/docker/libnetwork/driverapi" - "github.com/docker/docker/libnetwork/internal/setmatrix" "github.com/docker/docker/libnetwork/ipamapi" "github.com/docker/docker/libnetwork/netlabel" "github.com/docker/docker/libnetwork/netutils" @@ -389,11 +388,8 @@ func TestSRVServiceQuery(t *testing.T) { t.Fatal(err) } - sr := svcInfo{ - svcMap: setmatrix.NewSetMatrix(), - svcIPv6Map: setmatrix.NewSetMatrix(), - ipMap: setmatrix.NewSetMatrix(), - service: make(map[string][]servicePorts), + sr := &svcInfo{ + service: make(map[string][]servicePorts), } // backing container for the service cTarget := serviceTarget{ diff --git a/libnetwork/network.go b/libnetwork/network.go index aad1e93383..498c5566d5 100644 --- a/libnetwork/network.go +++ b/libnetwork/network.go @@ -105,9 +105,9 @@ type svcMapEntry struct { } type svcInfo struct { - svcMap *setmatrix.SetMatrix - svcIPv6Map *setmatrix.SetMatrix - ipMap *setmatrix.SetMatrix + svcMap setmatrix.SetMatrix[svcMapEntry] + svcIPv6Map setmatrix.SetMatrix[svcMapEntry] + ipMap setmatrix.SetMatrix[ipInfo] service map[string][]servicePorts } @@ -1362,7 +1362,7 @@ func (n *network) updateSvcRecord(ep *Endpoint, localEps []*Endpoint, isAdd bool } } -func addIPToName(ipMap *setmatrix.SetMatrix, name, serviceID string, ip net.IP) { +func addIPToName(ipMap *setmatrix.SetMatrix[ipInfo], name, serviceID string, ip net.IP) { reverseIP := netutils.ReverseIP(ip.String()) ipMap.Insert(reverseIP, ipInfo{ name: name, @@ -1370,7 +1370,7 @@ func addIPToName(ipMap *setmatrix.SetMatrix, name, serviceID string, ip net.IP) }) } -func delIPToName(ipMap *setmatrix.SetMatrix, name, serviceID string, ip net.IP) { +func delIPToName(ipMap *setmatrix.SetMatrix[ipInfo], name, serviceID string, ip net.IP) { reverseIP := netutils.ReverseIP(ip.String()) ipMap.Remove(reverseIP, ipInfo{ name: name, @@ -1378,7 +1378,7 @@ func delIPToName(ipMap *setmatrix.SetMatrix, name, serviceID string, ip net.IP) }) } -func addNameToIP(svcMap *setmatrix.SetMatrix, name, serviceID string, epIP net.IP) { +func addNameToIP(svcMap *setmatrix.SetMatrix[svcMapEntry], name, serviceID string, epIP net.IP) { // Since DNS name resolution is case-insensitive, Use the lower-case form // of the name as the key into svcMap lowerCaseName := strings.ToLower(name) @@ -1388,7 +1388,7 @@ func addNameToIP(svcMap *setmatrix.SetMatrix, name, serviceID string, epIP net.I }) } -func delNameToIP(svcMap *setmatrix.SetMatrix, name, serviceID string, epIP net.IP) { +func delNameToIP(svcMap *setmatrix.SetMatrix[svcMapEntry], name, serviceID string, epIP net.IP) { lowerCaseName := strings.ToLower(name) svcMap.Remove(lowerCaseName, svcMapEntry{ ip: epIP.String(), @@ -1411,24 +1411,20 @@ func (n *network) addSvcRecords(eID, name, serviceID string, epIP, epIPv6 net.IP sr, ok := c.svcRecords[networkID] if !ok { - sr = svcInfo{ - svcMap: setmatrix.NewSetMatrix(), - svcIPv6Map: setmatrix.NewSetMatrix(), - ipMap: setmatrix.NewSetMatrix(), - } + sr = &svcInfo{} c.svcRecords[networkID] = sr } if ipMapUpdate { - addIPToName(sr.ipMap, name, serviceID, epIP) + addIPToName(&sr.ipMap, name, serviceID, epIP) if epIPv6 != nil { - addIPToName(sr.ipMap, name, serviceID, epIPv6) + addIPToName(&sr.ipMap, name, serviceID, epIPv6) } } - addNameToIP(sr.svcMap, name, serviceID, epIP) + addNameToIP(&sr.svcMap, name, serviceID, epIP) if epIPv6 != nil { - addNameToIP(sr.svcIPv6Map, name, serviceID, epIPv6) + addNameToIP(&sr.svcIPv6Map, name, serviceID, epIPv6) } } @@ -1451,17 +1447,17 @@ func (n *network) deleteSvcRecords(eID, name, serviceID string, epIP net.IP, epI } if ipMapUpdate { - delIPToName(sr.ipMap, name, serviceID, epIP) + delIPToName(&sr.ipMap, name, serviceID, epIP) if epIPv6 != nil { - delIPToName(sr.ipMap, name, serviceID, epIPv6) + delIPToName(&sr.ipMap, name, serviceID, epIPv6) } } - delNameToIP(sr.svcMap, name, serviceID, epIP) + delNameToIP(&sr.svcMap, name, serviceID, epIP) if epIPv6 != nil { - delNameToIP(sr.svcIPv6Map, name, serviceID, epIPv6) + delNameToIP(&sr.svcIPv6Map, name, serviceID, epIPv6) } } @@ -1480,7 +1476,7 @@ func (n *network) getSvcRecords(ep *Endpoint) []etchosts.Record { n.ctrlr.mu.Lock() defer n.ctrlr.mu.Unlock() sr, ok := n.ctrlr.svcRecords[n.id] - if !ok || sr.svcMap == nil { + if !ok { return nil } @@ -1503,7 +1499,7 @@ func (n *network) getSvcRecords(ep *Endpoint) []etchosts.Record { recs = append(recs, etchosts.Record{ Hosts: k, - IP: mapEntryList[0].(svcMapEntry).ip, + IP: mapEntryList[0].ip, }) } @@ -2005,9 +2001,9 @@ func (n *network) ResolveName(req string, ipType int) ([]net.IP, bool) { noDup := make(map[string]bool) var ipLocal []net.IP for _, ip := range ipSet { - if _, dup := noDup[ip.(svcMapEntry).ip]; !dup { - noDup[ip.(svcMapEntry).ip] = true - ipLocal = append(ipLocal, net.ParseIP(ip.(svcMapEntry).ip)) + if _, dup := noDup[ip.ip]; !dup { + noDup[ip.ip] = true + ipLocal = append(ipLocal, net.ParseIP(ip.ip)) } } return ipLocal, ok @@ -2058,13 +2054,7 @@ func (n *network) ResolveIP(ip string) string { // network db notifications) // In such cases the resolution will be based on the first element of the set, and can vary // during the system stabilitation - elem, ok := elemSet[0].(ipInfo) - if !ok { - setStr, b := sr.ipMap.String(ip) - logrus.Errorf("expected set of ipInfo type for key %s set:%t %s", ip, b, setStr) - return "" - } - + elem := elemSet[0] if elem.extResolver { return "" } diff --git a/libnetwork/service.go b/libnetwork/service.go index eff371ed70..4bb903afab 100644 --- a/libnetwork/service.go +++ b/libnetwork/service.go @@ -54,7 +54,7 @@ type service struct { // associated with it. At stable state the endpoint ID expected is 1 // but during transition and service change it is possible to have // temporary more than 1 - ipToEndpoint *setmatrix.SetMatrix + ipToEndpoint setmatrix.SetMatrix[string] deleted bool diff --git a/libnetwork/service_common.go b/libnetwork/service_common.go index c632466c22..e04f4048ed 100644 --- a/libnetwork/service_common.go +++ b/libnetwork/service_common.go @@ -6,7 +6,6 @@ package libnetwork import ( "net" - "github.com/docker/docker/libnetwork/internal/setmatrix" "github.com/sirupsen/logrus" ) @@ -144,7 +143,6 @@ func newService(name string, id string, ingressPorts []*PortConfig, serviceAlias ingressPorts: ingressPorts, loadBalancers: make(map[string]*loadBalancer), aliases: serviceAliases, - ipToEndpoint: setmatrix.NewSetMatrix(), } } @@ -174,7 +172,7 @@ func (c *Controller) cleanupServiceDiscovery(cleanupNID string) { defer c.mu.Unlock() if cleanupNID == "" { logrus.Debugf("cleanupServiceDiscovery for all networks") - c.svcRecords = make(map[string]svcInfo) + c.svcRecords = make(map[string]*svcInfo) return } logrus.Debugf("cleanupServiceDiscovery for network:%s", cleanupNID) diff --git a/vendor.mod b/vendor.mod index 323b544e17..b512d03efa 100644 --- a/vendor.mod +++ b/vendor.mod @@ -31,7 +31,7 @@ require ( github.com/containerd/typeurl/v2 v2.1.0 github.com/coreos/go-systemd/v22 v22.5.0 github.com/creack/pty v1.1.11 - github.com/deckarep/golang-set v0.0.0-20141123011944-ef32fa3046d9 + github.com/deckarep/golang-set/v2 v2.3.0 github.com/docker/distribution v2.8.1+incompatible github.com/docker/go-connections v0.4.0 github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c diff --git a/vendor.sum b/vendor.sum index 42b7fb6c05..7b7c92cd8a 100644 --- a/vendor.sum +++ b/vendor.sum @@ -484,8 +484,8 @@ github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deckarep/golang-set v0.0.0-20141123011944-ef32fa3046d9 h1:YpTz1+8tEHbybtxtMJNkV3U3GBAA05EakMRTR3dXkis= -github.com/deckarep/golang-set v0.0.0-20141123011944-ef32fa3046d9/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= +github.com/deckarep/golang-set/v2 v2.3.0 h1:qs18EKUfHm2X9fA50Mr/M5hccg2tNnVqsiBImnyDs0g= +github.com/deckarep/golang-set/v2 v2.3.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= diff --git a/vendor/github.com/deckarep/golang-set/.travis.yml b/vendor/github.com/deckarep/golang-set/.travis.yml deleted file mode 100644 index db1359c72e..0000000000 --- a/vendor/github.com/deckarep/golang-set/.travis.yml +++ /dev/null @@ -1,9 +0,0 @@ -language: go - -go: - - 1.2 - -script: - - go test ./... - #- go test -race ./... - diff --git a/vendor/github.com/deckarep/golang-set/README.md b/vendor/github.com/deckarep/golang-set/README.md deleted file mode 100644 index 744b1841cd..0000000000 --- a/vendor/github.com/deckarep/golang-set/README.md +++ /dev/null @@ -1,94 +0,0 @@ -[![Build Status](https://travis-ci.org/deckarep/golang-set.png?branch=master)](https://travis-ci.org/deckarep/golang-set) -[![GoDoc](https://godoc.org/github.com/deckarep/golang-set?status.png)](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 CartesionProduct() method has been added with unit-tests: [Read more about the cartesion 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) diff --git a/vendor/github.com/deckarep/golang-set/set.go b/vendor/github.com/deckarep/golang-set/set.go deleted file mode 100644 index eccba70e44..0000000000 --- a/vendor/github.com/deckarep/golang-set/set.go +++ /dev/null @@ -1,168 +0,0 @@ -/* -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. The default -// implementation is safe for concurrent access. There is a non-threadsafe -// implementation which is slightly more performant. -package mapset - -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 emtpy 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 the other set - // is in this 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 this set is in - // the other 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 - - // Returns a channel of elements that you can - // range over. - Iter() <-chan interface{} - - // 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 - - // 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{} -} - -// Creates and returns a reference to an empty set. -func NewSet() Set { - set := newThreadSafeSet() - return &set -} - -// Creates and returns a reference to a set from an existing slice -func NewSetFromSlice(s []interface{}) Set { - a := NewSet() - for _, item := range s { - a.Add(item) - } - return a -} - -func NewThreadUnsafeSet() Set { - set := newThreadUnsafeSet() - return &set -} - -func NewThreadUnsafeSetFromSlice(s []interface{}) Set { - a := NewThreadUnsafeSet() - for _, item := range s { - a.Add(item) - } - return a -} diff --git a/vendor/github.com/deckarep/golang-set/threadsafe.go b/vendor/github.com/deckarep/golang-set/threadsafe.go deleted file mode 100644 index 9dca94af73..0000000000 --- a/vendor/github.com/deckarep/golang-set/threadsafe.go +++ /dev/null @@ -1,204 +0,0 @@ -/* -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) IsSuperset(other Set) bool { - return other.IsSubset(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) - - unsafeDifference := set.s.SymmetricDifference(&o.s).(*threadUnsafeSet) - return &threadSafeSet{s: *unsafeDifference} -} - -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) 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) 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) 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{} { - set.RLock() - keys := make([]interface{}, 0, set.Cardinality()) - for elem := range set.s { - keys = append(keys, elem) - } - set.RUnlock() - return keys -} diff --git a/vendor/github.com/deckarep/golang-set/threadunsafe.go b/vendor/github.com/deckarep/golang-set/threadunsafe.go deleted file mode 100644 index 124521e2ee..0000000000 --- a/vendor/github.com/deckarep/golang-set/threadunsafe.go +++ /dev/null @@ -1,246 +0,0 @@ -/* -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 ( - "fmt" - "reflect" - "strings" -) - -type threadUnsafeSet map[interface{}]struct{} - -type orderedPair struct { - first interface{} - second interface{} -} - -func newThreadUnsafeSet() threadUnsafeSet { - return make(threadUnsafeSet) -} - -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] - (*set)[i] = struct{}{} - return !found //False if it existed already -} - -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) IsSuperset(other Set) bool { - return other.IsSubset(set) -} - -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) Iter() <-chan interface{} { - ch := make(chan interface{}) - go func() { - for elem := range *set { - ch <- elem - } - close(ch) - }() - - return ch -} - -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, ", ")) -} - -func (pair orderedPair) String() string { - return fmt.Sprintf("(%v, %v)", pair.first, pair.second) -} - -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 -} diff --git a/vendor/github.com/deckarep/golang-set/.gitignore b/vendor/github.com/deckarep/golang-set/v2/.gitignore similarity index 100% rename from vendor/github.com/deckarep/golang-set/.gitignore rename to vendor/github.com/deckarep/golang-set/v2/.gitignore diff --git a/vendor/github.com/deckarep/golang-set/LICENSE b/vendor/github.com/deckarep/golang-set/v2/LICENSE similarity index 94% rename from vendor/github.com/deckarep/golang-set/LICENSE rename to vendor/github.com/deckarep/golang-set/v2/LICENSE index b5768f89cf..efd4827e21 100644 --- a/vendor/github.com/deckarep/golang-set/LICENSE +++ b/vendor/github.com/deckarep/golang-set/v2/LICENSE @@ -1,7 +1,7 @@ Open Source Initiative OSI - The MIT License (MIT):Licensing The MIT License (MIT) -Copyright (c) 2013 Ralph Caraveo (deckarep@gmail.com) +Copyright (c) 2013 - 2022 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 diff --git a/vendor/github.com/deckarep/golang-set/v2/README.md b/vendor/github.com/deckarep/golang-set/v2/README.md new file mode 100644 index 0000000000..55e30afc7c --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/README.md @@ -0,0 +1,173 @@ +![example workflow](https://github.com/deckarep/golang-set/actions/workflows/ci.yml/badge.svg) +[![Go Report Card](https://goreportcard.com/badge/github.com/deckarep/golang-set/v2)](https://goreportcard.com/report/github.com/deckarep/golang-set/v2) +[![GoDoc](https://godoc.org/github.com/deckarep/golang-set/v2?status.svg)](http://godoc.org/github.com/deckarep/golang-set/v2) + +# golang-set + +The missing `generic` set collection for the Go language. Until Go has sets built-in...use this. + +## Update 3/5/2023 +* Packaged version: `2.2.0` release includes a refactor to minimize pointer indirection, better method documentation standards and a few constructor convenience methods to increase ergonomics when appending items `Append` or creating a new set from an exist `Map`. +* supports `new generic` syntax +* Go `1.18.0` or higher +* Workflow tested on Go `1.20` + +![With Generics](new_improved.jpeg) + +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 collection 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 contributing with suggestions or PRs. + +## Features + +* *NEW* [Generics](https://go.dev/doc/tutorial/generics) based implementation (requires [Go 1.18](https://go.dev/blog/go1.18beta1) or higher) +* One common *interface* to both implementations + * a **non threadsafe** implementation favoring *performance* + * a **threadsafe** implementation favoring *concurrent* use +* Feature complete set implementation modeled after [Python's set implementation](https://docs.python.org/3/library/stdtypes.html#set). +* Exhaustive unit-test and benchmark suite + +## Trusted by + +This package is trusted by many companies and thousands of open-source packages. Here are just a few sample users of this package. + +* Notable projects/companies using this package + * Ethereum + * Docker + * 1Password + * Hashicorp + +## Star History + +[![Star History Chart](https://api.star-history.com/svg?repos=deckarep/golang-set&type=Date)](https://star-history.com/#deckarep/golang-set&Date) + + +## Usage + +The code below demonstrates how a Set collection can better manage data and actually minimize boilerplate and needless loops in code. This package now fully supports *generic* syntax so you are now able to instantiate a collection for any [comparable](https://flaviocopes.com/golang-comparing-values/) type object. + +What is considered comparable in Go? +* `Booleans`, `integers`, `strings`, `floats` or basically primitive types. +* `Pointers` +* `Arrays` +* `Structs` if *all of their fields* are also comparable independently + +Using this library is as simple as creating either a threadsafe or non-threadsafe set and providing a `comparable` type for instantiation of the collection. + +```go +// Syntax example, doesn't compile. +mySet := mapset.NewSet[T]() // where T is some concrete comparable type. + +// Therefore this code creates an int set +mySet := mapset.NewSet[int]() + +// Or perhaps you want a string set +mySet := mapset.NewSet[string]() + +type myStruct { + name string + age uint8 +} + +// Alternatively a set of structs +mySet := mapset.NewSet[myStruct]() + +// Lastly a set that can hold anything using the any or empty interface keyword: interface{}. This is effectively removes type safety. +mySet := mapset.NewSet[any]() +``` + +## Comprehensive Example + +```go +package main + +import ( + "fmt" + mapset "github.com/deckarep/golang-set/v2" +) + +func main() { + // Create a string-based set of required classes. + required := mapset.NewSet[string]() + required.Add("cooking") + required.Add("english") + required.Add("math") + required.Add("biology") + + // Create a string-based set of science classes. + sciences := mapset.NewSet[string]() + sciences.Add("biology") + sciences.Add("chemistry") + + // Create a string-based set of electives. + electives := mapset.NewSet[string]() + electives.Add("welding") + electives.Add("music") + electives.Add("automotive") + + // Create a string-based set of bonus programming classes. + bonus := mapset.NewSet[string]() + bonus.Add("beginner go") + bonus.Add("python for dummies") +} +``` + +Create a set of all unique classes. +Sets will *automatically* deduplicate the same data. + +```go + all := required + .Union(sciences) + .Union(electives) + .Union(bonus) + + fmt.Println(all) +``` + +Output: +```sh +Set{cooking, english, math, chemistry, welding, biology, music, automotive, beginner go, python for dummies} +``` + +Is cooking considered a science class? +```go +result := sciences.Contains("cooking") +fmt.Println(result) +``` + +Output: +```false +false +``` + +Show me all classes that are not science classes, since I don't enjoy science. +```go +notScience := all.Difference(sciences) +fmt.Println(notScience) +``` + +```sh +Set{ music, automotive, beginner go, python for dummies, cooking, english, math, welding } +``` + +Which science classes are also required classes? +```go +reqScience := sciences.Intersect(required) +``` + +Output: +```sh +Set{biology} +``` + +How many bonus classes do you offer? +```go +fmt.Println(bonus.Cardinality()) +``` +Output: +```sh +2 +``` + +Thanks for visiting! + +-deckarep diff --git a/vendor/github.com/deckarep/golang-set/v2/iterator.go b/vendor/github.com/deckarep/golang-set/v2/iterator.go new file mode 100644 index 0000000000..fc14e70564 --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/iterator.go @@ -0,0 +1,58 @@ +/* +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 - 2022 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[T comparable] struct { + C <-chan T + stop chan struct{} +} + +// Stop stops the Iterator, no further elements will be received on C, C will be closed. +func (i *Iterator[T]) 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[T comparable]() (*Iterator[T], chan<- T, <-chan struct{}) { + itemChan := make(chan T) + stopChan := make(chan struct{}) + return &Iterator[T]{ + C: itemChan, + stop: stopChan, + }, itemChan, stopChan +} diff --git a/vendor/github.com/deckarep/golang-set/v2/new_improved.jpeg b/vendor/github.com/deckarep/golang-set/v2/new_improved.jpeg new file mode 100644 index 0000000000..429752a07a Binary files /dev/null and b/vendor/github.com/deckarep/golang-set/v2/new_improved.jpeg differ diff --git a/vendor/github.com/deckarep/golang-set/v2/set.go b/vendor/github.com/deckarep/golang-set/v2/set.go new file mode 100644 index 0000000000..803c8ead9b --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/set.go @@ -0,0 +1,241 @@ +/* +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 - 2022 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 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[T comparable] interface { + // Add adds an element to the set. Returns whether + // the item was added. + Add(val T) bool + + // Append multiple elements to the set. Returns + // the number of elements added. + Append(val ...T) int + + // Cardinality returns the number of elements in the set. + Cardinality() int + + // Clear removes all elements from the set, leaving + // the empty set. + Clear() + + // Clone returns a clone of the set using the same + // implementation, duplicating all keys. + Clone() Set[T] + + // Contains returns whether the given items + // are all in the set. + Contains(val ...T) bool + + // Difference 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[T]) Set[T] + + // Equal 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[T]) bool + + // Intersect 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[T]) Set[T] + + // IsProperSubset 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[T]) bool + + // IsProperSuperset 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[T]) bool + + // IsSubset 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[T]) bool + + // IsSuperset 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[T]) bool + + // Each iterates over elements and executes the passed func against each element. + // If passed func returns true, stop iteration at the time. + Each(func(T) bool) + + // Iter returns a channel of elements that you can + // range over. + Iter() <-chan T + + // Iterator returns an Iterator object that you can + // use to range over the set. + Iterator() *Iterator[T] + + // Remove removes a single element from the set. + Remove(i T) + + // RemoveAll removes multiple elements from the set. + RemoveAll(i ...T) + + // String provides a convenient string representation + // of the current state of the set. + String() string + + // SymmetricDifference 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[T]) Set[T] + + // Union 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[T]) Set[T] + + // Pop removes and returns an arbitrary item from the set. + Pop() (T, bool) + + // ToSlice returns the members of the set as a slice. + ToSlice() []T + + // MarshalJSON will marshal the set into a JSON-based representation. + MarshalJSON() ([]byte, error) + + // UnmarshalJSON will unmarshal a JSON-based byte slice into a full Set datastructure. + // For this to work, set subtypes must implemented the Marshal/Unmarshal interface. + UnmarshalJSON(b []byte) error +} + +// NewSet creates and returns a new set with the given elements. +// Operations on the resulting set are thread-safe. +func NewSet[T comparable](vals ...T) Set[T] { + s := newThreadSafeSetWithSize[T](len(vals)) + for _, item := range vals { + s.Add(item) + } + return s +} + +// NewSetWithSize creates and returns a reference to an empty set with a specified +// capacity. Operations on the resulting set are thread-safe. +func NewSetWithSize[T comparable](cardinality int) Set[T] { + s := newThreadSafeSetWithSize[T](cardinality) + return s +} + +// NewThreadUnsafeSet creates and returns a new set with the given elements. +// Operations on the resulting set are not thread-safe. +func NewThreadUnsafeSet[T comparable](vals ...T) Set[T] { + s := newThreadUnsafeSetWithSize[T](len(vals)) + for _, item := range vals { + s.Add(item) + } + return s +} + +// NewThreadUnsafeSetWithSize creates and returns a reference to an empty set with +// a specified capacity. Operations on the resulting set are not thread-safe. +func NewThreadUnsafeSetWithSize[T comparable](cardinality int) Set[T] { + s := newThreadUnsafeSetWithSize[T](cardinality) + return s +} + +// NewSetFromMapKeys creates and returns a new set with the given keys of the map. +// Operations on the resulting set are thread-safe. +func NewSetFromMapKeys[T comparable, V any](val map[T]V) Set[T] { + s := NewSetWithSize[T](len(val)) + + for k := range val { + s.Add(k) + } + + return s +} + +// NewThreadUnsafeSetFromMapKeys creates and returns a new set with the given keys of the map. +// Operations on the resulting set are not thread-safe. +func NewThreadUnsafeSetFromMapKeys[T comparable, V any](val map[T]V) Set[T] { + s := NewThreadUnsafeSetWithSize[T](len(val)) + + for k := range val { + s.Add(k) + } + + return s +} diff --git a/vendor/github.com/deckarep/golang-set/v2/threadsafe.go b/vendor/github.com/deckarep/golang-set/v2/threadsafe.go new file mode 100644 index 0000000000..9e3a0ca016 --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/threadsafe.go @@ -0,0 +1,279 @@ +/* +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 - 2022 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[T comparable] struct { + sync.RWMutex + uss threadUnsafeSet[T] +} + +func newThreadSafeSet[T comparable]() *threadSafeSet[T] { + return &threadSafeSet[T]{ + uss: newThreadUnsafeSet[T](), + } +} + +func newThreadSafeSetWithSize[T comparable](cardinality int) *threadSafeSet[T] { + return &threadSafeSet[T]{ + uss: newThreadUnsafeSetWithSize[T](cardinality), + } +} + +func (t *threadSafeSet[T]) Add(v T) bool { + t.Lock() + ret := t.uss.Add(v) + t.Unlock() + return ret +} + +func (t *threadSafeSet[T]) Append(v ...T) int { + t.Lock() + ret := t.uss.Append(v...) + t.Unlock() + return ret +} + +func (t *threadSafeSet[T]) Contains(v ...T) bool { + t.RLock() + ret := t.uss.Contains(v...) + t.RUnlock() + + return ret +} + +func (t *threadSafeSet[T]) IsSubset(other Set[T]) bool { + o := other.(*threadSafeSet[T]) + + t.RLock() + o.RLock() + + ret := t.uss.IsSubset(o.uss) + t.RUnlock() + o.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) IsProperSubset(other Set[T]) bool { + o := other.(*threadSafeSet[T]) + + t.RLock() + defer t.RUnlock() + o.RLock() + defer o.RUnlock() + + return t.uss.IsProperSubset(o.uss) +} + +func (t *threadSafeSet[T]) IsSuperset(other Set[T]) bool { + return other.IsSubset(t) +} + +func (t *threadSafeSet[T]) IsProperSuperset(other Set[T]) bool { + return other.IsProperSubset(t) +} + +func (t *threadSafeSet[T]) Union(other Set[T]) Set[T] { + o := other.(*threadSafeSet[T]) + + t.RLock() + o.RLock() + + unsafeUnion := t.uss.Union(o.uss).(threadUnsafeSet[T]) + ret := &threadSafeSet[T]{uss: unsafeUnion} + t.RUnlock() + o.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) Intersect(other Set[T]) Set[T] { + o := other.(*threadSafeSet[T]) + + t.RLock() + o.RLock() + + unsafeIntersection := t.uss.Intersect(o.uss).(threadUnsafeSet[T]) + ret := &threadSafeSet[T]{uss: unsafeIntersection} + t.RUnlock() + o.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) Difference(other Set[T]) Set[T] { + o := other.(*threadSafeSet[T]) + + t.RLock() + o.RLock() + + unsafeDifference := t.uss.Difference(o.uss).(threadUnsafeSet[T]) + ret := &threadSafeSet[T]{uss: unsafeDifference} + t.RUnlock() + o.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) SymmetricDifference(other Set[T]) Set[T] { + o := other.(*threadSafeSet[T]) + + t.RLock() + o.RLock() + + unsafeDifference := t.uss.SymmetricDifference(o.uss).(threadUnsafeSet[T]) + ret := &threadSafeSet[T]{uss: unsafeDifference} + t.RUnlock() + o.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) Clear() { + t.Lock() + t.uss.Clear() + t.Unlock() +} + +func (t *threadSafeSet[T]) Remove(v T) { + t.Lock() + delete(t.uss, v) + t.Unlock() +} + +func (t *threadSafeSet[T]) RemoveAll(i ...T) { + t.Lock() + t.uss.RemoveAll(i...) + t.Unlock() +} + +func (t *threadSafeSet[T]) Cardinality() int { + t.RLock() + defer t.RUnlock() + return len(t.uss) +} + +func (t *threadSafeSet[T]) Each(cb func(T) bool) { + t.RLock() + for elem := range t.uss { + if cb(elem) { + break + } + } + t.RUnlock() +} + +func (t *threadSafeSet[T]) Iter() <-chan T { + ch := make(chan T) + go func() { + t.RLock() + + for elem := range t.uss { + ch <- elem + } + close(ch) + t.RUnlock() + }() + + return ch +} + +func (t *threadSafeSet[T]) Iterator() *Iterator[T] { + iterator, ch, stopCh := newIterator[T]() + + go func() { + t.RLock() + L: + for elem := range t.uss { + select { + case <-stopCh: + break L + case ch <- elem: + } + } + close(ch) + t.RUnlock() + }() + + return iterator +} + +func (t *threadSafeSet[T]) Equal(other Set[T]) bool { + o := other.(*threadSafeSet[T]) + + t.RLock() + o.RLock() + + ret := t.uss.Equal(o.uss) + t.RUnlock() + o.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) Clone() Set[T] { + t.RLock() + + unsafeClone := t.uss.Clone().(threadUnsafeSet[T]) + ret := &threadSafeSet[T]{uss: unsafeClone} + t.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) String() string { + t.RLock() + ret := t.uss.String() + t.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) Pop() (T, bool) { + t.Lock() + defer t.Unlock() + return t.uss.Pop() +} + +func (t *threadSafeSet[T]) ToSlice() []T { + keys := make([]T, 0, t.Cardinality()) + t.RLock() + for elem := range t.uss { + keys = append(keys, elem) + } + t.RUnlock() + return keys +} + +func (t *threadSafeSet[T]) MarshalJSON() ([]byte, error) { + t.RLock() + b, err := t.uss.MarshalJSON() + t.RUnlock() + + return b, err +} + +func (t *threadSafeSet[T]) UnmarshalJSON(p []byte) error { + t.RLock() + err := t.uss.UnmarshalJSON(p) + t.RUnlock() + + return err +} diff --git a/vendor/github.com/deckarep/golang-set/v2/threadunsafe.go b/vendor/github.com/deckarep/golang-set/v2/threadunsafe.go new file mode 100644 index 0000000000..e5f4629af0 --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/threadunsafe.go @@ -0,0 +1,325 @@ +/* +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 - 2022 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" + "strings" +) + +type threadUnsafeSet[T comparable] map[T]struct{} + +// Assert concrete type:threadUnsafeSet adheres to Set interface. +var _ Set[string] = (threadUnsafeSet[string])(nil) + +func newThreadUnsafeSet[T comparable]() threadUnsafeSet[T] { + return make(threadUnsafeSet[T]) +} + +func newThreadUnsafeSetWithSize[T comparable](cardinality int) threadUnsafeSet[T] { + return make(threadUnsafeSet[T], cardinality) +} + +func (s threadUnsafeSet[T]) Add(v T) bool { + prevLen := len(s) + s[v] = struct{}{} + return prevLen != len(s) +} + +func (s threadUnsafeSet[T]) Append(v ...T) int { + prevLen := len(s) + for _, val := range v { + (s)[val] = struct{}{} + } + return len(s) - prevLen +} + +// private version of Add which doesn't return a value +func (s threadUnsafeSet[T]) add(v T) { + s[v] = struct{}{} +} + +func (s threadUnsafeSet[T]) Cardinality() int { + return len(s) +} + +func (s threadUnsafeSet[T]) Clear() { + // Constructions like this are optimised by compiler, and replaced by + // mapclear() function, defined in + // https://github.com/golang/go/blob/29bbca5c2c1ad41b2a9747890d183b6dd3a4ace4/src/runtime/map.go#L993) + for key := range s { + delete(s, key) + } +} + +func (s threadUnsafeSet[T]) Clone() Set[T] { + clonedSet := newThreadUnsafeSetWithSize[T](s.Cardinality()) + for elem := range s { + clonedSet.add(elem) + } + return clonedSet +} + +func (s threadUnsafeSet[T]) Contains(v ...T) bool { + for _, val := range v { + if _, ok := s[val]; !ok { + return false + } + } + return true +} + +// private version of Contains for a single element v +func (s threadUnsafeSet[T]) contains(v T) (ok bool) { + _, ok = s[v] + return ok +} + +func (s threadUnsafeSet[T]) Difference(other Set[T]) Set[T] { + o := other.(threadUnsafeSet[T]) + + diff := newThreadUnsafeSet[T]() + for elem := range s { + if !o.contains(elem) { + diff.add(elem) + } + } + return diff +} + +func (s threadUnsafeSet[T]) Each(cb func(T) bool) { + for elem := range s { + if cb(elem) { + break + } + } +} + +func (s threadUnsafeSet[T]) Equal(other Set[T]) bool { + o := other.(threadUnsafeSet[T]) + + if s.Cardinality() != other.Cardinality() { + return false + } + for elem := range s { + if !o.contains(elem) { + return false + } + } + return true +} + +func (s threadUnsafeSet[T]) Intersect(other Set[T]) Set[T] { + o := other.(threadUnsafeSet[T]) + + intersection := newThreadUnsafeSet[T]() + // loop over smaller set + if s.Cardinality() < other.Cardinality() { + for elem := range s { + if o.contains(elem) { + intersection.add(elem) + } + } + } else { + for elem := range o { + if s.contains(elem) { + intersection.add(elem) + } + } + } + return intersection +} + +func (s threadUnsafeSet[T]) IsProperSubset(other Set[T]) bool { + return s.Cardinality() < other.Cardinality() && s.IsSubset(other) +} + +func (s threadUnsafeSet[T]) IsProperSuperset(other Set[T]) bool { + return s.Cardinality() > other.Cardinality() && s.IsSuperset(other) +} + +func (s threadUnsafeSet[T]) IsSubset(other Set[T]) bool { + o := other.(threadUnsafeSet[T]) + if s.Cardinality() > other.Cardinality() { + return false + } + for elem := range s { + if !o.contains(elem) { + return false + } + } + return true +} + +func (s threadUnsafeSet[T]) IsSuperset(other Set[T]) bool { + return other.IsSubset(s) +} + +func (s threadUnsafeSet[T]) Iter() <-chan T { + ch := make(chan T) + go func() { + for elem := range s { + ch <- elem + } + close(ch) + }() + + return ch +} + +func (s threadUnsafeSet[T]) Iterator() *Iterator[T] { + iterator, ch, stopCh := newIterator[T]() + + go func() { + L: + for elem := range s { + select { + case <-stopCh: + break L + case ch <- elem: + } + } + close(ch) + }() + + return iterator +} + +// Pop returns a popped item in case set is not empty, or nil-value of T +// if set is already empty +func (s threadUnsafeSet[T]) Pop() (v T, ok bool) { + for item := range s { + delete(s, item) + return item, true + } + return v, false +} + +func (s threadUnsafeSet[T]) Remove(v T) { + delete(s, v) +} + +func (s threadUnsafeSet[T]) RemoveAll(i ...T) { + for _, elem := range i { + delete(s, elem) + } +} + +func (s threadUnsafeSet[T]) String() string { + items := make([]string, 0, len(s)) + + for elem := range s { + items = append(items, fmt.Sprintf("%v", elem)) + } + return fmt.Sprintf("Set{%s}", strings.Join(items, ", ")) +} + +func (s threadUnsafeSet[T]) SymmetricDifference(other Set[T]) Set[T] { + o := other.(threadUnsafeSet[T]) + + sd := newThreadUnsafeSet[T]() + for elem := range s { + if !o.contains(elem) { + sd.add(elem) + } + } + for elem := range o { + if !s.contains(elem) { + sd.add(elem) + } + } + return sd +} + +func (s threadUnsafeSet[T]) ToSlice() []T { + keys := make([]T, 0, s.Cardinality()) + for elem := range s { + keys = append(keys, elem) + } + + return keys +} + +func (s threadUnsafeSet[T]) Union(other Set[T]) Set[T] { + o := other.(threadUnsafeSet[T]) + + n := s.Cardinality() + if o.Cardinality() > n { + n = o.Cardinality() + } + unionedSet := make(threadUnsafeSet[T], n) + + for elem := range s { + unionedSet.add(elem) + } + for elem := range o { + unionedSet.add(elem) + } + return unionedSet +} + +// MarshalJSON creates a JSON array from the set, it marshals all elements +func (s threadUnsafeSet[T]) MarshalJSON() ([]byte, error) { + items := make([]string, 0, s.Cardinality()) + + for elem := range s { + 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 (s threadUnsafeSet[T]) UnmarshalJSON(b []byte) error { + var i []any + + 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 T: + s.add(t) + default: + // anything else must be skipped. + continue + } + } + + return nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 68ba8145b2..d521608730 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -355,9 +355,9 @@ github.com/creack/pty # github.com/cyphar/filepath-securejoin v0.2.3 ## explicit; go 1.13 github.com/cyphar/filepath-securejoin -# github.com/deckarep/golang-set v0.0.0-20141123011944-ef32fa3046d9 -## explicit -github.com/deckarep/golang-set +# github.com/deckarep/golang-set/v2 v2.3.0 +## explicit; go 1.18 +github.com/deckarep/golang-set/v2 # github.com/dimchansky/utfbom v1.1.1 ## explicit github.com/dimchansky/utfbom