mirror of
https://github.com/oxen-io/session-android.git
synced 2025-05-02 17:30:47 +00:00
Fix/ip2 country out of memory (#1628)
* Avoiding to recall Ipv4Int on every loop * Avoiding memory allocation since this is used in laarge sets * Further improvements and updated the memory allocation avoiding code * PR feedback
This commit is contained in:
parent
245241e614
commit
e731c7d0bd
@ -5,45 +5,64 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.IntentFilter
|
import android.content.IntentFilter
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||||
import org.session.libsignal.utilities.Log
|
|
||||||
import com.opencsv.CSVReader
|
import com.opencsv.CSVReader
|
||||||
import org.session.libsession.snode.OnionRequestAPI
|
import org.session.libsession.snode.OnionRequestAPI
|
||||||
|
import org.session.libsignal.utilities.Log
|
||||||
import org.session.libsignal.utilities.ThreadUtils
|
import org.session.libsignal.utilities.ThreadUtils
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import java.io.FileReader
|
import java.io.FileReader
|
||||||
|
import java.util.SortedMap
|
||||||
|
import java.util.TreeMap
|
||||||
|
|
||||||
class IP2Country private constructor(private val context: Context) {
|
class IP2Country private constructor(private val context: Context) {
|
||||||
private val pathsBuiltEventReceiver: BroadcastReceiver
|
private val pathsBuiltEventReceiver: BroadcastReceiver
|
||||||
val countryNamesCache = mutableMapOf<String, String>()
|
val countryNamesCache = mutableMapOf<String, String>()
|
||||||
|
|
||||||
private fun Ipv4Int(ip:String) = ip.takeWhile { it != '/' }.split('.').foldIndexed(0L) { i, acc, s ->
|
private fun Ipv4Int(ip: String): Int {
|
||||||
val asInt = s.toLong()
|
var result = 0L
|
||||||
acc + (asInt shl (8 * (3-i)))
|
var currentValue = 0L
|
||||||
|
var octetIndex = 0
|
||||||
|
|
||||||
|
for (char in ip) {
|
||||||
|
if (char == '.' || char == '/') {
|
||||||
|
result = result or (currentValue shl (8 * (3 - octetIndex)))
|
||||||
|
currentValue = 0
|
||||||
|
octetIndex++
|
||||||
|
if (char == '/') break
|
||||||
|
} else {
|
||||||
|
currentValue = currentValue * 10 + (char - '0')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the last octet
|
||||||
|
result = result or (currentValue shl (8 * (3 - octetIndex)))
|
||||||
|
|
||||||
|
return result.toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val ipv4ToCountry by lazy {
|
private val ipv4ToCountry: TreeMap<Int, Int?> by lazy {
|
||||||
val file = loadFile("geolite2_country_blocks_ipv4.csv")
|
val file = loadFile("geolite2_country_blocks_ipv4.csv")
|
||||||
val csv = CSVReader(FileReader(file.absoluteFile)).apply {
|
CSVReader(FileReader(file.absoluteFile)).use { csv ->
|
||||||
skip(1)
|
csv.skip(1)
|
||||||
}
|
|
||||||
|
|
||||||
csv.readAll()
|
csv.asSequence().associateTo(TreeMap()) { cols ->
|
||||||
.associate { cols ->
|
Ipv4Int(cols[0]).toInt() to cols[1].toIntOrNull()
|
||||||
Ipv4Int(cols[0]) to cols[1].toIntOrNull()
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val countryToNames by lazy {
|
private val countryToNames: Map<Int, String> by lazy {
|
||||||
val file = loadFile("geolite2_country_locations_english.csv")
|
val file = loadFile("geolite2_country_locations_english.csv")
|
||||||
val csv = CSVReader(FileReader(file.absoluteFile)).apply {
|
CSVReader(FileReader(file.absoluteFile)).use { csv ->
|
||||||
skip(1)
|
csv.skip(1)
|
||||||
}
|
|
||||||
csv.readAll()
|
csv.asSequence()
|
||||||
.filter { cols -> !cols[0].isNullOrEmpty() && !cols[1].isNullOrEmpty() }
|
.filter { cols -> !cols[0].isNullOrEmpty() && !cols[1].isNullOrEmpty() }
|
||||||
.associate { cols ->
|
.associate { cols ->
|
||||||
cols[0].toInt() to cols[5]
|
cols[0].toInt() to cols[5]
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// region Initialization
|
// region Initialization
|
||||||
@ -95,9 +114,8 @@ class IP2Country private constructor(private val context: Context) {
|
|||||||
// return early if cached
|
// return early if cached
|
||||||
countryNamesCache[ip]?.let { return it }
|
countryNamesCache[ip]?.let { return it }
|
||||||
|
|
||||||
val comps = ipv4ToCountry.asSequence()
|
val ipInt = Ipv4Int(ip)
|
||||||
|
val bestMatchCountry = ipv4ToCountry.floorEntry(ipInt)?.let { (_, code) ->
|
||||||
val bestMatchCountry = comps.lastOrNull { it.key <= Ipv4Int(ip) }?.let { (_, code) ->
|
|
||||||
if (code != null) {
|
if (code != null) {
|
||||||
countryToNames[code]
|
countryToNames[code]
|
||||||
} else {
|
} else {
|
||||||
@ -127,3 +145,4 @@ class IP2Country private constructor(private val context: Context) {
|
|||||||
}
|
}
|
||||||
// endregion
|
// endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,10 +2,7 @@ package org.thoughtcrime.securesms.util;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.annimon.stream.Stream;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public final class ListUtil {
|
public final class ListUtil {
|
||||||
@ -21,16 +18,4 @@ public final class ListUtil {
|
|||||||
|
|
||||||
return chunks;
|
return chunks;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SafeVarargs
|
|
||||||
public static <T> List<T> concat(Collection<T>... items) {
|
|
||||||
//noinspection Convert2MethodRef
|
|
||||||
final List<T> concat = new ArrayList<>(Stream.of(items).map(Collection::size).reduce(0, (lhs, rhs) -> lhs+rhs));
|
|
||||||
|
|
||||||
for (Collection<T> list : items) {
|
|
||||||
concat.addAll(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
return concat;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user