| package set |
| |
| import ( |
| "sort" |
| ) |
| |
| // Add inserts the given value into the receiving Set. |
| // |
| // This mutates the set in-place. This operation is not thread-safe. |
| func (s Set) Add(val interface{}) { |
| hv := s.rules.Hash(val) |
| if _, ok := s.vals[hv]; !ok { |
| s.vals[hv] = make([]interface{}, 0, 1) |
| } |
| bucket := s.vals[hv] |
| |
| // See if an equivalent value is already present |
| for _, ev := range bucket { |
| if s.rules.Equivalent(val, ev) { |
| return |
| } |
| } |
| |
| s.vals[hv] = append(bucket, val) |
| } |
| |
| // Remove deletes the given value from the receiving set, if indeed it was |
| // there in the first place. If the value is not present, this is a no-op. |
| func (s Set) Remove(val interface{}) { |
| hv := s.rules.Hash(val) |
| bucket, ok := s.vals[hv] |
| if !ok { |
| return |
| } |
| |
| for i, ev := range bucket { |
| if s.rules.Equivalent(val, ev) { |
| newBucket := make([]interface{}, 0, len(bucket)-1) |
| newBucket = append(newBucket, bucket[:i]...) |
| newBucket = append(newBucket, bucket[i+1:]...) |
| if len(newBucket) > 0 { |
| s.vals[hv] = newBucket |
| } else { |
| delete(s.vals, hv) |
| } |
| return |
| } |
| } |
| } |
| |
| // Has returns true if the given value is in the receiving set, or false if |
| // it is not. |
| func (s Set) Has(val interface{}) bool { |
| hv := s.rules.Hash(val) |
| bucket, ok := s.vals[hv] |
| if !ok { |
| return false |
| } |
| |
| for _, ev := range bucket { |
| if s.rules.Equivalent(val, ev) { |
| return true |
| } |
| } |
| return false |
| } |
| |
| // Copy performs a shallow copy of the receiving set, returning a new set |
| // with the same rules and elements. |
| func (s Set) Copy() Set { |
| ret := NewSet(s.rules) |
| for k, v := range s.vals { |
| ret.vals[k] = v |
| } |
| return ret |
| } |
| |
| // Iterator returns an iterator over values in the set. If the set's rules |
| // implement OrderedRules then the result is ordered per those rules. If |
| // no order is provided, or if it is not a total order, then the iteration |
| // order is undefined but consistent for a particular version of cty. Do not |
| // rely on specific ordering between cty releases unless the rules order is a |
| // total order. |
| // |
| // The pattern for using the returned iterator is: |
| // |
| // it := set.Iterator() |
| // for it.Next() { |
| // val := it.Value() |
| // // ... |
| // } |
| // |
| // Once an iterator has been created for a set, the set *must not* be mutated |
| // until the iterator is no longer in use. |
| func (s Set) Iterator() *Iterator { |
| vals := s.Values() |
| |
| return &Iterator{ |
| vals: vals, |
| idx: -1, |
| } |
| } |
| |
| // EachValue calls the given callback once for each value in the set, in an |
| // undefined order that callers should not depend on. |
| func (s Set) EachValue(cb func(interface{})) { |
| it := s.Iterator() |
| for it.Next() { |
| cb(it.Value()) |
| } |
| } |
| |
| // Values returns a slice of all the values in the set. If the set rules have |
| // an order then the result is in that order. If no order is provided or if |
| // it is not a total order then the result order is undefined, but consistent |
| // for a particular set value within a specific release of cty. |
| func (s Set) Values() []interface{} { |
| var ret []interface{} |
| // Sort the bucketIds to ensure that we always traverse in a |
| // consistent order. |
| bucketIDs := make([]int, 0, len(s.vals)) |
| for id := range s.vals { |
| bucketIDs = append(bucketIDs, id) |
| } |
| sort.Ints(bucketIDs) |
| |
| for _, bucketID := range bucketIDs { |
| ret = append(ret, s.vals[bucketID]...) |
| } |
| |
| if orderRules, ok := s.rules.(OrderedRules); ok { |
| sort.SliceStable(ret, func(i, j int) bool { |
| return orderRules.Less(ret[i], ret[j]) |
| }) |
| } |
| |
| return ret |
| } |
| |
| // Length returns the number of values in the set. |
| func (s Set) Length() int { |
| var count int |
| for _, bucket := range s.vals { |
| count = count + len(bucket) |
| } |
| return count |
| } |
| |
| // Union returns a new set that contains all of the members of both the |
| // receiving set and the given set. Both sets must have the same rules, or |
| // else this function will panic. |
| func (s1 Set) Union(s2 Set) Set { |
| mustHaveSameRules(s1, s2) |
| rs := NewSet(s1.rules) |
| s1.EachValue(func(v interface{}) { |
| rs.Add(v) |
| }) |
| s2.EachValue(func(v interface{}) { |
| rs.Add(v) |
| }) |
| return rs |
| } |
| |
| // Intersection returns a new set that contains the values that both the |
| // receiver and given sets have in common. Both sets must have the same rules, |
| // or else this function will panic. |
| func (s1 Set) Intersection(s2 Set) Set { |
| mustHaveSameRules(s1, s2) |
| rs := NewSet(s1.rules) |
| s1.EachValue(func(v interface{}) { |
| if s2.Has(v) { |
| rs.Add(v) |
| } |
| }) |
| return rs |
| } |
| |
| // Subtract returns a new set that contains all of the values from the receiver |
| // that are not also in the given set. Both sets must have the same rules, |
| // or else this function will panic. |
| func (s1 Set) Subtract(s2 Set) Set { |
| mustHaveSameRules(s1, s2) |
| rs := NewSet(s1.rules) |
| s1.EachValue(func(v interface{}) { |
| if !s2.Has(v) { |
| rs.Add(v) |
| } |
| }) |
| return rs |
| } |
| |
| // SymmetricDifference returns a new set that contains all of the values from |
| // both the receiver and given sets, except those that both sets have in |
| // common. Both sets must have the same rules, or else this function will |
| // panic. |
| func (s1 Set) SymmetricDifference(s2 Set) Set { |
| mustHaveSameRules(s1, s2) |
| rs := NewSet(s1.rules) |
| s1.EachValue(func(v interface{}) { |
| if !s2.Has(v) { |
| rs.Add(v) |
| } |
| }) |
| s2.EachValue(func(v interface{}) { |
| if !s1.Has(v) { |
| rs.Add(v) |
| } |
| }) |
| return rs |
| } |