netaddr_setoid.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. /*
  2. * This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
  5. */
  6. package netaddr
  7. import (
  8. "fmt"
  9. "sort"
  10. "strings"
  11. )
  12. func insert(s []*NetAddr, i int, e *NetAddr) []*NetAddr {
  13. i = clamp(i, 0, len(s))
  14. s = append(s, e)
  15. copy(s[i+1:], s[i:])
  16. s[i] = e
  17. return s
  18. }
  19. func remove(s []*NetAddr, i int) []*NetAddr {
  20. i = clamp(i, 0, len(s)-1)
  21. copy(s[i:], s[i+1:])
  22. return s[:len(s)-1]
  23. }
  24. /*
  25. Create a new NetAddrSetoid.
  26. Pass an empty slice to create an empty setoid.
  27. */
  28. func NewNetAddrSetoid(ps []*NetAddr) *NetAddrSetoid {
  29. set := &NetAddrSetoid{make([]*NetAddr, 0, len(ps))}
  30. for _, p := range ps {
  31. set = set.Insert(p)
  32. }
  33. return set
  34. }
  35. /*
  36. NetAddrSetoid is a dynamic collection of NetAddrs.
  37. It is often advantageous to describe a set of addresses
  38. using the fewest prefixes possible. In essence, this is the
  39. smallest bitwise partition on the set. There is a sense in
  40. which this partition acts as a witness for an equivalence
  41. relation on the underlying set of addresses, and this set,
  42. together with said equivalence relation, is called a setoid.
  43. As addresses are added to and deleted from NetAddrSetoid,
  44. the implementation maintains a minimal set of prefixes. This
  45. effectively amortizes the work of efficiently extracting
  46. such a partition, post hoc.
  47. */
  48. type NetAddrSetoid struct {
  49. elems []*NetAddr
  50. }
  51. /* Clone returns a new (deep) copy of NetAddrSetoid. */
  52. func (s *NetAddrSetoid) Clone() *NetAddrSetoid {
  53. next := &NetAddrSetoid{make([]*NetAddr, len(s.elems))}
  54. for i, e := range s.elems {
  55. next.elems[i] = e.Clone()
  56. }
  57. return next
  58. }
  59. func (s *NetAddrSetoid) getIndex(
  60. na *NetAddr,
  61. equiv func(*NetAddr, *NetAddr) bool,
  62. ) int {
  63. return sort.Search(len(s.elems), func(i int) bool {
  64. ei := s.elems[i]
  65. return !LessThan(ei, na) || equiv(ei, na)
  66. })
  67. }
  68. func (s *NetAddrSetoid) findEquivalent(
  69. na *NetAddr,
  70. equiv func(*NetAddr, *NetAddr) bool,
  71. ) (found *NetAddr, err error) {
  72. i := s.getIndex(na, equiv)
  73. if i == len(s.elems) {
  74. err = fmt.Errorf("netaddr setoid: find equivalent: none found")
  75. return
  76. }
  77. ei := s.elems[i]
  78. if equiv(ei, na) {
  79. found = ei
  80. return
  81. }
  82. err = fmt.Errorf("netaddr setoid: find equivalent: none found")
  83. return
  84. }
  85. /*
  86. Blocks returns a slice of NetAddrs representing the minimum
  87. set of prefixes that contains the underlying set of
  88. addresses. This is just the quotient set of the setoid.
  89. The returned slice is the result of a deep copy.
  90. */
  91. func (s *NetAddrSetoid) Blocks() []*NetAddr {
  92. c := s.Clone()
  93. return c.elems
  94. }
  95. /*
  96. Cardinality returns the number of prefixes (blocks) in the
  97. setoid's quotient set.
  98. */
  99. func (s *NetAddrSetoid) Cardinality() int {
  100. return len(s.elems)
  101. }
  102. /*
  103. String returns a string representation of the prefixes
  104. (blocks) in the setoid's quotient set.
  105. */
  106. func (s *NetAddrSetoid) String() string {
  107. var b strings.Builder
  108. b.WriteString("{")
  109. for _, e := range s.elems {
  110. fmt.Fprintf(&b, "%s,", e.String())
  111. }
  112. return strings.TrimSuffix(b.String(), ",") + "}"
  113. }
  114. /*
  115. Insert adds the set of addresses represented by na to
  116. the setoid.
  117. */
  118. func (s *NetAddrSetoid) Insert(
  119. na *NetAddr,
  120. ) *NetAddrSetoid {
  121. /* na may be one of
  122. * disjoint, left contiguous
  123. * disjoint, right contiguous
  124. * subset
  125. * proper superset
  126. * disjoint, discontiguous
  127. but na will never be a cover of two, four, etc.
  128. contiguous prefixes, as these will already have been
  129. coalesced into a single prefix.
  130. */
  131. idx := s.getIndex(na, NetAddrContains)
  132. if idx == len(s.elems) { // disjoint
  133. s.elems = append(s.elems, na.FirstAddress())
  134. }
  135. for {
  136. cur := s.elems[idx]
  137. if 0 <= idx-1 {
  138. left := s.elems[idx-1]
  139. if Contiguous(left, cur) &&
  140. LeftAdjacent(left, cur) { // left contiguous
  141. s.elems[idx-1] =
  142. left.
  143. SetLength(left.Length() - 1).
  144. FirstAddress()
  145. s.elems = remove(s.elems, idx)
  146. idx-- // cur is gone: cur <- left
  147. continue
  148. }
  149. }
  150. if idx+1 < len(s.elems) {
  151. right := s.elems[idx+1]
  152. if Contiguous(cur, right) &&
  153. LeftAdjacent(cur, right) { // right contiguous
  154. s.elems[idx] =
  155. cur.
  156. SetLength(cur.Length() - 1).
  157. FirstAddress()
  158. s.elems = remove(s.elems, idx+1)
  159. // cur is unchanged: cur <- cur
  160. continue
  161. }
  162. }
  163. if NetAddrContains(cur, na) { // subset
  164. return s
  165. }
  166. // Since the above `contains` was false, na < e[i].
  167. // But then e[i-1] <~ na; check whether ~ or <.
  168. if 0 <= idx-1 {
  169. left := s.elems[idx-1]
  170. if NetAddrContains(na, left) { // proper superset
  171. s.elems = remove(s.elems, idx-1)
  172. idx-- // cur has moved: cur <- cur
  173. continue
  174. }
  175. }
  176. // disjoint, discontiguous
  177. s.elems = insert(s.elems, idx, na)
  178. }
  179. }
  180. /*
  181. Delete removes the set of addresses represented by na
  182. from the setoid.
  183. */
  184. func (s *NetAddrSetoid) Delete(
  185. na *NetAddr,
  186. ) *NetAddrSetoid {
  187. idx := s.getIndex(na, NetAddrContains)
  188. if idx == len(s.elems) {
  189. return s
  190. }
  191. for na.Contains(s.elems[idx]) {
  192. s.elems = remove(s.elems, idx)
  193. }
  194. next := s.elems[idx]
  195. for next.Contains(na) {
  196. l, r := Split(next)
  197. if l.IsEqual(na) {
  198. s.elems[idx] = r
  199. return s
  200. }
  201. s.elems[idx] = l
  202. if r.IsEqual(na) {
  203. return s
  204. }
  205. s.elems = insert(s.elems, idx+1, r)
  206. switch {
  207. case l.Contains(na):
  208. next = l
  209. case r.Contains(na):
  210. next = r
  211. idx++
  212. }
  213. }
  214. return s
  215. }
  216. /*
  217. FindSuperset returns a prefix wholly containing the given
  218. NetAddr. If none can be found, an error is provided.
  219. */
  220. func (s *NetAddrSetoid) FindSuperset(
  221. na *NetAddr,
  222. ) (*NetAddr, error) {
  223. return s.findEquivalent(na, NetAddrContains)
  224. }
  225. /*
  226. Includes returns true when the setoid contains all of the
  227. given NetAddr.
  228. */
  229. func (s *NetAddrSetoid) Includes(na *NetAddr) bool {
  230. _, err := s.FindSuperset(na)
  231. return err != nil
  232. }
  233. /*
  234. FindOverlapping returns the largest (shortest) prefix that
  235. contains, or is contained by, the given NetAddr.
  236. */
  237. func (s *NetAddrSetoid) FindOverlapping(
  238. na *NetAddr,
  239. ) (*NetAddr, error) {
  240. return s.findEquivalent(na, NetAddrOverlaps)
  241. }
  242. /*
  243. Overlaps returns true when the setoid and the given NetAddr
  244. have addresses in common.
  245. */
  246. func (s *NetAddrSetoid) Overlaps(na *NetAddr) bool {
  247. _, err := s.FindOverlapping(na)
  248. return err != nil
  249. }
  250. /*
  251. IsEqual returns true if both setoids contain precisely the
  252. same addresses.
  253. */
  254. func (s1 *NetAddrSetoid) IsEqual(
  255. s2 *NetAddrSetoid,
  256. ) bool {
  257. if s1.Cardinality() != s2.Cardinality() {
  258. return false
  259. }
  260. for i := 0; i < len(s1.elems); i++ {
  261. if !NetAddrIsEqual(s1.elems[i], s2.elems[i]) {
  262. return false
  263. }
  264. }
  265. return true
  266. }
  267. /*
  268. Union returns the set union of both setoids' underlying
  269. sets.
  270. The resulting setoid is a deep copy.
  271. */
  272. func Union(s1, s2 *NetAddrSetoid) *NetAddrSetoid {
  273. next := s1.Clone()
  274. for _, e := range s2.elems {
  275. next.Insert(e)
  276. }
  277. return next
  278. }
  279. /*
  280. RelComp returns a setoid whose underlying set is the set
  281. difference s1 - s2.
  282. The resulting setoid is a deep copy.
  283. */
  284. func RelComp(s1, s2 *NetAddrSetoid) *NetAddrSetoid {
  285. next := s1.Clone()
  286. for _, e := range s2.elems {
  287. next.Delete(e)
  288. }
  289. return next
  290. }