Use array instead of Treemap

pull/1684/head
bemusementpark 6 months ago
parent 84b57283f4
commit f05daaa194

@ -12,28 +12,37 @@ import org.session.libsignal.utilities.ThreadUtils
import java.io.DataInputStream
import java.io.InputStream
import java.io.InputStreamReader
import java.util.TreeMap
private fun ipv4Int(ip: String): UInt =
ip.split(".", "/", ",").take(4).fold(0U) { acc, s -> acc shl 8 or s.toUInt() }
@OptIn(ExperimentalUnsignedTypes::class)
class IP2Country internal constructor(
private val context: Context,
private val openStream: (String) -> InputStream = context.assets::open
) {
val countryNamesCache = mutableMapOf<String, String>()
private val ipv4ToCountry by lazy {
private val ips: UIntArray by lazy { ipv4ToCountry.first }
private val codes: IntArray by lazy { ipv4ToCountry.second }
private val ipv4ToCountry: Pair<UIntArray, IntArray> by lazy {
openStream("geolite2_country_blocks_ipv4.bin")
.let(::DataInputStream)
.use {
TreeMap<UInt, Int>().apply {
while (it.available() > 0) {
val ip = it.readInt().toUInt()
val code = it.readInt()
put(ip, code)
}
val size = it.available() / 8
val ips = UIntArray(size)
val codes = IntArray(size)
var i = 0
while (it.available() > 0) {
ips[i] = it.readInt().toUInt()
codes[i] = it.readInt()
i++
}
ips to codes
}
}
@ -82,7 +91,9 @@ class IP2Country internal constructor(
countryNamesCache[ip]?.let { return it }
val ipInt = ipv4Int(ip)
val bestMatchCountry = ipv4ToCountry.floorEntry(ipInt)?.value?.let { countryToNames[it] }
val index = ips.fuzzyBinarySearch(ipInt)
val code = index?.let { codes[it] }
val bestMatchCountry = countryToNames[code]
if (bestMatchCountry != null) countryNamesCache[ip] = bestMatchCountry
else Log.d("Loki","Country name for $ip couldn't be found")
@ -92,13 +103,37 @@ class IP2Country internal constructor(
private fun populateCacheIfNeeded() {
ThreadUtils.queue {
val start = System.currentTimeMillis()
OnionRequestAPI.paths.iterator().forEach { path ->
path.iterator().forEach { snode ->
cacheCountryForIP(snode.ip) // Preload if needed
}
}
Log.d("Loki","Cache populated in ${System.currentTimeMillis() - start}ms")
Broadcaster(context).broadcast("onionRequestPathCountriesLoaded")
}
}
// endregion
}
@OptIn(ExperimentalUnsignedTypes::class)
private fun UIntArray.fuzzyBinarySearch(target: UInt): Int? {
if (isEmpty()) return null
var low = 0
var high = size - 1
while (low <= high) {
val mid = (low + high) / 2
val midValue = this[mid]
when {
midValue == target -> return mid // Exact match found
midValue < target -> low = mid + 1 // Search in the right half
else -> high = mid - 1 // Search in the left half
}
}
// If no exact match, return the largest index with value <= target
return if (high >= 0) high else null
}

Loading…
Cancel
Save