Skip to content

IpAddresses

Canonical source: protos/lib/network/IpAddresses.protos at locked revision e99b712ecf658a527b1fe82c8beba2c1286d8dc9.

// 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
}

← network family · Standard-library source