____ _ _______ __
/ __ \ | /| / / __/ |/ /
/ /_/ / |/ |/ / _// /
\____/|__/|__/___/_||_/
owenclave / app/src/main/java/io/nekohasekai/sagernet/utils/Subnet.kt
/******************************************************************************
* *
* Copyright (C) 2021 by nekohasekai *
* Copyright (C) 2021 by Max Lv *
* Copyright (C) 2021 by Mygod Studio *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see . *
* *
******************************************************************************/
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 {
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 {
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)
}