Speed up Acl.fromReader

It should run 9x faster than before now.

Past mistakes:

* InetAddress.parseNumericAddress throws TWO exceptions underthehood if the string passed is invalid. This caused a huge overhead;
* CharSequence.split in Kotlin is EXTREMELY SLOW.
This commit is contained in:
Mygod
2017-12-17 04:43:19 -08:00
parent bc22b0c481
commit 1010edc561
10 changed files with 111 additions and 78 deletions
@@ -116,7 +116,8 @@ class Acl {
var subnets: SortedList<Subnet>? = if (defaultBypass) proxySubnets else bypassSubnets
reader.useLines {
for (line in it) {
val blocks = line.split('#', limit = 2)
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
val blocks = (line as java.lang.String).split("#", 2)
val url = networkAclParser.matchEntire(blocks.getOrElse(1, {""}))?.groupValues?.getOrNull(1)
if (url != null) urls.add(URL(url))
val input = blocks[0].trim()
@@ -135,10 +136,9 @@ class Acl {
}
"[reject_all]", "[bypass_all]" -> bypass = true
"[accept_all]", "[proxy_all]" -> bypass = false
else -> if (subnets != null && input.isNotEmpty()) try {
subnets!!.add(Subnet.fromString(input))
} catch (_: IllegalArgumentException) {
hostnames!!.add(input)
else -> if (subnets != null && input.isNotEmpty()) {
val subnet = Subnet.fromString(input)
if (subnet == null) hostnames!!.add(input) else subnets!!.add(subnet)
}
}
}
@@ -333,8 +333,8 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (savedInstanceState != null) {
selectedItems.addAll(savedInstanceState.getStringArray(SELECTED_SUBNETS)?.map(Subnet.Companion::fromString)
?: listOf())
selectedItems.addAll(savedInstanceState.getStringArray(SELECTED_SUBNETS)
?.mapNotNull(Subnet.Companion::fromString) ?: listOf())
selectedItems.addAll(savedInstanceState.getStringArray(SELECTED_HOSTNAMES)
?: arrayOf())
selectedItems.addAll(savedInstanceState.getStringArray(SELECTED_URLS)?.map { URL(it) }
@@ -159,10 +159,10 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
Acl.ALL, Acl.BYPASS_CHN, Acl.CUSTOM_RULES -> builder.addRoute("0.0.0.0", 0)
else -> {
resources.getStringArray(R.array.bypass_private_route).forEach {
val subnet = Subnet.fromString(it)
val subnet = Subnet.fromString(it)!!
builder.addRoute(subnet.address.hostAddress, subnet.prefixSize)
}
profile.remoteDns.split(",").map { it.trim().parseNumericAddress() }
profile.remoteDns.split(",").mapNotNull { it.trim().parseNumericAddress() }
.forEach { builder.addRoute(it, it.address.size shl 3) }
}
}
@@ -25,15 +25,15 @@ import java.util.*
class Subnet(val address: InetAddress, val prefixSize: Int) : Comparable<Subnet> {
companion object {
@Throws(IllegalArgumentException::class)
fun fromString(value: String): Subnet {
val parts = value.split('/')
val addr = parts[0].parseNumericAddress()
return when (parts.size) {
1 -> Subnet(addr, addr.address.size shl 3)
2 -> Subnet(addr, parts[1].toInt())
else -> throw IllegalArgumentException()
}
fun fromString(value: String): Subnet? {
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
val parts = (value as java.lang.String).split("/", 2)
val addr = parts[0].parseNumericAddress() ?: return null
return if (parts.size == 2) try {
Subnet(addr, parts[1].toInt())
} catch (_: NumberFormatException) {
null
} else Subnet(addr, addr.address.size shl 3)
}
}
@@ -11,31 +11,20 @@ import android.support.v4.app.FragmentManager
import android.support.v7.util.SortedList
import android.util.TypedValue
import com.github.shadowsocks.App.Companion.app
import com.github.shadowsocks.JniHelper
import java.lang.reflect.InvocationTargetException
import java.net.Inet4Address
import java.net.InetAddress
import java.net.URLConnection
private val isNumericMethod by lazy { InetAddress::class.java.getMethod("isNumeric", String::class.java) }
private val parseNumericAddressMethod by lazy {
try {
InetAddress::class.java.getMethod("parseNumericAddress", String::class.java)
} catch (_: NoSuchMethodException) {
null
}
}
private val fieldChildFragmentManager by lazy {
val field = Fragment::class.java.getDeclaredField("mChildFragmentManager")
field.isAccessible = true
field
}
fun String.isNumericAddress(): Boolean = isNumericMethod.invoke(null, this) as Boolean
fun String.parseNumericAddress(): InetAddress =
if (parseNumericAddressMethod == null) InetAddress.getByName(this) else try {
parseNumericAddressMethod!!.invoke(null, this) as InetAddress
} catch (exc: InvocationTargetException) {
throw exc.cause ?: exc
}
fun String.isNumericAddress() = parseNumericAddress() != null
fun String.parseNumericAddress() = JniHelper.parseNumericAddress(this)
fun parsePort(str: String?, default: Int, min: Int = 1025): Int {
val x = str?.toIntOrNull() ?: default