Current section
Files
Jump to
Current section
Files
lib/ip2location/column.ex
defmodule IP2Location.Column do
@moduledoc false
use Bitwise, skip_operators: true
@column_position_map %{
country: [0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
region: [0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3],
city: [0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4],
isp: [0, 0, 3, 0, 5, 0, 7, 5, 7, 0, 8, 0, 9, 0, 9, 0, 9, 0, 9, 7, 9, 0, 9, 7, 9],
latitude: [0, 0, 0, 0, 0, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5],
longitude: [0, 0, 0, 0, 0, 6, 6, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6],
domain: [0, 0, 0, 0, 0, 0, 0, 6, 8, 0, 9, 0, 10,0, 10, 0, 10, 0, 10, 8, 10, 0, 10, 8, 10],
zipcode: [0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 7, 0, 7, 7, 7, 0, 7, 0, 7, 7, 7, 0, 7],
timezone: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 7, 8, 8, 8, 7, 8, 0, 8, 8, 8, 0, 8],
netspeed: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 11,0, 11,8, 11, 0, 11, 0, 11, 0, 11],
idd_code: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 12, 0, 12, 0, 12, 9, 12, 0, 12],
area_code: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10 ,13 ,0, 13, 0, 13, 10, 13, 0, 13],
weatherstation_code: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 14, 0, 14, 0, 14, 0, 14],
weatherstation_name: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 15, 0, 15, 0, 15, 0, 15],
mcc: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 16, 0, 16, 9, 16],
mnc: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10,17, 0, 17, 10, 17],
mobile_brand: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11,18, 0, 18, 11, 18],
elevation: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 19, 0, 19],
usage_type: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 20]
}
@column_type_map %{
country: :country,
region: :string, city: :string, isp: :string,
latitude: :float, longitude: :float,
domain: :string, zipcode: :string, timezone: :string,
netspeed: :string, idd_code: :string, area_code: :string,
weatherstation_code: :string, weatherstation_name: :string,
mcc: :string, mnc: :string,
mobile_brand: :string, usage_type: :string,
elevation: :float_string
}
@unavailable_value "This parameter is unavailable for selected data file. Please upgrade the data file."
@empty_value_map %{
country: {@unavailable_value, @unavailable_value},
string: @unavailable_value,
float: 0.0, float_string: 0.0
}
@read_uint_sizes [8, 32, 128]
@read_uint_functions Enum.map(@read_uint_sizes, fn size ->
{:"read_uint#{size}", 2}
end)
for size <- @read_uint_sizes do
def unquote(:"read_uint#{size}")(input, offset) do
<< result::little-unquote(size) >> =
binary_part(input, offset - 1, unquote(div(size, 8)))
result
end
end
def read_string(input, offset) do
<< length::little-8 >> = binary_part(input, offset, 1)
binary_part(input, offset + 1, length)
end
def read_float(input, offset) do
<< result::float-little-32 >> = binary_part(input, offset - 1, 4)
result
end
def read_country_column(input, column_position, row_offset) do
column_offset = bsl(column_position - 1, 2)
data_start = read_uint32(input, row_offset + column_offset)
short_name = read_string(input, data_start)
long_name = read_string(input, data_start + 3)
{short_name, long_name}
end
def read_string_column(input, column_position, row_offset) do
column_offset = bsl(column_position - 1, 2)
data_start = read_uint32(input, row_offset + column_offset)
read_string(input, data_start)
end
def read_float_column(input, column_position, row_offset) do
column_offset = bsl(column_position - 1, 2)
read_float(input, row_offset + column_offset)
end
def read_float_string_column(input, column_position, row_offset) do
string = read_string_column(input, column_position, row_offset)
case Float.parse(string) do
{value, _rest} -> value
:error -> 0.0
end
end
@compile {:inline, @read_uint_functions ++ [
read_string: 2, read_float: 2,
read_country_column: 3, read_string_column: 3,
read_float_column: 3, read_float_string_column: 3
]}
for {column_name, positions} <- @column_position_map do
for {position, db_type} <- Enum.with_index(positions, 1) do
if position == 0 do
empty_value = @empty_value_map[@column_type_map[column_name]]
def read_column(_, unquote(db_type), unquote(column_name), _) do
unquote(empty_value)
end
else
function_name = :"read_#{@column_type_map[column_name]}_column"
def read_column(input, unquote(db_type), unquote(column_name), row_offset) do
unquote(function_name)(input, unquote(position), row_offset)
end
end
end
end
end