Added int array for types, added truncate
This commit is contained in:
parent
726e4dbf21
commit
d5ceece82f
|
@ -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 private172 = toInt("172.16.0.0")
|
||||
private val private192 = toInt("192.168.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.
|
||||
*
|
||||
|
@ -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 {
|
||||
var i: Int
|
||||
|
@ -315,6 +326,27 @@ object IPv4 {
|
|||
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.
|
||||
*/
|
||||
|
@ -338,20 +370,27 @@ object IPv4 {
|
|||
}
|
||||
|
||||
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 ret = decimalDigit(ip, newFrom)
|
||||
newFrom++
|
||||
|
||||
if (newFrom == toExclusive) {
|
||||
return ret.toByte()
|
||||
return ret
|
||||
}
|
||||
|
||||
ret = ret * 10 + decimalDigit(ip, newFrom)
|
||||
newFrom++
|
||||
|
||||
return if (newFrom == toExclusive) {
|
||||
ret.toByte()
|
||||
} else (ret * 10 + decimalDigit(ip, newFrom)).toByte()
|
||||
ret
|
||||
} 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 {
|
||||
return ipBytes[0].toInt() shl 24 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 {
|
||||
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('.')
|
||||
buf.append(ipAddress shr 16 and 0xFF)
|
||||
|
@ -718,11 +768,21 @@ object IPv4 {
|
|||
buf.append(ipAddress shr 8 and 0xFF)
|
||||
buf.append('.')
|
||||
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()
|
||||
}
|
||||
|
||||
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('.')
|
||||
buf.append(ipBytes[1].toInt() and 0xFF)
|
||||
|
@ -730,9 +790,30 @@ object IPv4 {
|
|||
buf.append(ipBytes[2].toInt() and 0xFF)
|
||||
buf.append('.')
|
||||
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()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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]
|
||||
*
|
||||
|
@ -823,4 +904,40 @@ object IPv4 {
|
|||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
@ -573,7 +585,6 @@ object IPv6 {
|
|||
* [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
|
||||
|
@ -596,15 +607,54 @@ object IPv6 {
|
|||
* [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.
|
||||
*
|
||||
* @param bytes [InetAddress] to be converted to an address string
|
||||
*
|
||||
* @return `String` containing the text-formatted IP address
|
||||
* @param buf the StringBuilder to build the address into
|
||||
*/
|
||||
fun toString(bytes: ByteArray, offset: Int): String {
|
||||
return toAddressString(bytes, offset, false)
|
||||
fun toString(bytes: ByteArray, buf: StringBuilder) {
|
||||
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)
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @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
|
||||
* @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
|
||||
* following the updated guidelines in [rfc 5952 section 4](http://tools.ietf.org/html/rfc5952#section-4)
|
||||
* `false` to strictly follow rfc 5952
|
||||
*
|
||||
* @return `String` containing the text-formatted IP address
|
||||
*/
|
||||
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)
|
||||
var i: Int
|
||||
val end = offset + words.size
|
||||
|
@ -686,13 +851,12 @@ object IPv6 {
|
|||
|
||||
// Translate to string taking into account longest consecutive 0s
|
||||
val shortestEnd = shortestStart + shortestLength
|
||||
val b = StringBuilder(IPV6_MAX_CHAR_COUNT)
|
||||
if (shortestEnd < 0) { // Optimization when there is no compressing needed
|
||||
b.append(Integer.toHexString(words[0]))
|
||||
buf.append(Integer.toHexString(words[0]))
|
||||
i = 1
|
||||
while (i < words.size) {
|
||||
b.append(':')
|
||||
b.append(Integer.toHexString(words[i]))
|
||||
buf.append(':')
|
||||
buf.append(Integer.toHexString(words[i]))
|
||||
++i
|
||||
}
|
||||
}
|
||||
|
@ -700,11 +864,11 @@ object IPv6 {
|
|||
// 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)) {
|
||||
b.append("::")
|
||||
buf.append("::")
|
||||
ipv4Mapped && shortestEnd == 5 && words[5] == 0xffff
|
||||
}
|
||||
else {
|
||||
b.append(Integer.toHexString(words[0]))
|
||||
buf.append(Integer.toHexString(words[0]))
|
||||
false
|
||||
}
|
||||
i = 1
|
||||
|
@ -713,29 +877,125 @@ object IPv6 {
|
|||
if (!inRangeEndExclusive(i - 1, shortestStart, shortestEnd)) {
|
||||
// If the last index was not part of the shortened sequence
|
||||
if (!isIpv4Mapped || i == 6) {
|
||||
b.append(':')
|
||||
buf.append(':')
|
||||
}
|
||||
else {
|
||||
b.append('.')
|
||||
buf.append('.')
|
||||
}
|
||||
}
|
||||
if (isIpv4Mapped && i > 5) {
|
||||
b.append(words[i] shr 8)
|
||||
b.append('.')
|
||||
b.append(words[i] and 0xff)
|
||||
buf.append(words[i] shr 8)
|
||||
buf.append('.')
|
||||
buf.append(words[i] and 0xff)
|
||||
}
|
||||
else {
|
||||
b.append(Integer.toHexString(words[i]))
|
||||
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
|
||||
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
|
||||
}
|
||||
}
|
||||
return b.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -784,4 +1044,40 @@ object IPv6 {
|
|||
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue