fix(erlang): integer guard, cache dedup, dead macro cleanup

This commit is contained in:
Alice39s 2026-06-28 01:50:39 +09:00
parent a1667d1182
commit 445eb4cb8e
No known key found for this signature in database
GPG Key ID: 0E0AA11C3939DDDD
12 changed files with 86 additions and 106 deletions

View File

@ -158,11 +158,11 @@ System:
CPU : Apple M4 CPU : Apple M4
Cores : 10 cores / 10 threads Cores : 10 cores / 10 threads
Erlang : Erlang/OTP 29 [erts-17.0.2] [source] [64-bit] [smp:10:10] [ds:10:10:10] [async-threads:1] [jit] [dtrace] Erlang : Erlang/OTP 29 [erts-17.0.2] [source] [64-bit] [smp:10:10] [ds:10:10:10] [async-threads:1] [jit] [dtrace]
Loaded : 487169 IPs in 1.574 s Loaded : 487169 IPs in 1.335 s
Benchmarks: Benchmarks:
file total= 9.460s count= 487169 qps= 51500.40 avg= 0.019417 ms/op (19.417 us/op) cold total= 9.601s count= 487169 qps= 50740.66 avg= 0.019708 ms/op (19.708 us/op)
cache total= 0.150s count= 487169 qps= 3245240.41 avg= 0.000308 ms/op ( 0.308 us/op) warm total= 0.160s count= 487169 qps= 3053164.29 avg= 0.000328 ms/op ( 0.328 us/op)
Done. Done.
``` ```
@ -174,11 +174,11 @@ System:
CPU : Apple M4 CPU : Apple M4
Cores : 10 cores / 10 threads Cores : 10 cores / 10 threads
Erlang : Erlang/OTP 29 [erts-17.0.2] [source] [64-bit] [smp:10:10] [ds:10:10:10] [async-threads:1] [jit] [dtrace] Erlang : Erlang/OTP 29 [erts-17.0.2] [source] [64-bit] [smp:10:10] [ds:10:10:10] [async-threads:1] [jit] [dtrace]
Loaded : 638953 IPs in 2.724 s Loaded : 638953 IPs in 2.949 s
Benchmarks: Benchmarks:
file total= 16.326s count= 638953 qps= 39137.65 avg= 0.025551 ms/op (25.551 us/op) cold total= 20.504s count= 638953 qps= 31162.52 avg= 0.032090 ms/op (32.090 us/op)
cache total= 0.469s count= 638953 qps= 1361972.38 avg= 0.000734 ms/op ( 0.734 us/op) warm total= 0.444s count= 638953 qps= 1437781.56 avg= 0.000696 ms/op ( 0.696 us/op)
Done. Done.
``` ```

View File

@ -135,11 +135,11 @@ System:
CPU : Apple M4 CPU : Apple M4
Cores : 10 cores / 10 threads Cores : 10 cores / 10 threads
Erlang : Erlang/OTP 29 [erts-17.0.2] [source] [64-bit] [smp:10:10] [ds:10:10:10] [async-threads:1] [jit] [dtrace] Erlang : Erlang/OTP 29 [erts-17.0.2] [source] [64-bit] [smp:10:10] [ds:10:10:10] [async-threads:1] [jit] [dtrace]
Loaded : 487169 IPs in 1.574 s Loaded : 487169 IPs in 1.335 s
Benchmarks: Benchmarks:
file total= 9.460s count= 487169 qps= 51500.40 avg= 0.019417 ms/op (19.417 us/op) cold total= 9.601s count= 487169 qps= 50740.66 avg= 0.019708 ms/op (19.708 us/op)
cache total= 0.150s count= 487169 qps= 3245240.41 avg= 0.000308 ms/op ( 0.308 us/op) warm total= 0.160s count= 487169 qps= 3053164.29 avg= 0.000328 ms/op ( 0.328 us/op)
Done. Done.
``` ```
@ -151,11 +151,11 @@ System:
CPU : Apple M4 CPU : Apple M4
Cores : 10 cores / 10 threads Cores : 10 cores / 10 threads
Erlang : Erlang/OTP 29 [erts-17.0.2] [source] [64-bit] [smp:10:10] [ds:10:10:10] [async-threads:1] [jit] [dtrace] Erlang : Erlang/OTP 29 [erts-17.0.2] [source] [64-bit] [smp:10:10] [ds:10:10:10] [async-threads:1] [jit] [dtrace]
Loaded : 638953 IPs in 2.724 s Loaded : 638953 IPs in 2.949 s
Benchmarks: Benchmarks:
file total= 16.326s count= 638953 qps= 39137.65 avg= 0.025551 ms/op (25.551 us/op) cold total= 20.504s count= 638953 qps= 31162.52 avg= 0.032090 ms/op (32.090 us/op)
cache total= 0.469s count= 638953 qps= 1361972.38 avg= 0.000734 ms/op ( 0.734 us/op) warm total= 0.444s count= 638953 qps= 1437781.56 avg= 0.000696 ms/op ( 0.696 us/op)
Done. Done.
``` ```

