prefix.go 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  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 ipplot
  7. import (
  8. "errors"
  9. "fmt"
  10. "math"
  11. )
  12. const (
  13. ipv4ByteSize = 4
  14. )
  15. func lessThan(o1, m1, o2, m2 byte) bool {
  16. /*
  17. We seek an isometry from a binary trie embedded in R²
  18. to the integral number line, with ones bits branching to
  19. the right and zeros bits branching to the left. When
  20. considering only the ones bits of an n-bit binary
  21. number, the usual decimal encoding suffices, as each ith
  22. bit, for 0 <= i <= n-1, shifts a point at the origin to
  23. the right by 2^i. However, the zeros bits have no
  24. effect, in this sense, as leading zeros do not
  25. contribute to the integer value, resulting in many
  26. binary strings occupying the same point. This mapping is
  27. well-formed and surjective (every integer has a binary
  28. encoding), but it is not injective.
  29. -> R 0 00 000 0000
  30. -> 0001
  31. -> 001 0010
  32. -> 0011
  33. -> 01 010 0100
  34. -> 0101
  35. -> 011 0110
  36. -> 0111
  37. -> 1 10 100 1000
  38. -> 1001
  39. -> 101 1010
  40. -> 1011
  41. -> 11 110 1100
  42. -> 1101
  43. -> 111 1110
  44. -> 1111
  45. Instead, by allowing each zero bit to provide a negative
  46. contribution, we ensure that every path (binary string)
  47. maps to a unique point on the integral line. To see that
  48. this is an isometry, project the nodes of a binary trie
  49. embedded in R², with branch width w = 2^(n-j-1) at rank
  50. j and arbitrary branch height h, onto the integral line.
  51. Here, we express the above isometry by simply
  52. subtracting the contributions of the place values of the
  53. zeros bits, which we obtain by ones complement (XOR).
  54. Masking ensures we map only the prefix.
  55. Finally, we note that projecting the usual geometry of a
  56. binary trie onto the integral line places each ancestor
  57. at the midpoint of its descendants. This creates a
  58. disagreement between the partial and total orders, which
  59. makes it difficult to recover the original partial order
  60. generated by the trie.
  61. -> 0000
  62. -> 000
  63. -> 0001
  64. -> 00
  65. -> 0010
  66. -> 001
  67. -> 0011
  68. -> 0
  69. -> 0100
  70. -> 010
  71. -> 0101
  72. -> 01
  73. -> 0110
  74. -> 011
  75. -> 0111
  76. 0/0
  77. -> 1000
  78. -> 100
  79. -> 1001
  80. -> 10
  81. -> 1010
  82. -> 101
  83. -> 1011
  84. -> 1
  85. -> 1100
  86. -> 110
  87. -> 1101
  88. -> 11
  89. -> 1110
  90. -> 111
  91. -> 1111
  92. Instead, we require that the total order < given by the
  93. proposed isometry have the property that it always agree
  94. with the partial order ≪ given by the binary trie. That
  95. is, given nodes a, b of a binary trie, if a ≪ b, then
  96. a < b. Note that the converse does not hold, nor would
  97. it be useful if it did.
  98. To accomplish this, we effectively apply a horizontal
  99. shear to the right on the binary trie such that the root
  100. of each subtree falls to the right of its descendants.
  101. -> 0000
  102. -> 0001
  103. -> 000
  104. -> 0010
  105. -> 0011
  106. -> 001
  107. -> 00
  108. -> 0100
  109. -> 0101
  110. -> 010
  111. -> 0110
  112. -> 0111
  113. -> 011
  114. -> 01
  115. -> 0
  116. -> 1000
  117. -> 1001
  118. -> 100
  119. -> 1010
  120. -> 1011
  121. -> 101
  122. -> 10
  123. -> 1100
  124. -> 1101
  125. -> 110
  126. -> 1110
  127. -> 1111
  128. -> 111
  129. -> 11
  130. -> 1
  131. 0/0
  132. In the unsheared isometry, the nodes we wish to reorder
  133. are specifically those which precede their descendants--
  134. that is, whenever a < b and b ≪ a, we would like to
  135. move a to the right of b so that b < a, thereby agreeing
  136. with the partial order. Since b ≪ a, it is the case
  137. that a represents a shorter prefix than b. Furthermore,
  138. since b is a descendant of a, b's prefix is guaranteed
  139. to share all of the bits of a. Conversely, if a and b
  140. share a prefix, then that prefix represents the most
  141. recent shared ancestor (least upper bound). And if the
  142. prefix shared by a and b is a's prefix, then it must be
  143. the case that a's prefix is shorter, and b is a
  144. descendant of a. Thus, b ≪ a.
  145. Therefore, we conclude that a must be reordered
  146. precisely when a < b and b shares a's prefix; hence, our
  147. assertion `f1 < f2 && o1 != o2&m1`. To reorder, we
  148. simply place prefixes with longer masks before prefixes
  149. with shorter ones, as given by `m1 > m2`.
  150. */
  151. f1 := int(o1&m1) - int((o1^byte(255))&m1)
  152. f2 := int(o2&m2) - int((o2^byte(255))&m2)
  153. return (f1 < f2 &&
  154. (o1 != o2&m1 || // assume o2/m2 is not a descendant of o1/m1
  155. m1 > m2)) // otherwise, sort by length, descending
  156. }
  157. func lessThanOrEq(p1, p2 IPPrefix) bool {
  158. a1, a2 := p1.Prefix(), p2.Prefix()
  159. m1, m2 := p1.Mask(), p2.Mask()
  160. for i := 0; i < len(a1); i++ {
  161. if lessThan(a1[i], m1[i], a2[i], m2[i]) {
  162. return true
  163. }
  164. // !(p1<=p2) <=> p1>p2 <=> p2<p1
  165. if lessThan(a2[i], m2[i], a1[i], m1[i]) {
  166. return false
  167. }
  168. }
  169. return true
  170. }
  171. type IPPrefix interface {
  172. Mask() []byte
  173. Length() int
  174. LessThanOrEq(IPPrefix) bool
  175. Prefix() []byte
  176. String() string
  177. ToGeometry() (uint64, uint64, uint64, uint64)
  178. }
  179. var maskTable = &[9]byte{0, 128, 192, 224, 240, 248, 252, 254, 255}
  180. func lengthToMask(byteSz, l int) (mask []byte) {
  181. mask = make([]byte, byteSz)
  182. for i := 0; i < len(mask); i++ {
  183. rem := l - 8*(i+1)
  184. if rem < 0 {
  185. if rem != -8 {
  186. mask[i] = maskTable[rem+8]
  187. }
  188. break
  189. }
  190. mask[i] = maskTable[8]
  191. }
  192. return
  193. }
  194. type IPv6Prefix struct {
  195. addr [16]byte
  196. length int
  197. }
  198. type IPv4Prefix struct {
  199. addr [4]byte
  200. length int
  201. }
  202. func (p1 IPv4Prefix) Mask() []byte {
  203. return lengthToMask(len(p1.Prefix()), p1.Length())
  204. }
  205. func (p1 IPv4Prefix) LessThanOrEq(p2 IPPrefix) bool {
  206. if p2, ok := p2.(IPv4Prefix); ok {
  207. return lessThanOrEq(p1, p2)
  208. }
  209. return false
  210. }
  211. func (p IPv4Prefix) Prefix() []byte {
  212. return p.addr[:]
  213. }
  214. func (p IPv4Prefix) Length() int {
  215. return p.length
  216. }
  217. func (p IPv4Prefix) String() string {
  218. return fmt.Sprintf(
  219. "%d.%d.%d.%d/%d",
  220. p.addr[0],
  221. p.addr[1],
  222. p.addr[2],
  223. p.addr[3],
  224. p.length,
  225. )
  226. }
  227. /*
  228. "Unzip" bits from least- to most-significant, making the
  229. first group the "x" coordinate, and the second group, the
  230. "y" coordinate. This is sometimes called *Morton encoding*.
  231. See https://en.wikipedia.org/wiki/Z-order_curve for details.
  232. abcdefgh bdfh aceg
  233. 10110001 -> (0101,1100)
  234. This transformation exhibits a weak form of continuity,
  235. where sufficiently small changes in the prefix result in
  236. similarly small changes in the point (x, y), and vice versa.
  237. */
  238. func (p IPv4Prefix) ToGeometry() (x, y, w, h uint64) {
  239. for i, b := range p.addr {
  240. x += uint64(((1 << 0) & b) >> 0)
  241. y += uint64(((1 << 1) & b) >> 1)
  242. x += uint64(((1 << 2) & b) >> 1)
  243. y += uint64(((1 << 3) & b) >> 2)
  244. x += uint64(((1 << 4) & b) >> 2)
  245. y += uint64(((1 << 5) & b) >> 3)
  246. x += uint64(((1 << 6) & b) >> 3)
  247. y += uint64(((1 << 7) & b) >> 4)
  248. if i < 3 { // Make room for the next four bits
  249. x <<= 4
  250. y <<= 4
  251. }
  252. }
  253. l := p.length
  254. w = uint64(math.Exp2(math.Ceil(float64(32-l) / 2)))
  255. h = uint64(math.Exp2(math.Floor(float64(32-l) / 2)))
  256. return
  257. }
  258. /*
  259. Inverse of ToGeometry().
  260. bdfh aceg abcdefgh
  261. (0101,1100) -> 10110001
  262. */
  263. func IPv4FromGeometry(x, y, w, h uint64) IPv4Prefix {
  264. p := [4]byte{}
  265. for i := 3; i >= 0; i-- {
  266. p[i] |= byte(((1 << 0) & x) << 0)
  267. p[i] |= byte(((1 << 0) & y) << 1)
  268. p[i] |= byte(((1 << 1) & x) << 1)
  269. p[i] |= byte(((1 << 1) & y) << 2)
  270. p[i] |= byte(((1 << 2) & x) << 2)
  271. p[i] |= byte(((1 << 2) & y) << 3)
  272. p[i] |= byte(((1 << 3) & x) << 3)
  273. p[i] |= byte(((1 << 3) & y) << 4)
  274. x >>= 4
  275. y >>= 4
  276. }
  277. l := 32 - 2*math.Log2(float64(w))
  278. if w != h {
  279. l += 1
  280. }
  281. return IPv4Prefix{addr: p, length: int(l)}
  282. }
  283. func NewIPv4(
  284. addr []byte,
  285. length int,
  286. ) (IPv4Prefix, error) {
  287. var newPrefix IPv4Prefix
  288. if len(addr) != 4 {
  289. return newPrefix, errors.New("bad ipv4 prefix")
  290. }
  291. if length < 0 || length > 32 {
  292. return newPrefix, errors.New("bad ipv4 prefix length")
  293. }
  294. copy(newPrefix.addr[:], addr[0:4])
  295. newPrefix.length = length
  296. return newPrefix, nil
  297. }