| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586 |
- package io.nekohasekai.sagernet.utils
- import io.nekohasekai.sagernet.ktx.parseNumericAddress
- import java.net.InetAddress
- import java.util.*
- class Subnet(val address: InetAddress, val prefixSize: Int) : Comparable<Subnet> {
- companion object {
- fun fromString(value: String, lengthCheck: Int = -1): Subnet? {
- val parts = value.split('/', limit = 2)
- val addr = parts[0].parseNumericAddress() ?: return null
- check(lengthCheck < 0 || addr.address.size == lengthCheck)
- return if (parts.size == 2) try {
- val prefixSize = parts[1].toInt()
- if (prefixSize < 0 || prefixSize > addr.address.size shl 3) null else Subnet(addr,
- prefixSize)
- } catch (_: NumberFormatException) {
- null
- } else Subnet(addr, addr.address.size shl 3)
- }
- }
- private val addressLength get() = address.address.size shl 3
- init {
- require(prefixSize in 0..addressLength) { "prefixSize $prefixSize not in 0..$addressLength" }
- }
- class Immutable(private val a: ByteArray, private val prefixSize: Int = 0) {
- companion object : Comparator<Immutable> {
- override fun compare(a: Immutable, b: Immutable): Int {
- check(a.a.size == b.a.size)
- for (i in a.a.indices) {
- val result = a.a[i].compareTo(b.a[i])
- if (result != 0) return result
- }
- return 0
- }
- }
- fun matches(b: Immutable) = matches(b.a)
- fun matches(b: ByteArray): Boolean {
- if (a.size != b.size) return false
- var i = 0
- while (i * 8 < prefixSize && i * 8 + 8 <= prefixSize) {
- if (a[i] != b[i]) return false
- ++i
- }
- return i * 8 == prefixSize || a[i] == (b[i].toInt() and -(1 shl i * 8 + 8 - prefixSize)).toByte()
- }
- }
- fun toImmutable() = Immutable(address.address.also {
- var i = prefixSize / 8
- if (prefixSize % 8 > 0) {
- it[i] = (it[i].toInt() and -(1 shl i * 8 + 8 - prefixSize)).toByte()
- ++i
- }
- while (i < it.size) it[i++] = 0
- }, prefixSize)
- override fun toString(): String =
- if (prefixSize == addressLength) address.hostAddress else address.hostAddress + '/' + prefixSize
- private fun Byte.unsigned() = toInt() and 0xFF
- override fun compareTo(other: Subnet): Int {
- val addrThis = address.address
- val addrThat = other.address.address
- var result =
- addrThis.size.compareTo(addrThat.size) // IPv4 address goes first
- if (result != 0) return result
- for (i in addrThis.indices) {
- result = addrThis[i].unsigned()
- .compareTo(addrThat[i].unsigned()) // undo sign extension of signed byte
- if (result != 0) return result
- }
- return prefixSize.compareTo(other.prefixSize)
- }
- override fun equals(other: Any?): Boolean {
- val that = other as? Subnet
- return address == that?.address && prefixSize == that.prefixSize
- }
- override fun hashCode(): Int = Objects.hash(address, prefixSize)
- }
|