@@ -3,142 +3,3 @@ package com.statsig.sdk
3
3
private const val IP_TABLE_FILE : String = " ip_supalite.table"
4
4
private const val NULL_CC : String = " --"
5
5
private const val LOOKUP_TABLE_TERMINATOR : Int = ' *' .code
6
-
7
- class CountryLookup {
8
- companion object {
9
- val countryCodes: MutableList <String > = mutableListOf ()
10
- val ipRanges: MutableList <Long > = mutableListOf ()
11
-
12
- private val countryTable: MutableList <String > = mutableListOf ()
13
- private var initialized = false
14
- private val lock = Any ()
15
-
16
- @JvmStatic
17
- fun initialize () {
18
- synchronized(lock) {
19
- if (initialized) {
20
- return
21
- }
22
-
23
- // optimistically set initialized to true - otherwise we could reinitialize every check
24
- initialized = true
25
-
26
- val resourceAsStream = CountryLookup ::class .java.classLoader.getResourceAsStream(IP_TABLE_FILE )
27
- resourceAsStream?.use { inputStream ->
28
- val bytes = inputStream.readBytes()
29
- initializeWithBytes(bytes)
30
- } ? : return
31
- }
32
- }
33
-
34
- fun cleanup () {
35
- synchronized(lock) {
36
- this .countryCodes.clear()
37
- this .ipRanges.clear()
38
- this .countryTable.clear()
39
- initialized = false
40
- }
41
- }
42
-
43
- @JvmStatic
44
- fun lookupIPString (ipAddressString : String ): String? {
45
- initialize()
46
- if (ipAddressString.isEmpty()) {
47
- return null
48
- }
49
-
50
- val components = ipAddressString.split(" ." )
51
- if (components.size != 4 ) {
52
- return null
53
- }
54
-
55
- return try {
56
- val ipNumber = components[0 ].toLong() * 16777216 +
57
- components[1 ].toLong() * 65536 +
58
- components[2 ].toLong() * 256 +
59
- components[3 ].toLong()
60
-
61
- lookupIPNumber(ipNumber)
62
- } catch (_e : Exception ) {
63
- null
64
- }
65
- }
66
-
67
- @JvmStatic
68
- fun lookupIPNumber (ipNumber : Long ): String? {
69
- initialize()
70
- val index = binarySearch(ipNumber)
71
- val cc = countryCodes[index]
72
- if (cc == NULL_CC ) {
73
- return null
74
- }
75
- return cc
76
- }
77
-
78
- /* *
79
- * The binary is packed as follows:
80
- * c1.c2.c3.....**: Country code look up table, terminated by **
81
-
82
- * n1.c: if n is < 240, c is country code index
83
- * 242.n2.n3.c: if n >= 240 but < 65536. n2 being lower order byte
84
- * 243.n2.n3.n4.c: if n >= 65536. n2 being lower order byte
85
- */
86
- private fun initializeWithBytes (bytes : ByteArray ) {
87
- var index = 0
88
- while (index < bytes.size) {
89
- val c1 = bytes[index++ ]
90
- val c2 = bytes[index++ ]
91
-
92
- countryTable.add(String (byteArrayOf(c1, c2)))
93
- if (c1.toInt() == LOOKUP_TABLE_TERMINATOR ) {
94
- break
95
- }
96
- }
97
- var lastEndRange: Long = 0
98
- while (index < bytes.size) {
99
- var count: Long = 0
100
- val n1 = bytes[index++ ].toInt().and (0xff )
101
- when {
102
- n1 < 240 -> {
103
- count = n1.toLong()
104
- }
105
- n1 == 242 -> {
106
- val n2 = bytes[index++ ].toInt().and (0xff )
107
- val n3 = bytes[index++ ].toInt().and (0xff )
108
- count = (n2.or ((n3 shl 8 ))).toLong()
109
- }
110
- n1 == 243 -> {
111
- val n2 = bytes[index++ ].toInt().and (0xff )
112
- val n3 = bytes[index++ ].toInt().and (0xff )
113
- val n4 = bytes[index++ ].toInt().and (0xff )
114
- count = ((n2.or ((n3 shl 8 ))).or ((n4 shl 16 ))).toLong()
115
- }
116
- }
117
- lastEndRange + = count * 256
118
-
119
- val cc = bytes[index++ ].toInt().and (0xff )
120
-
121
- ipRanges.add(lastEndRange)
122
- countryCodes.add(countryTable[cc])
123
- }
124
-
125
- initialized = true
126
- }
127
-
128
- private fun binarySearch (ipNumber : Long ): Int {
129
- var min = 0
130
- var max = ipRanges.size - 1
131
-
132
- while (min < max) {
133
- val mid = (min + max) shr 1
134
- if (ipRanges[mid] <= ipNumber) {
135
- min = mid + 1
136
- } else {
137
- max = mid
138
- }
139
- }
140
-
141
- return min
142
- }
143
- }
144
- }
0 commit comments