View File

@ -10,7 +10,7 @@ PROJECT_ROOT="$(cd ../.. && pwd)"
if [ "$VERSION" = "ipv6" ]; then if [ "$VERSION" = "ipv6" ]; then
DB_FILE="${PROJECT_ROOT}/data/ip2region_v6.xdb" DB_FILE="${PROJECT_ROOT}/data/ip2region_v6.xdb"
DATA_FILE="${PROJECT_ROOT}/data/ipv6_source.txt" DATA_FILE="${PROJECT_ROOT}/data/ipv6_source.txt"
DB_CONFIG="[{ipv4, \"ip2region.xdb\"}, {ipv6, \"${DB_FILE}\"}]" DB_CONFIG="[{ipv6, \"${DB_FILE}\"}]"
elif [ "$VERSION" = "ipv4" ]; then elif [ "$VERSION" = "ipv4" ]; then
DATA_FILE="${PROJECT_ROOT}/data/ipv4_source.txt" DATA_FILE="${PROJECT_ROOT}/data/ipv4_source.txt"
DB_CONFIG="[{ipv4, \"ip2region.xdb\"}]" DB_CONFIG="[{ipv4, \"ip2region.xdb\"}]"

View File

@ -4,20 +4,11 @@
-define(NONE, none). -define(NONE, none).
-define(APP_NAME, ip2region). -define(APP_NAME, ip2region).
-define(XDB_VECTOR_INDEX, ets_xdb_vector_index).
-define(XDB_SEGMENT_INDEX, ets_xdb_segement_index).
-define(IP2REGION_CACHE, ets_ip2region_cache).
-define(XDB_HEADER_SIZE, 256). -define(XDB_HEADER_SIZE, 256).
-define(XDB_VECTOR_COLS, 256). -define(XDB_VECTOR_COLS, 256).
-define(XDB_VECTOR_INDEX_SIZE, 8). -define(XDB_VECTOR_INDEX_SIZE, 8).
-define(XDB_VECTOR_INDEX_COUNT, (16#10000)). %% 256*256 -define(XDB_VECTOR_INDEX_COUNT, (16#10000)). %% 256*256
-define(XDB_SEGMENT_INDEX_SIZE, 14).
-define(IP2REGION_POOL, ip2region_pool).
%% xdb header field offsets (bytes) %% xdb header field offsets (bytes)
-define(XDB_HEADER_VERSION_OFFSET, 0). -define(XDB_HEADER_VERSION_OFFSET, 0).
-define(XDB_HEADER_INDEX_POLICY_OFFSET, 2). -define(XDB_HEADER_INDEX_POLICY_OFFSET, 2).
@ -26,7 +17,6 @@
-define(XDB_HEADER_END_INDEX_PTR_OFFSET, 12). -define(XDB_HEADER_END_INDEX_PTR_OFFSET, 12).
-define(XDB_HEADER_IP_VERSION_OFFSET, 16). -define(XDB_HEADER_IP_VERSION_OFFSET, 16).
-define(XDB_HEADER_RUNTIME_PTR_BYTES_OFFSET, 18). -define(XDB_HEADER_RUNTIME_PTR_BYTES_OFFSET, 18).
-define(XDB_HEADER_INFO_LENGTH, 256).
%% IP versions as stored in xdb header %% IP versions as stored in xdb header
-define(IP_VERSION_4, 4). -define(IP_VERSION_4, 4).
@ -45,12 +35,8 @@
-define(IP2REGION_CACHE_V6, ets_ip2region_cache_v6). -define(IP2REGION_CACHE_V6, ets_ip2region_cache_v6).
%% Per-version pool names. ?IP2REGION_POOL is kept as the legacy v4 pool name. %% Per-version pool names. ?IP2REGION_POOL is kept as the legacy v4 pool name.
-define(IP2REGION_POOL, ip2region_pool).
-define(IP2REGION_POOL_V4, ip2region_pool_v4). -define(IP2REGION_POOL_V4, ip2region_pool_v4).
-define(IP2REGION_POOL_V6, ip2region_pool_v6). -define(IP2REGION_POOL_V6, ip2region_pool_v6).
-ifndef(IF).
-define(IF(C, T, F), case (C) of true -> (T); false -> (F) end).
-define(IF(C, T), ?IF(C, T, skip)).
-endif.
-endif. -endif.

View File

@ -41,10 +41,6 @@ init([]) ->
%% %%
create_table() -> create_table() ->
Opts = [named_table, set, public, {read_concurrency, true}, {keypos, 1}], Opts = [named_table, set, public, {read_concurrency, true}, {keypos, 1}],
%% Legacy tables (kept for backward compatibility)
ensure_table(?XDB_VECTOR_INDEX, Opts),
ensure_table(?XDB_SEGMENT_INDEX, Opts),
ensure_table(?IP2REGION_CACHE, Opts),
%% Version-specific tables for dual-stack support %% Version-specific tables for dual-stack support
ensure_table(?XDB_VECTOR_INDEX_V4, Opts), ensure_table(?XDB_VECTOR_INDEX_V4, Opts),
ensure_table(?XDB_VECTOR_INDEX_V6, Opts), ensure_table(?XDB_VECTOR_INDEX_V6, Opts),
@ -64,11 +60,14 @@ pool_child_specs() ->
{ok, PoolArgsCfg} = application:get_env(poolargs), {ok, PoolArgsCfg} = application:get_env(poolargs),
Versions = [Version || {Version, _File} <- DbConfig], Versions = [Version || {Version, _File} <- DbConfig],
UseLegacyName = (Versions == [ipv4]), UseLegacyName = (Versions == [ipv4]),
V4PoolName = case UseLegacyName of
true -> ?IP2REGION_POOL;
false -> ?IP2REGION_POOL_V4
end,
ok = application:set_env(?APP_NAME, v4_pool_name, V4PoolName),
lists:foldl( lists:foldl(
fun({ipv4, File}, Acc) when UseLegacyName -> fun({ipv4, File}, Acc) ->
[make_pool_spec(?IP2REGION_POOL, ipv4, File, PoolArgsCfg) | Acc]; [make_pool_spec(V4PoolName, ipv4, File, PoolArgsCfg) | Acc];
({ipv4, File}, Acc) ->
[make_pool_spec(?IP2REGION_POOL_V4, ipv4, File, PoolArgsCfg) | Acc];
({ipv6, File}, Acc) -> ({ipv6, File}, Acc) ->
[make_pool_spec(?IP2REGION_POOL_V6, ipv6, File, PoolArgsCfg) | Acc]; [make_pool_spec(?IP2REGION_POOL_V6, ipv6, File, PoolArgsCfg) | Acc];
(_, Acc) -> (_, Acc) ->

View File

@ -13,7 +13,7 @@
-spec ip_version(Ip :: tuple() | list() | binary() | integer()) -> -spec ip_version(Ip :: tuple() | list() | binary() | integer()) ->
ipv4 | ipv6 | {error, atom()}. ipv4 | ipv6 | {error, atom()}.
ip_version(Ip) when is_integer(Ip) -> ipv4; ip_version(Ip) when is_integer(Ip), Ip >= 0, Ip =< 16#FFFFFFFF -> ipv4;
ip_version({_, _, _, _}) -> ipv4; ip_version({_, _, _, _}) -> ipv4;
ip_version({_, _, _, _, _, _, _, _}) -> ipv6; ip_version({_, _, _, _, _, _, _, _}) -> ipv6;
ip_version(Ip) when is_binary(Ip) -> ip_version(Ip) when is_binary(Ip) ->
@ -29,7 +29,7 @@ ip_version(_) ->
-spec ip_to_bytes(Ip :: tuple() | list() | binary() | integer()) -> -spec ip_to_bytes(Ip :: tuple() | list() | binary() | integer()) ->
{ok, ipv4 | ipv6, binary()} | {error, atom()}. {ok, ipv4 | ipv6, binary()} | {error, atom()}.
ip_to_bytes(Ip) when is_integer(Ip) -> ip_to_bytes(Ip) when is_integer(Ip), Ip >= 0, Ip =< 16#FFFFFFFF ->
{ok, ipv4, <<Ip:32>>}; {ok, ipv4, <<Ip:32>>};
ip_to_bytes({A, B, C, D}) -> ip_to_bytes({A, B, C, D}) ->
{ok, ipv4, <<A, B, C, D>>}; {ok, ipv4, <<A, B, C, D>>};
@ -51,7 +51,7 @@ ip_to_bytes(_) ->
-spec ipv4_to_n(Ip :: tuple() | list() | binary() | integer()) -> -spec ipv4_to_n(Ip :: tuple() | list() | binary() | integer()) ->
non_neg_integer() | {error, atom()}. non_neg_integer() | {error, atom()}.
ipv4_to_n(IntIp) when is_integer(IntIp) -> IntIp; ipv4_to_n(IntIp) when is_integer(IntIp), IntIp >= 0, IntIp =< 16#FFFFFFFF -> IntIp;
ipv4_to_n({A, B, C, D}) -> ipv4_to_n({A, B, C, D}) ->
<<N:32>> = <<A, B, C, D>>, <<N:32>> = <<A, B, C, D>>,
N; N;

View File

@ -18,8 +18,7 @@
-record(state, { -record(state, {
xdb_fd, xdb_fd,
version :: ipv4 | ipv6, version :: ipv4 | ipv6
segment_index_size :: pos_integer()
}). }).
%%========================================== %%==========================================
@ -63,14 +62,14 @@ init(Args) ->
{ok, IoDevice} = file:open(XdbFileName, [read, binary]), {ok, IoDevice} = file:open(XdbFileName, [read, binary]),
{ok, HeaderBin} = file:read(IoDevice, ?XDB_HEADER_SIZE), {ok, HeaderBin} = file:read(IoDevice, ?XDB_HEADER_SIZE),
{ok, Header} = ip2region_xdb:parse_header(HeaderBin), {ok, Header} = ip2region_xdb:parse_header(HeaderBin),
Version = resolve_version(Header), case resolve_version(Header) of
SegmentIndexSize = ip2region_xdb:segment_index_size(Version), {ok, Version} ->
load_vector_index(IoDevice, Version), load_vector_index(IoDevice, Version),
{ok, #state{ {ok, #state{xdb_fd = IoDevice, version = Version}};
xdb_fd = IoDevice, {error, Reason} ->
version = Version, file:close(IoDevice),
segment_index_size = SegmentIndexSize {stop, Reason}
}}. end.
handle_call(Request, From, State) -> handle_call(Request, From, State) ->
try try
@ -137,18 +136,18 @@ do_info(Info, State) ->
resolve_version(Header) -> resolve_version(Header) ->
case ip2region_xdb:header_version(Header) of case ip2region_xdb:header_version(Header) of
2 -> ipv4; 2 -> {ok, ipv4};
3 -> 3 ->
case ip2region_xdb:header_ip_version(Header) of case ip2region_xdb:header_ip_version(Header) of
?IP_VERSION_4 -> ipv4; ?IP_VERSION_4 -> {ok, ipv4};
?IP_VERSION_6 -> ipv6; ?IP_VERSION_6 -> {ok, ipv6};
_ -> ipv4 V -> {error, {invalid_xdb_ip_version, V}}
end; end;
_ -> ipv4 V -> {error, {invalid_xdb_version, V}}
end. end.
load_vector_index(IoDevice, Version) -> load_vector_index(IoDevice, Version) ->
Table = vector_index_table(Version), Table = ip2region_xdb:vector_index_table(Version),
case ets:info(Table, size) of case ets:info(Table, size) of
undefined -> undefined ->
Opts = [named_table, set, public, {read_concurrency, true}, {keypos, 1}], Opts = [named_table, set, public, {read_concurrency, true}, {keypos, 1}],
@ -162,7 +161,7 @@ load_vector_index(IoDevice, Version) ->
load_vector_index_data(IoDevice, Table) -> load_vector_index_data(IoDevice, Table) ->
{ok, VectorIndexBin} = {ok, VectorIndexBin} =
file:read(IoDevice, ?XDB_VECTOR_INDEX_COUNT * 8), file:read(IoDevice, ?XDB_VECTOR_INDEX_COUNT * ?XDB_VECTOR_INDEX_SIZE),
load_vector_index_aux(VectorIndexBin, 0, Table). load_vector_index_aux(VectorIndexBin, 0, Table).
load_vector_index_aux(<<>>, _Index, _Table) -> ok; load_vector_index_aux(<<>>, _Index, _Table) -> ok;
@ -170,24 +169,15 @@ load_vector_index_aux(<<SPtr:32/little, EPtr:32/little, VectorIndexBin/binary>>,
ets:insert(Table, {Index, SPtr, EPtr}), ets:insert(Table, {Index, SPtr, EPtr}),
load_vector_index_aux(VectorIndexBin, Index + 1, Table). load_vector_index_aux(VectorIndexBin, Index + 1, Table).
search_ip(IoDevice, IpInt, State) when is_integer(IpInt) -> search_ip(IoDevice, Ip, #state{version = Version}) ->
search_ip(IoDevice, <<IpInt:32>>, State); SegSize = ip2region_xdb:segment_index_size(Version),
search_ip(IoDevice, Ip, #state{version = Version, segment_index_size = SegSize}) -> VectorTable = ip2region_xdb:vector_index_table(Version),
CacheTable = cache_table(Version), SegmentTable = ip2region_xdb:segment_index_table(Version),
VectorTable = vector_index_table(Version), <<A:8, B:8, _/binary>> = Ip,
SegmentTable = segment_index_table(Version), VectorIdx = A * ?XDB_VECTOR_COLS + B,
case ets:lookup(CacheTable, Ip) of [{_, SPtr, EPtr}] = ets:lookup(VectorTable, VectorIdx),
[{_, RegionInfo}] -> search_ip(IoDevice, Ip, SPtr, EPtr, 0,
RegionInfo; (EPtr - SPtr) div SegSize, SegSize, Version, SegmentTable).
_ ->
<<A:8, B:8, _/binary>> = Ip,
VectorIdx = A * ?XDB_VECTOR_COLS + B,
[{_, SPtr, EPtr}] = ets:lookup(VectorTable, VectorIdx),
RegionInfo = search_ip(IoDevice, Ip, SPtr, EPtr, 0,
(EPtr - SPtr) div SegSize, SegSize, Version, SegmentTable),
ets:insert_new(CacheTable, {Ip, RegionInfo}),
RegionInfo
end.
search_ip(IoDevice, Ip, SPtr, EPtr, Low, High, SegSize, Version, SegmentTable) when Low =< High -> search_ip(IoDevice, Ip, SPtr, EPtr, Low, High, SegSize, Version, SegmentTable) when Low =< High ->
Middle = (Low + High) bsr 1, Middle = (Low + High) bsr 1,
@ -240,12 +230,3 @@ decode_segment_index(Bin, ?XDB_SEGMENT_INDEX_SIZE_V4) ->
decode_segment_index(Bin, ?XDB_SEGMENT_INDEX_SIZE_V6) -> decode_segment_index(Bin, ?XDB_SEGMENT_INDEX_SIZE_V6) ->
<<SIp:16/binary, EIp:16/binary, DataLen:16/little, DataPtr:32/little>> = Bin, <<SIp:16/binary, EIp:16/binary, DataLen:16/little, DataPtr:32/little>> = Bin,
{SIp, EIp, DataLen, DataPtr}. {SIp, EIp, DataLen, DataPtr}.
vector_index_table(ipv4) -> ?XDB_VECTOR_INDEX_V4;
vector_index_table(ipv6) -> ?XDB_VECTOR_INDEX_V6.
segment_index_table(ipv4) -> ?XDB_SEGMENT_INDEX_V4;
segment_index_table(ipv6) -> ?XDB_SEGMENT_INDEX_V6.
cache_table(ipv4) -> ?IP2REGION_CACHE_V4;
cache_table(ipv6) -> ?IP2REGION_CACHE_V6.

View File

@ -14,6 +14,7 @@
header_start_index_ptr/1, header_end_index_ptr/1, header_start_index_ptr/1, header_end_index_ptr/1,
header_ip_version/1, header_runtime_ptr_bytes/1]). header_ip_version/1, header_runtime_ptr_bytes/1]).
-export([segment_index_size/1]). -export([segment_index_size/1]).
-export([vector_index_table/1, segment_index_table/1, cache_table/1]).
-include("ip2region.hrl"). -include("ip2region.hrl").
@ -55,3 +56,12 @@ header_runtime_ptr_bytes(#xdb_header{runtime_ptr_bytes = B}) -> B.
-spec segment_index_size(ipv4 | ipv6) -> pos_integer(). -spec segment_index_size(ipv4 | ipv6) -> pos_integer().
segment_index_size(ipv4) -> ?XDB_SEGMENT_INDEX_SIZE_V4; segment_index_size(ipv4) -> ?XDB_SEGMENT_INDEX_SIZE_V4;
segment_index_size(ipv6) -> ?XDB_SEGMENT_INDEX_SIZE_V6. segment_index_size(ipv6) -> ?XDB_SEGMENT_INDEX_SIZE_V6.
vector_index_table(ipv4) -> ?XDB_VECTOR_INDEX_V4;
vector_index_table(ipv6) -> ?XDB_VECTOR_INDEX_V6.
segment_index_table(ipv4) -> ?XDB_SEGMENT_INDEX_V4;
segment_index_table(ipv6) -> ?XDB_SEGMENT_INDEX_V6.
cache_table(ipv4) -> ?IP2REGION_CACHE_V4;
cache_table(ipv6) -> ?IP2REGION_CACHE_V6.

View File

@ -13,7 +13,7 @@
-export([search/1]). -export([search/1]).
-spec search(Ip :: tuple() | list() | binary() | integer()) -> -spec search(Ip :: tuple() | list() | binary() | integer()) ->
Result :: list() | {error, Reason::atom()}. Result :: list() | {error, term()}.
search(Ip) -> search(Ip) ->
case ip2region_util:ip_to_bytes(Ip) of case ip2region_util:ip_to_bytes(Ip) of
{ok, ipv4, IpBin} -> {ok, ipv4, IpBin} ->
@ -25,23 +25,22 @@ search(Ip) ->
end. end.
do_search(PoolName, Version, IpBin) -> do_search(PoolName, Version, IpBin) ->
CacheTable = cache_table(Version), CacheTable = ip2region_xdb:cache_table(Version),
case ets:lookup(CacheTable, IpBin) of case ets:lookup(CacheTable, IpBin) of
[{_, Region}] -> Region; [{_, Region}] -> Region;
_ -> _ ->
Worker = poolboy:checkout(PoolName, true, infinity), Worker = poolboy:checkout(PoolName, true, infinity),
try try
ip2region_worker:search(Worker, IpBin) Region = ip2region_worker:search(Worker, IpBin),
ets:insert(CacheTable, {IpBin, Region}),
Region
after after
poolboy:checkin(PoolName, Worker) poolboy:checkin(PoolName, Worker)
end end
end. end.
v4_pool() -> v4_pool() ->
case whereis(?IP2REGION_POOL) of case application:get_env(?APP_NAME, v4_pool_name) of
Pid when is_pid(Pid) -> ?IP2REGION_POOL; {ok, PoolName} -> PoolName;
undefined -> ?IP2REGION_POOL_V4 undefined -> ?IP2REGION_POOL
end. end.
cache_table(ipv4) -> ?IP2REGION_CACHE_V4;
cache_table(ipv6) -> ?IP2REGION_CACHE_V6.

View File

@ -46,7 +46,7 @@ cpu_info() ->
{unix, linux} -> {unix, linux} ->
Model = linux_cpu_field("model name"), Model = linux_cpu_field("model name"),
Clock = format_clock_mhz(linux_cpu_field("cpu MHz")), Clock = format_clock_mhz(linux_cpu_field("cpu MHz")),
Cores = trim(os:cmd("grep -c '^processor' /proc/cpuinfo 2>/dev/null")), Cores = string:trim(os:cmd("grep -c '^processor' /proc/cpuinfo 2>/dev/null")),
Threads = Cores, Threads = Cores,
{Model, Clock, Cores, Threads}; {Model, Clock, Cores, Threads};
_ -> _ ->
@ -54,28 +54,28 @@ cpu_info() ->
end. end.
sysctl(Key) -> sysctl(Key) ->
trim(os:cmd("sysctl -n " ++ Key ++ " 2>/dev/null")). string:trim(os:cmd("sysctl -n " ++ Key ++ " 2>/dev/null")).
linux_cpu_field(Key) -> linux_cpu_field(Key) ->
Cmd = "grep -m1 '^" ++ Key ++ "' /proc/cpuinfo 2>/dev/null | cut -d: -f2- | sed 's/^ *//'", Cmd = "grep -m1 '^" ++ Key ++ "' /proc/cpuinfo 2>/dev/null | cut -d: -f2- | sed 's/^ *//'",
trim(os:cmd(Cmd)). string:trim(os:cmd(Cmd)).
first_non_empty(["" | Rest]) -> first_non_empty(Rest); first_non_empty(["" | Rest]) -> first_non_empty(Rest);
first_non_empty([Val | _]) -> Val; first_non_empty([Val | _]) -> Val;
first_non_empty([]) -> "". first_non_empty([]) -> "".
format_clock(HzStr) -> format_clock(HzStr) ->
case string:to_integer(trim(HzStr)) of case string:to_integer(string:trim(HzStr)) of
{ok, Hz, _} when Hz > 1000000000 -> {ok, Hz, _} when Hz > 1000000000 ->
lists:flatten(io_lib:format("~.2f GHz", [Hz / 1000000000])); lists:flatten(io_lib:format("~.2f GHz", [Hz / 1000000000]));
{ok, Hz, _} when Hz > 1000000 -> {ok, Hz, _} when Hz > 1000000 ->
lists:flatten(io_lib:format("~.2f GHz", [Hz / 1000000000])); lists:flatten(io_lib:format("~.2f MHz", [Hz / 1000000]));
_ -> _ ->
"" ""
end. end.
format_clock_mhz(MhzStr) -> format_clock_mhz(MhzStr) ->
case string:to_float(trim(MhzStr)) of case string:to_float(string:trim(MhzStr)) of
{ok, Mhz, _} -> {ok, Mhz, _} ->
lists:flatten(io_lib:format("~.3f GHz", [Mhz / 1000])); lists:flatten(io_lib:format("~.3f GHz", [Mhz / 1000]));
_ -> _ ->
@ -108,8 +108,8 @@ load_test_data(Fd, IpList) ->
run(IpList) -> run(IpList) ->
garbage_collect(), garbage_collect(),
io:format("~nBenchmarks:~n", []), io:format("~nBenchmarks:~n", []),
run_test("file", IpList), run_test("cold", IpList),
run_test("cache", IpList), run_test("warm", IpList),
io:format("~nDone.~n", []). io:format("~nDone.~n", []).
run_test(Label, IpList) -> run_test(Label, IpList) ->
@ -128,5 +128,3 @@ run_test_aux([]) -> ok;
run_test_aux([Ip | Tail]) -> run_test_aux([Ip | Tail]) ->
xdb:search(Ip), xdb:search(Ip),
run_test_aux(Tail). run_test_aux(Tail).
trim(Str) -> string:trim(Str).

View File

@ -6,8 +6,11 @@ pools_started_test() ->
application:stop(ip2region), application:stop(ip2region),
application:unload(ip2region), application:unload(ip2region),
ok = application:load(ip2region), ok = application:load(ip2region),
{ok, Cwd} = file:get_cwd(), TestDir = filename:dirname(?FILE),
V6File = filename:join([Cwd, "..", "..", "data", "ip2region_v6.xdb"]), ErlangDir = filename:dirname(TestDir),
BindingDir = filename:dirname(ErlangDir),
RepoRoot = filename:dirname(BindingDir),
V6File = filename:join([RepoRoot, "data", "ip2region_v6.xdb"]),
ok = application:set_env(ip2region, db, [ ok = application:set_env(ip2region, db, [
{ipv4, "ip2region.xdb"}, {ipv4, "ip2region.xdb"},
{ipv6, V6File} {ipv6, V6File}

View File

@ -30,7 +30,11 @@ invalid_inputs_test_() ->
?_assertEqual({error, bad_ip_format}, ip2region_util:ip_to_bytes("xxx.0.8.0")), ?_assertEqual({error, bad_ip_format}, ip2region_util:ip_to_bytes("xxx.0.8.0")),
?_assertEqual({error, bad_ip_format}, ip2region_util:ip_to_bytes("::ggg")), ?_assertEqual({error, bad_ip_format}, ip2region_util:ip_to_bytes("::ggg")),
?_assertEqual({error, bad_ip_format}, ip2region_util:ip_to_bytes({1,2,3})), ?_assertEqual({error, bad_ip_format}, ip2region_util:ip_to_bytes({1,2,3})),
?_assertEqual({error, bad_ip_format}, ip2region_util:ip_to_bytes(atom)) ?_assertEqual({error, bad_ip_format}, ip2region_util:ip_to_bytes(atom)),
?_assertEqual({error, bad_ip_format}, ip2region_util:ip_to_bytes(-1)),
?_assertEqual({error, bad_ip_format}, ip2region_util:ip_to_bytes(16#100000000)),
?_assertEqual({error, bad_ip_format}, ip2region_util:ipv4_to_n(-1)),
?_assertEqual({error, bad_ip_format}, ip2region_util:ipv4_to_n(16#100000000))
]. ].
legacy_ipv4_to_n_test_() -> legacy_ipv4_to_n_test_() ->