IpAddresses
IpAddresses
Section titled “IpAddresses”Canonical source:
protos/lib/network/IpAddresses.protosat locked revisione99b712ecf658a527b1fe82c8beba2c1286d8dc9.
// THE LICENSED WORK IS PROVIDED UNDER THE TERMS OF THE ADAPTIVE PUBLIC LICENSE// ("LICENSE") AS FIRST COMPLETED BY: Guillermo Adrián Molina. ANY USE, PUBLIC// DISPLAY, PUBLIC PERFORMANCE, REPRODUCTION OR DISTRIBUTION OF, OR PREPARATION OF// DERIVATIVE WORKS BASED ON, THE LICENSED WORK CONSTITUTES RECIPIENT'S ACCEPTANCE// OF THIS LICENSE AND ITS TERMS, WHETHER OR NOT SUCH RECIPIENT READS THE TERMS OF// THE LICENSE. "LICENSED WORK" AND "RECIPIENT" ARE DEFINED IN THE LICENSE. A COPY// OF THE LICENSE IS LOCATED IN THE TEXT FILE ENTITLED "LICENSE.TXT" ACCOMPANYING// THE CONTENTS OF THIS FILE. IF A COPY OF THE LICENSE DOES NOT ACCOMPANY THIS// FILE, A COPY OF THE LICENSE MAY ALSO BE OBTAINED AT THE FOLLOWING WEB SITE:// https://github.com/guillermomolina/protos//// Software distributed under the License is distributed on an "AS IS" basis,// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for// the specific language governing rights and limitations under the License.
//! Construction, parsing, and formatting of canonical `IpAddress` values; the//! `IpAddress` factory itself is installed by the runtime.
/// Answers the IPv4 `IpAddress` with octets `a.b.c.d`; each must be an Integer in/// 0..255.v4: (a, b, c, d) => { requireComponent: (value) => { // Validate the exact ordinary-Integer family without conversion. Integer.recognizes(value).ifFalse(() => { Error().signal() }) (value < 0).ifTrue(() => { Error().signal() }) (value > 255).ifTrue(() => { Error().signal() }) }
requireComponent(a) requireComponent(b) requireComponent(c) requireComponent(d)
bits: a bits = bits * 256 + b bits = bits * 256 + c bits = bits * 256 + d
IpAddress(4, bits)}
/// Answers the IPv6 `IpAddress` with hextets `a` through `h`; each must be an Integer/// in 0..65535.v6: (a, b, c, d, e, f, g, h) => { requireHextet: (value) => { // Validate the exact ordinary-Integer family without conversion or // delegation. Integer.recognizes(value).ifFalse(() => { Error().signal() }) (value < 0).ifTrue(() => { Error().signal() }) (value > 65535).ifTrue(() => { Error().signal() }) }
requireHextet(a) requireHextet(b) requireHextet(c) requireHextet(d) requireHextet(e) requireHextet(f) requireHextet(g) requireHextet(h)
bits: a bits = bits * 65536 + b bits = bits * 65536 + c bits = bits * 65536 + d bits = bits * 65536 + e bits = bits * 65536 + f bits = bits * 65536 + g bits = bits * 65536 + h
IpAddress(6, bits)}
/// Parses numeric IP address text: dotted-decimal IPv4 without leading zeros, or/// IPv6 hextets with optional `::` compression and a trailing dotted-decimal/// IPv4 part. Text containing `:` is parsed as IPv6. Invalid text signals an/// Error.parse: (text) => { // Validate the exact semantic String family without conversion. String.recognizes(text).ifFalse(() => { Error().signal() })
totalSize: text.size() (totalSize > 45).ifTrue(() => { fail() })
decimalDigitValue: (character) => { result: -1
(character == "0").ifTrue(() => { result = 0 }) (character == "1").ifTrue(() => { result = 1 }) (character == "2").ifTrue(() => { result = 2 }) (character == "3").ifTrue(() => { result = 3 }) (character == "4").ifTrue(() => { result = 4 }) (character == "5").ifTrue(() => { result = 5 }) (character == "6").ifTrue(() => { result = 6 }) (character == "7").ifTrue(() => { result = 7 }) (character == "8").ifTrue(() => { result = 8 }) (character == "9").ifTrue(() => { result = 9 })
result }
parseIpv4Components: (source) => { size: source.size() (size < 7).ifTrue(() => { fail() }) (size > 15).ifTrue(() => { fail() })
index: 0 octetIndex: 0 current: 0 digitCount: 0 leadingZero: false
a: null b: null c: null d: null
finishOctet: () => { (digitCount == 0).ifTrue(() => { fail() }) (current > 255).ifTrue(() => { fail() })
(octetIndex == 0).ifTrue(() => { a = current }) (octetIndex == 1).ifTrue(() => { b = current }) (octetIndex == 2).ifTrue(() => { c = current }) (octetIndex == 3).ifTrue(() => { d = current })
octetIndex = octetIndex + 1 current = 0 digitCount = 0 leadingZero = false }
(() => index < size).whileTrue() { character: source[index] isSeparator: character == "."
isSeparator.ifTrue(() => { (octetIndex >= 3).ifTrue(() => { fail() }) finishOctet() })
isSeparator.ifFalse(() => { digit: decimalDigitValue(character) (digit < 0).ifTrue(() => { fail() })
(digitCount == 0).ifTrue(() => { (digit == 0).ifTrue(() => { leadingZero = true }) })
(digitCount > 0).ifTrue(() => { leadingZero.ifTrue(() => { fail() }) })
digitCount = digitCount + 1 (digitCount > 3).ifTrue(() => { fail() })
current = current * 10 + digit (current > 255).ifTrue(() => { fail() }) })
index = index + 1 }
finishOctet()
(octetIndex == 4).ifFalse(() => { fail() })
Array(a, b, c, d) }
parseIpv4: (source) => { components: parseIpv4Components(source) v4(components[0], components[1], components[2], components[3]) }
hexDigitValue: (character) => { result: decimalDigitValue(character)
(result < 0).ifTrue(() => { (character == "a").ifTrue(() => { result = 10 }) (character == "b").ifTrue(() => { result = 11 }) (character == "c").ifTrue(() => { result = 12 }) (character == "d").ifTrue(() => { result = 13 }) (character == "e").ifTrue(() => { result = 14 }) (character == "f").ifTrue(() => { result = 15 })
(character == "A").ifTrue(() => { result = 10 }) (character == "B").ifTrue(() => { result = 11 }) (character == "C").ifTrue(() => { result = 12 }) (character == "D").ifTrue(() => { result = 13 }) (character == "E").ifTrue(() => { result = 14 }) (character == "F").ifTrue(() => { result = 15 }) })
result }
parseIpv6: (source) => { size: source.size() (size < 2).ifTrue(() => { fail() }) (size > 45).ifTrue(() => { fail() })
// Core Array has fixed indexed extent; it deliberately has no append // selector. IPv6 itself is bounded to eight hextets, so use an exact // eight-entry call-local workspace and an explicit logical count. hextets: Array(0, 0, 0, 0, 0, 0, 0, 0) hextetCount: 0 compressionIndex: -1 index: 0
appendHextet: (value) => { (hextetCount >= 8).ifTrue(() => { fail() }) hextets[hextetCount] = value hextetCount = hextetCount + 1 }
(source[0] == ":").ifTrue(() => { (size < 2).ifTrue(() => { fail() }) (source[1] == ":").ifFalse(() => { fail() }) compressionIndex = 0 index = 2 })
appendHexToken: (token) => { tokenSize: token.size() (tokenSize == 0).ifTrue(() => { fail() }) (tokenSize > 4).ifTrue(() => { fail() })
tokenIndex: 0 value: 0
(() => tokenIndex < tokenSize).whileTrue() { digit: hexDigitValue(token[tokenIndex]) (digit < 0).ifTrue(() => { fail() }) value = value * 16 + digit tokenIndex = tokenIndex + 1 }
appendHextet(value) }
(() => index < size).whileTrue() { token: "" containsDot: false
(() => { stillInside: index < size stillInside.ifTrue(() => { stillInside = source[index] != ":" }) stillInside }).whileTrue() { character: source[index] (character == ".").ifTrue(() => { containsDot = true }) token = token + character index = index + 1 }
(token.size() == 0).ifTrue(() => { fail() })
containsDot.ifTrue(() => { // An embedded dotted-decimal IPv4 component is legal only as // the final 32 bits of the IPv6 text. (index < size).ifTrue(() => { fail() })
components: parseIpv4Components(token) appendHextet(components[0] * 256 + components[1]) appendHextet(components[2] * 256 + components[3]) })
containsDot.ifFalse(() => { appendHexToken(token) })
(index < size).ifTrue(() => { // The token scan stopped on exactly one colon. index = index + 1
// A single trailing colon is never valid. (index == size).ifTrue(() => { fail() })
(source[index] == ":").ifTrue(() => { (compressionIndex >= 0).ifTrue(() => { fail() }) compressionIndex = hextetCount index = index + 1 }) }) }
count: hextetCount expanded: Array(0, 0, 0, 0, 0, 0, 0, 0) expandedCount: 0
appendExpanded: (value) => { (expandedCount >= 8).ifTrue(() => { fail() }) expanded[expandedCount] = value expandedCount = expandedCount + 1 }
(compressionIndex >= 0).ifTrue(() => { // `::` must stand for at least one zero hextet. (count >= 8).ifTrue(() => { fail() })
zeroCount: 8 - count sourceIndex: 0
(() => sourceIndex < count).whileTrue() { (sourceIndex == compressionIndex).ifTrue(() => { zerosAdded: 0 (() => zerosAdded < zeroCount).whileTrue() { appendExpanded(0) zerosAdded = zerosAdded + 1 } })
appendExpanded(hextets[sourceIndex]) sourceIndex = sourceIndex + 1 }
(compressionIndex == count).ifTrue(() => { zerosAdded: 0 (() => zerosAdded < zeroCount).whileTrue() { appendExpanded(0) zerosAdded = zerosAdded + 1 } }) })
(compressionIndex < 0).ifTrue(() => { (count == 8).ifFalse(() => { fail() })
sourceIndex: 0 (() => sourceIndex < count).whileTrue() { appendExpanded(hextets[sourceIndex]) sourceIndex = sourceIndex + 1 } })
(expandedCount == 8).ifFalse(() => { fail() })
v6( expanded[0], expanded[1], expanded[2], expanded[3], expanded[4], expanded[5], expanded[6], expanded[7] ) }
containsColon: false scanIndex: 0 (() => scanIndex < totalSize).whileTrue() { (text[scanIndex] == ":").ifTrue(() => { containsColon = true }) scanIndex = scanIndex + 1 }
result: null containsColon.ifTrue(() => { result = parseIpv6(text) }) containsColon.ifFalse(() => { result = parseIpv4(text) }) result}
/// Formats an `IpAddress`: IPv4 as dotted decimal, IPv6 as lowercase hextets/// with the leftmost longest run of two or more zero hextets compressed to `::`,/// and IPv4-mapped IPv6 as `::ffff:` followed by dotted decimal.format: (address) => { IpAddress.recognizes(address).ifFalse(() => { fail() })
decimalDigitText: (digit) => { result: null
(digit == 0).ifTrue(() => { result = "0" }) (digit == 1).ifTrue(() => { result = "1" }) (digit == 2).ifTrue(() => { result = "2" }) (digit == 3).ifTrue(() => { result = "3" }) (digit == 4).ifTrue(() => { result = "4" }) (digit == 5).ifTrue(() => { result = "5" }) (digit == 6).ifTrue(() => { result = "6" }) (digit == 7).ifTrue(() => { result = "7" }) (digit == 8).ifTrue(() => { result = "8" }) (digit == 9).ifTrue(() => { result = "9" })
(result === null).ifTrue(() => { fail() })
result }
octetText: (value) => { hundreds: value.div(100) rest: value.mod(100) tens: rest.div(10) ones: rest.mod(10)
result: ""
(hundreds > 0).ifTrue(() => { result = decimalDigitText(hundreds) + decimalDigitText(tens) + decimalDigitText(ones) })
(hundreds > 0).ifFalse(() => { (tens > 0).ifTrue(() => { result = decimalDigitText(tens) + decimalDigitText(ones) }) (tens > 0).ifFalse(() => { result = decimalDigitText(ones) }) })
result }
ipv4BitsText: (bits) => { a: bits.div(16777216) remainder: bits.mod(16777216)
b: remainder.div(65536) remainder = remainder.mod(65536)
c: remainder.div(256) d: remainder.mod(256)
octetText(a) + "." + octetText(b) + "." + octetText(c) + "." + octetText(d) }
hexDigitText: (digit) => { result: null
(digit == 0).ifTrue(() => { result = "0" }) (digit == 1).ifTrue(() => { result = "1" }) (digit == 2).ifTrue(() => { result = "2" }) (digit == 3).ifTrue(() => { result = "3" }) (digit == 4).ifTrue(() => { result = "4" }) (digit == 5).ifTrue(() => { result = "5" }) (digit == 6).ifTrue(() => { result = "6" }) (digit == 7).ifTrue(() => { result = "7" }) (digit == 8).ifTrue(() => { result = "8" }) (digit == 9).ifTrue(() => { result = "9" }) (digit == 10).ifTrue(() => { result = "a" }) (digit == 11).ifTrue(() => { result = "b" }) (digit == 12).ifTrue(() => { result = "c" }) (digit == 13).ifTrue(() => { result = "d" }) (digit == 14).ifTrue(() => { result = "e" }) (digit == 15).ifTrue(() => { result = "f" })
(result === null).ifTrue(() => { fail() })
result }
hextetText: (value) => { d0: value.div(4096) d1: value.div(256).mod(16) d2: value.div(16).mod(16) d3: value.mod(16)
result: "" started: false
(d0 > 0).ifTrue(() => { result = result + hexDigitText(d0) started = true })
started.ifTrue(() => { result = result + hexDigitText(d1) }) started.ifFalse(() => { (d1 > 0).ifTrue(() => { result = result + hexDigitText(d1) started = true }) })
started.ifTrue(() => { result = result + hexDigitText(d2) }) started.ifFalse(() => { (d2 > 0).ifTrue(() => { result = result + hexDigitText(d2) started = true }) })
result = result + hexDigitText(d3) result }
result: null
(address.version == 4).ifTrue(() => { result = ipv4BitsText(address.bits) })
(address.version == 6).ifTrue(() => { hextets: Array(0, 0, 0, 0, 0, 0, 0, 0) remaining: address.bits hextetIndex: 7
(() => hextetIndex >= 0).whileTrue() { hextets[hextetIndex] = remaining.mod(65536) remaining = remaining.div(65536) hextetIndex = hextetIndex - 1 }
mapped: true (hextets[0] == 0).ifFalse(() => { mapped = false }) (hextets[1] == 0).ifFalse(() => { mapped = false }) (hextets[2] == 0).ifFalse(() => { mapped = false }) (hextets[3] == 0).ifFalse(() => { mapped = false }) (hextets[4] == 0).ifFalse(() => { mapped = false }) (hextets[5] == 65535).ifFalse(() => { mapped = false })
mapped.ifTrue(() => { ipv4Bits: hextets[6] * 65536 + hextets[7] result = "::ffff:" + ipv4BitsText(ipv4Bits) })
mapped.ifFalse(() => { bestStart: -1 bestLength: 0 currentStart: -1 currentLength: 0
considerCurrentRun: () => { (currentLength >= 2).ifTrue(() => { (currentLength > bestLength).ifTrue(() => { bestStart = currentStart bestLength = currentLength }) }) }
scanIndex: 0 (() => scanIndex < 8).whileTrue() { (hextets[scanIndex] == 0).ifTrue(() => { (currentLength == 0).ifTrue(() => { currentStart = scanIndex }) currentLength = currentLength + 1 })
(hextets[scanIndex] == 0).ifFalse(() => { considerCurrentRun() currentStart = -1 currentLength = 0 })
scanIndex = scanIndex + 1 } considerCurrentRun()
text: "" needsColon: false emitIndex: 0
(() => emitIndex < 8).whileTrue() { compressed: false
(emitIndex == bestStart).ifTrue(() => { text = text + "::" emitIndex = emitIndex + bestLength needsColon = false compressed = true })
compressed.ifFalse(() => { needsColon.ifTrue(() => { text = text + ":" }) text = text + hextetText(hextets[emitIndex]) needsColon = true emitIndex = emitIndex + 1 }) }
result = text }) })
(result === null).ifTrue(() => { fail() })
result}