Added int array for types, added truncate

This commit is contained in:
Robinson 2021-04-08 19:59:25 +02:00
parent 726e4dbf21
commit d5ceece82f
2 changed files with 449 additions and 36 deletions

View File

@ -131,14 +131,25 @@ object IPv4 {
} }
} }
private val SLASH_REGEX = "\\.".toRegex() /**
* the length of an address in this particular family.
*/
val length = 4
private val SLASH_REGEX = "\\.".toRegex()
private val private10 = toInt("10.0.0.0") private val private10 = toInt("10.0.0.0")
private val private172 = toInt("172.16.0.0") private val private172 = toInt("172.16.0.0")
private val private192 = toInt("192.168.0.0") private val private192 = toInt("192.168.0.0")
private val loopback127 = toInt("127.0.0.0") private val loopback127 = toInt("127.0.0.0")
/**
* @return if the specified address is of this family type
*/
fun isFamily(address: InetAddress) : Boolean {
return address is Inet4Address
}
/** /**
* Determines if this IP address is a private address or not. * Determines if this IP address is a private address or not.
* *
@ -295,7 +306,7 @@ object IPv4 {
} }
/** /**
* Creates an byte[] based on an ipAddressString. No error handling is performed here. * Creates an byte[] based on an ip Address String. No error handling is performed here.
*/ */
fun toBytes(ip: String): ByteArray { fun toBytes(ip: String): ByteArray {
var i: Int var i: Int
@ -315,6 +326,27 @@ object IPv4 {
return byteArrayOf(a, b, c, ipv4WordToByte(ip, i + 1, ip.length)) return byteArrayOf(a, b, c, ipv4WordToByte(ip, i + 1, ip.length))
} }
/**
* Creates an int[] based on an ip address String. No error handling is performed here.
*/
fun toInts(ip: String): IntArray {
var i: Int
var index = ip.indexOf('.', 1)
val a = ipv4WordToInt(ip, 0, index)
i = index
index = ip.indexOf('.', index + 2)
val b = ipv4WordToInt(ip, i + 1, index)
i = index
index = ip.indexOf('.', index + 2)
val c = ipv4WordToInt(ip, i + 1, index)
i = index
return intArrayOf(a, b, c, ipv4WordToInt(ip, i + 1, ip.length))
}
/** /**
* Creates an Inet4Address based on an ip string. No error handling is performed here. * Creates an Inet4Address based on an ip string. No error handling is performed here.
*/ */
@ -338,20 +370,27 @@ object IPv4 {
} }
private fun ipv4WordToByte(ip: CharSequence, from: Int, toExclusive: Int): Byte { private fun ipv4WordToByte(ip: CharSequence, from: Int, toExclusive: Int): Byte {
return ipv4WordToInt(ip, from, toExclusive).toByte()
}
private fun ipv4WordToInt(ip: CharSequence, from: Int, toExclusive: Int): Int {
var newFrom = from var newFrom = from
var ret = decimalDigit(ip, newFrom) var ret = decimalDigit(ip, newFrom)
newFrom++ newFrom++
if (newFrom == toExclusive) { if (newFrom == toExclusive) {
return ret.toByte() return ret
} }
ret = ret * 10 + decimalDigit(ip, newFrom) ret = ret * 10 + decimalDigit(ip, newFrom)
newFrom++ newFrom++
return if (newFrom == toExclusive) { return if (newFrom == toExclusive) {
ret.toByte() ret
} else (ret * 10 + decimalDigit(ip, newFrom)).toByte() } else {
ret * 10 + decimalDigit(ip, newFrom)
}
} }
/** /**
@ -699,6 +738,9 @@ object IPv4 {
} }
/**
* Converts a byte array into an 32-bit integer
*/
fun toInt(ipBytes: ByteArray): Int { fun toInt(ipBytes: ByteArray): Int {
return ipBytes[0].toInt() shl 24 or return ipBytes[0].toInt() shl 24 or
(ipBytes[1].toInt() shl 16) or (ipBytes[1].toInt() shl 16) or
@ -707,10 +749,18 @@ object IPv4 {
} }
/** /**
* Converts a 32-bit integer into an IPv4 address. * Converts a 32-bit integer into a dotted-quad IPv4 address.
*/ */
fun toString(ipAddress: Int): String { fun toString(ipAddress: Int): String {
val buf = StringBuilder(15) val buf = StringBuilder(15)
toString(ipAddress, buf)
return buf.toString()
}
/**
* Converts a 32-bit integer into a dotted-quad IPv4 address.
*/
fun toString(ipAddress: Int, buf: StringBuilder) {
buf.append(ipAddress shr 24 and 0xFF) buf.append(ipAddress shr 24 and 0xFF)
buf.append('.') buf.append('.')
buf.append(ipAddress shr 16 and 0xFF) buf.append(ipAddress shr 16 and 0xFF)
@ -718,11 +768,21 @@ object IPv4 {
buf.append(ipAddress shr 8 and 0xFF) buf.append(ipAddress shr 8 and 0xFF)
buf.append('.') buf.append('.')
buf.append(ipAddress and 0xFF) buf.append(ipAddress and 0xFF)
}
/**
* Converts a byte array into a dotted-quad IPv4 address.
*/
fun toString(ipBytes: ByteArray): String {
val buf = StringBuilder(15)
toString(ipBytes, buf)
return buf.toString() return buf.toString()
} }
fun toString(ipBytes: ByteArray): String { /**
val buf = StringBuilder(15) * Converts a byte array into a dotted-quad IPv4 address.
*/
fun toString(ipBytes: ByteArray, buf: StringBuilder) {
buf.append(ipBytes[0].toInt() and 0xFF) buf.append(ipBytes[0].toInt() and 0xFF)
buf.append('.') buf.append('.')
buf.append(ipBytes[1].toInt() and 0xFF) buf.append(ipBytes[1].toInt() and 0xFF)
@ -730,9 +790,30 @@ object IPv4 {
buf.append(ipBytes[2].toInt() and 0xFF) buf.append(ipBytes[2].toInt() and 0xFF)
buf.append('.') buf.append('.')
buf.append(ipBytes[3].toInt() and 0xFF) buf.append(ipBytes[3].toInt() and 0xFF)
}
/**
* Converts an int array into a dotted-quad IPv4 address.
*/
fun toString(ipInts: IntArray): String {
val buf = StringBuilder(15)
toString(ipInts, buf)
return buf.toString() return buf.toString()
} }
/**
* Converts an int array into a dotted-quad IPv4 address.
*/
fun toString(ipInts: IntArray, buf: StringBuilder) {
buf.append(ipInts[0])
buf.append('.')
buf.append(ipInts[1])
buf.append('.')
buf.append(ipInts[2])
buf.append('.')
buf.append(ipInts[3])
}
/** /**
* Returns the [String] representation of an [InetAddress]. Results are identical to [InetAddress.getHostAddress] * Returns the [String] representation of an [InetAddress]. Results are identical to [InetAddress.getHostAddress]
* *
@ -823,4 +904,40 @@ object IPv4 {
null null
} }
} }
/**
* Truncates an address to the specified number of bits. For example,
* truncating the address 10.1.2.3 to 8 bits would yield 10.0.0.0.
*
* @param address The source address
* @param maskLength The number of bits to truncate the address to.
*/
fun truncate(address: Inet4Address, maskLength: Int): InetAddress? {
val maxMaskLength = IPv6.length * 8
require(!(maskLength < 0 || maskLength > maxMaskLength)) { "invalid mask length" }
if (maskLength == maxMaskLength) {
return address
}
val bytes = address.address
for (i in maskLength / 8 + 1 until bytes.size) {
bytes[i] = 0
}
val maskBits = maskLength % 8
var bitmask = 0
for (i in 0 until maskBits) {
bitmask = bitmask or (1 shl 7 - i)
}
bytes[maskLength / 8] = (bytes[maskLength / 8].toInt() and bitmask).toByte()
return try {
InetAddress.getByAddress(bytes)
} catch (e: UnknownHostException) {
throw IllegalArgumentException("invalid address")
}
}
} }

View File

@ -138,6 +138,18 @@ object IPv6 {
InetAddress.getByAddress(null, byteArrayOf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) as Inet6Address InetAddress.getByAddress(null, byteArrayOf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) as Inet6Address
} }
/**
* the length of an address in this particular family.
*/
val length = 16
/**
* @return if the specified address is of this family type
*/
fun isFamily(address: InetAddress) : Boolean {
return address is Inet6Address
}
/** /**
* Takes a [String] and parses it to see if it is a valid IPV6 address. * Takes a [String] and parses it to see if it is a valid IPV6 address.
* *
@ -573,7 +585,6 @@ object IPv6 {
* [rfc 5952 section 4](http://tools.ietf.org/html/rfc5952#section-4) * [rfc 5952 section 4](http://tools.ietf.org/html/rfc5952#section-4)
* *
* *
*
* The output does not include Scope ID. * The output does not include Scope ID.
* *
* @param bytes [InetAddress] to be converted to an address string * @param bytes [InetAddress] to be converted to an address string
@ -596,15 +607,54 @@ object IPv6 {
* [rfc 5952 section 4](http://tools.ietf.org/html/rfc5952#section-4) * [rfc 5952 section 4](http://tools.ietf.org/html/rfc5952#section-4)
* *
* *
* The output does not include Scope ID.
*
* @param ints [InetAddress] to be converted to an address string
*
* @return `String` containing the text-formatted IP address
*/
fun toString(ints: IntArray): String {
return toString(ints, 0)
}
/**
* Returns the [String] representation of an [InetAddress].
*
* * Inet4Address results are identical to [InetAddress.getHostAddress]
* * Inet6Address results adhere to
* [rfc 5952 section 4](http://tools.ietf.org/html/rfc5952#section-4) if
* `ipv4Mapped` is false. If `ipv4Mapped` is true then "IPv4 mapped" format
* from [rfc 4291 section 2](http://tools.ietf.org/html/rfc4291#section-2.5.5) will be supported.
* The compressed result will always obey the compression rules defined in
* [rfc 5952 section 4](http://tools.ietf.org/html/rfc5952#section-4)
* *
* The output does not include Scope ID. * The output does not include Scope ID.
* *
* @param bytes [InetAddress] to be converted to an address string * @param bytes [InetAddress] to be converted to an address string
* * @param buf the StringBuilder to build the address into
* @return `String` containing the text-formatted IP address
*/ */
fun toString(bytes: ByteArray, offset: Int): String { fun toString(bytes: ByteArray, buf: StringBuilder) {
return toAddressString(bytes, offset, false) toString(bytes, 0, buf)
}
/**
* Returns the [String] representation of an [InetAddress].
*
* * Inet4Address results are identical to [InetAddress.getHostAddress]
* * Inet6Address results adhere to
* [rfc 5952 section 4](http://tools.ietf.org/html/rfc5952#section-4) if
* `ipv4Mapped` is false. If `ipv4Mapped` is true then "IPv4 mapped" format
* from [rfc 4291 section 2](http://tools.ietf.org/html/rfc4291#section-2.5.5) will be supported.
* The compressed result will always obey the compression rules defined in
* [rfc 5952 section 4](http://tools.ietf.org/html/rfc5952#section-4)
*
* The output does not include Scope ID.
*
* @param ints [InetAddress] to be converted to an address string
* @param buf the StringBuilder to build the address into
*/
fun toString(ints: IntArray, buf: StringBuilder) {
toString(ints, 0, buf)
} }
/** /**
@ -619,26 +669,141 @@ object IPv6 {
* [rfc 5952 section 4](http://tools.ietf.org/html/rfc5952#section-4) * [rfc 5952 section 4](http://tools.ietf.org/html/rfc5952#section-4)
* *
* *
* The output does not include Scope ID.
*
* @param bytes [InetAddress] to be converted to an address string
*
* @return `String` containing the text-formatted IP address
*/
fun toString(bytes: ByteArray, offset: Int): String {
val buf = StringBuilder(IPV6_MAX_CHAR_COUNT)
toAddressString(bytes, offset, false, buf)
return buf.toString()
}
/**
* Returns the [String] representation of an [InetAddress].
*
* * Inet4Address results are identical to [InetAddress.getHostAddress]
* * Inet6Address results adhere to
* [rfc 5952 section 4](http://tools.ietf.org/html/rfc5952#section-4) if
* `ipv4Mapped` is false. If `ipv4Mapped` is true then "IPv4 mapped" format
* from [rfc 4291 section 2](http://tools.ietf.org/html/rfc4291#section-2.5.5) will be supported.
* The compressed result will always obey the compression rules defined in
* [rfc 5952 section 4](http://tools.ietf.org/html/rfc5952#section-4)
*
*
* The output does not include Scope ID.
*
* @param ints [InetAddress] to be converted to an address string
*
* @return `String` containing the text-formatted IP address
*/
fun toString(ints: IntArray, offset: Int): String {
val buf = StringBuilder(IPV6_MAX_CHAR_COUNT)
toAddressString(ints, offset, false, buf)
return buf.toString()
}
/**
* Returns the [String] representation of an [InetAddress].
*
* * Inet4Address results are identical to [InetAddress.getHostAddress]
* * Inet6Address results adhere to
* [rfc 5952 section 4](http://tools.ietf.org/html/rfc5952#section-4) if
* `ipv4Mapped` is false. If `ipv4Mapped` is true then "IPv4 mapped" format
* from [rfc 4291 section 2](http://tools.ietf.org/html/rfc4291#section-2.5.5) will be supported.
* The compressed result will always obey the compression rules defined in
* [rfc 5952 section 4](http://tools.ietf.org/html/rfc5952#section-4)
*
*
* The output does not include Scope ID.
*
* @param bytes [InetAddress] to be converted to an address string
* @param offset the offset into the array to start
* @param buf the StringBuilder to build the address into
*/
fun toString(bytes: ByteArray, offset: Int, buf: StringBuilder) {
toAddressString(bytes, offset, false, buf)
}
/**
* Returns the [String] representation of an [InetAddress].
*
* * Inet4Address results are identical to [InetAddress.getHostAddress]
* * Inet6Address results adhere to
* [rfc 5952 section 4](http://tools.ietf.org/html/rfc5952#section-4) if
* `ipv4Mapped` is false. If `ipv4Mapped` is true then "IPv4 mapped" format
* from [rfc 4291 section 2](http://tools.ietf.org/html/rfc4291#section-2.5.5) will be supported.
* The compressed result will always obey the compression rules defined in
* [rfc 5952 section 4](http://tools.ietf.org/html/rfc5952#section-4)
*
*
* The output does not include Scope ID.
*
* @param ints [InetAddress] to be converted to an address string
* @param offset the offset into the array to start
* @param buf the StringBuilder to build the address into
*/
fun toString(ints: IntArray, offset: Int, buf: StringBuilder) {
toAddressString(ints, offset, false, buf)
}
/**
* Returns the [String] representation of an [InetAddress].
*
* * Inet4Address results are identical to [InetAddress.getHostAddress]
* * Inet6Address results adhere to
* [rfc 5952 section 4](http://tools.ietf.org/html/rfc5952#section-4) if
* `ipv4Mapped` is false. If `ipv4Mapped` is true then "IPv4 mapped" format
* from [rfc 4291 section 2](http://tools.ietf.org/html/rfc4291#section-2.5.5) will be supported.
* The compressed result will always obey the compression rules defined in
* [rfc 5952 section 4](http://tools.ietf.org/html/rfc5952#section-4)
*
* *
* The output does not include Scope ID. * The output does not include Scope ID.
* *
* @param ip [InetAddress] to be converted to an address string * @param ip [InetAddress] to be converted to an address string
* @param ipv4Mapped * @param ipv4Mapped * `true` to stray from strict rfc 5952 and support the "IPv4 mapped" format
*
* * `true` to stray from strict rfc 5952 and support the "IPv4 mapped" format
* defined in [rfc 4291 section 2](http://tools.ietf.org/html/rfc4291#section-2.5.5) while still * defined in [rfc 4291 section 2](http://tools.ietf.org/html/rfc4291#section-2.5.5) while still
* following the updated guidelines in * following the updated guidelines in [rfc 5952 section 4](http://tools.ietf.org/html/rfc5952#section-4)
* [rfc 5952 section 4](http://tools.ietf.org/html/rfc5952#section-4) * `false` to strictly follow rfc 5952
*
* * `false` to strictly follow rfc 5952
* *
* @return `String` containing the text-formatted IP address * @return `String` containing the text-formatted IP address
*/ */
fun toString(ip: Inet6Address, ipv4Mapped: Boolean = false): String { fun toString(ip: Inet6Address, ipv4Mapped: Boolean = false): String {
return toAddressString(ip.address, 0, ipv4Mapped) val buf = StringBuilder(IPV6_MAX_CHAR_COUNT)
toAddressString(ip.address, 0, ipv4Mapped, buf)
return buf.toString()
} }
private fun toAddressString(bytes: ByteArray, offset: Int, ipv4Mapped: Boolean): String { /**
* Returns the [String] representation of an [InetAddress].
*
* * Inet4Address results are identical to [InetAddress.getHostAddress]
* * Inet6Address results adhere to
* [rfc 5952 section 4](http://tools.ietf.org/html/rfc5952#section-4) if
* `ipv4Mapped` is false. If `ipv4Mapped` is true then "IPv4 mapped" format
* from [rfc 4291 section 2](http://tools.ietf.org/html/rfc4291#section-2.5.5) will be supported.
* The compressed result will always obey the compression rules defined in
* [rfc 5952 section 4](http://tools.ietf.org/html/rfc5952#section-4)
*
*
* The output does not include Scope ID.
*
* @param ip [InetAddress] to be converted to an address string
* @param ipv4Mapped `true` to stray from strict rfc 5952 and support the "IPv4 mapped" format
* defined in [rfc 4291 section 2](http://tools.ietf.org/html/rfc4291#section-2.5.5) while still
* following the updated guidelines in [rfc 5952 section 4](http://tools.ietf.org/html/rfc5952#section-4)
* `false` to strictly follow rfc 5952
*
* @param buf the StringBuilder to build the address into
*/
fun toString(ip: Inet6Address, ipv4Mapped: Boolean = false, buf: StringBuilder) {
toAddressString(ip.address, 0, ipv4Mapped, buf)
}
private fun toAddressString(bytes: ByteArray, offset: Int, ipv4Mapped: Boolean, buf: StringBuilder) {
val words = IntArray(IPV6_WORD_COUNT) val words = IntArray(IPV6_WORD_COUNT)
var i: Int var i: Int
val end = offset + words.size val end = offset + words.size
@ -686,13 +851,12 @@ object IPv6 {
// Translate to string taking into account longest consecutive 0s // Translate to string taking into account longest consecutive 0s
val shortestEnd = shortestStart + shortestLength val shortestEnd = shortestStart + shortestLength
val b = StringBuilder(IPV6_MAX_CHAR_COUNT)
if (shortestEnd < 0) { // Optimization when there is no compressing needed if (shortestEnd < 0) { // Optimization when there is no compressing needed
b.append(Integer.toHexString(words[0])) buf.append(Integer.toHexString(words[0]))
i = 1 i = 1
while (i < words.size) { while (i < words.size) {
b.append(':') buf.append(':')
b.append(Integer.toHexString(words[i])) buf.append(Integer.toHexString(words[i]))
++i ++i
} }
} }
@ -700,11 +864,11 @@ object IPv6 {
// Loop unroll the first index (so we don't constantly check i==0 cases in loop) // Loop unroll the first index (so we don't constantly check i==0 cases in loop)
val isIpv4Mapped: Boolean val isIpv4Mapped: Boolean
isIpv4Mapped = if (inRangeEndExclusive(0, shortestStart, shortestEnd)) { isIpv4Mapped = if (inRangeEndExclusive(0, shortestStart, shortestEnd)) {
b.append("::") buf.append("::")
ipv4Mapped && shortestEnd == 5 && words[5] == 0xffff ipv4Mapped && shortestEnd == 5 && words[5] == 0xffff
} }
else { else {
b.append(Integer.toHexString(words[0])) buf.append(Integer.toHexString(words[0]))
false false
} }
i = 1 i = 1
@ -713,29 +877,125 @@ object IPv6 {
if (!inRangeEndExclusive(i - 1, shortestStart, shortestEnd)) { if (!inRangeEndExclusive(i - 1, shortestStart, shortestEnd)) {
// If the last index was not part of the shortened sequence // If the last index was not part of the shortened sequence
if (!isIpv4Mapped || i == 6) { if (!isIpv4Mapped || i == 6) {
b.append(':') buf.append(':')
} }
else { else {
b.append('.') buf.append('.')
} }
} }
if (isIpv4Mapped && i > 5) { if (isIpv4Mapped && i > 5) {
b.append(words[i] shr 8) buf.append(words[i] shr 8)
b.append('.') buf.append('.')
b.append(words[i] and 0xff) buf.append(words[i] and 0xff)
} }
else { else {
b.append(Integer.toHexString(words[i])) buf.append(Integer.toHexString(words[i]))
} }
} }
else if (!inRangeEndExclusive(i - 1, shortestStart, shortestEnd)) { else if (!inRangeEndExclusive(i - 1, shortestStart, shortestEnd)) {
// If we are in the shortened sequence and the last index was not // If we are in the shortened sequence and the last index was not
b.append("::") buf.append("::")
}
++i
}
}
}
private fun toAddressString(ints: IntArray, offset: Int, ipv4Mapped: Boolean, buf: StringBuilder) {
val words = IntArray(IPV6_WORD_COUNT)
var i: Int
val end = offset + words.size
i = offset
while (i < end) {
words[i] = ints[i shl 1] shl 8 or (ints[(i shl 1) + 1])
++i
}
// Find longest run of 0s, tie goes to first found instance
var currentStart = -1
var currentLength: Int
var shortestStart = -1
var shortestLength = 0
i = 0
while (i < words.size) {
if (words[i] == 0) {
if (currentStart < 0) {
currentStart = i
}
}
else if (currentStart >= 0) {
currentLength = i - currentStart
if (currentLength > shortestLength) {
shortestStart = currentStart
shortestLength = currentLength
}
currentStart = -1
}
++i
}
// If the array ends on a streak of zeros, make sure we account for it
if (currentStart >= 0) {
currentLength = i - currentStart
if (currentLength > shortestLength) {
shortestStart = currentStart
shortestLength = currentLength
}
}
// Ignore the longest streak if it is only 1 long
if (shortestLength == 1) {
shortestLength = 0
shortestStart = -1
}
// Translate to string taking into account longest consecutive 0s
val shortestEnd = shortestStart + shortestLength
if (shortestEnd < 0) { // Optimization when there is no compressing needed
buf.append(Integer.toHexString(words[0]))
i = 1
while (i < words.size) {
buf.append(':')
buf.append(Integer.toHexString(words[i]))
++i
}
}
else { // General case that can handle compressing (and not compressing)
// Loop unroll the first index (so we don't constantly check i==0 cases in loop)
val isIpv4Mapped: Boolean
isIpv4Mapped = if (inRangeEndExclusive(0, shortestStart, shortestEnd)) {
buf.append("::")
ipv4Mapped && shortestEnd == 5 && words[5] == 0xffff
}
else {
buf.append(Integer.toHexString(words[0]))
false
}
i = 1
while (i < words.size) {
if (!inRangeEndExclusive(i, shortestStart, shortestEnd)) {
if (!inRangeEndExclusive(i - 1, shortestStart, shortestEnd)) {
// If the last index was not part of the shortened sequence
if (!isIpv4Mapped || i == 6) {
buf.append(':')
}
else {
buf.append('.')
}
}
if (isIpv4Mapped && i > 5) {
buf.append(words[i] shr 8)
buf.append('.')
buf.append(words[i] and 0xff)
}
else {
buf.append(Integer.toHexString(words[i]))
}
}
else if (!inRangeEndExclusive(i - 1, shortestStart, shortestEnd)) {
// If we are in the shortened sequence and the last index was not
buf.append("::")
} }
++i ++i
} }
} }
return b.toString()
} }
/** /**
@ -784,4 +1044,40 @@ object IPv6 {
return oneCount == 1 return oneCount == 1
} }
/**
* Truncates an address to the specified number of bits. For example,
* truncating the address 10.1.2.3 to 8 bits would yield 10.0.0.0.
*
* @param address The source address
* @param maskLength The number of bits to truncate the address to.
*/
fun truncate(address: Inet6Address, maskLength: Int): InetAddress? {
val maxMaskLength = length * 8
require(!(maskLength < 0 || maskLength > maxMaskLength)) { "invalid mask length" }
if (maskLength == maxMaskLength) {
return address
}
val bytes = address.address
for (i in maskLength / 8 + 1 until bytes.size) {
bytes[i] = 0
}
val maskBits = maskLength % 8
var bitmask = 0
for (i in 0 until maskBits) {
bitmask = bitmask or (1 shl 7 - i)
}
bytes[maskLength / 8] = (bytes[maskLength / 8].toInt() and bitmask).toByte()
return try {
InetAddress.getByAddress(bytes)
} catch (e: UnknownHostException) {
throw IllegalArgumentException("invalid address")
}
}
} }