Browse Source

Add jsone_inet.erl file

Takeru Ohta 3 years ago
parent
commit
c4777949fe
1 changed files with 104 additions and 0 deletions
  1. 104 0
      src/jsone_inet.erl

+ 104 - 0
src/jsone_inet.erl

@@ -0,0 +1,104 @@
+%%% @doc Utility functions for `inet' module
+%%% @private
+%%% @end
+%%%
+%%% Copyright (c) 2013-2021, Takeru Ohta <phjgt308@gmail.com>
+%%%
+%%% The MIT License
+%%%
+%%% Permission is hereby granted, free of charge, to any person obtaining a copy
+%%% of this software and associated documentation files (the "Software"), to deal
+%%% in the Software without restriction, including without limitation the rights
+%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+%%% copies of the Software, and to permit persons to whom the Software is
+%%% furnished to do so, subject to the following conditions:
+%%%
+%%% The above copyright notice and this permission notice shall be included in
+%%% all copies or substantial portions of the Software.
+%%%
+%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+%%% THE SOFTWARE.
+%%%
+%%%---------------------------------------------------------------------------------------
+-module(jsone_inet).
+
+%%--------------------------------------------------------------------------------
+%% Exported API
+%%--------------------------------------------------------------------------------
+-export([ip_address_to_json_string/1]).
+
+%%--------------------------------------------------------------------------------
+%% Macros
+%%--------------------------------------------------------------------------------
+-define(IS_IPV4_RANGE(X), is_integer(X) andalso 0 =< X andalso X =< 255).
+-define(IS_IPV4(A, B, C, D),
+        ?IS_IPV4_RANGE(A) andalso ?IS_IPV4_RANGE(B) andalso ?IS_IPV4_RANGE(C) andalso ?IS_IPV4_RANGE(D)).
+
+-define(IS_IPV6_RANGE(X), is_integer(X) andalso 0 =< X andalso X =< 65535).
+-define(IS_IPV6(A, B, C, D, E, F, G, H),
+        ?IS_IPV6_RANGE(A) andalso ?IS_IPV6_RANGE(B) andalso ?IS_IPV6_RANGE(C) andalso ?IS_IPV6_RANGE(D) andalso
+        ?IS_IPV6_RANGE(E) andalso ?IS_IPV6_RANGE(F) andalso ?IS_IPV6_RANGE(G) andalso ?IS_IPV6_RANGE(H)).
+
+%%--------------------------------------------------------------------------------
+%% Exported Functions
+%%--------------------------------------------------------------------------------
+
+%% @doc Convert an IP address into a text representation.
+%%
+%% Please refer to the doc of `jsone:ip_address_to_json_string/1' for the detail.
+-spec ip_address_to_json_string(inet:ip_address()|any()) -> {ok, jsone:jsone_string()} | error.
+ip_address_to_json_string({A, B, C, D}) when ?IS_IPV4(A, B, C, D) ->
+    {ok, iolist_to_binary(io_lib:format("~p.~p.~p.~p", [A, B, C, D]))};
+ip_address_to_json_string({A, B, C, D, E, F, G, H}) when ?IS_IPV6(A, B, C, D, E, F, G, H) ->
+    Text =
+        case {A, B, C, D, E, F} of
+            {0, 0, 0, 0, 0, 0} ->
+                %% IPv4-mapped address
+                io_lib:format("::~p.~p.~p.~p", [G bsr 8, G band 16#ff, H bsr 8, G band 16#ff]);
+            {0, 0, 0, 0, 0, 16#ffff} ->
+                %% IPv4-compatible address
+                io_lib:format("::ffff:~p.~p.~p.~p", [G bsr 8, G band 16#ff, H bsr 8, G band 16#ff]);
+            {0, 0, 0, 0, 16#ffff, 0} ->
+                %% IPv4-translated address
+                io_lib:format("::ffff:0:~p.~p.~p.~p", [G bsr 8, G band 16#ff, H bsr 8, G band 16#ff]);
+            _ ->
+                format_ipv6([A, B, C, D, E, F, G, H]);
+        end,
+    {ok, iolist_to_binary(Text)};
+ip_address_to_json_string(_) ->
+    error.
+
+%%--------------------------------------------------------------------------------
+%% Internal Functions
+%%--------------------------------------------------------------------------------
+-spec format_ipv6([0..65535]) -> string().
+format_ipv6(Xs) ->
+    {Ys, _} = format_ipv6(Xs, 0, 0),
+    string:join(Ys, ":").
+
+-spec format_ipv6([0..65535], non_neg_integer(), non_neg_integer()) -> {[string()], not_shortened | shortening | shortened}.
+format_ipv6([], Zeros, MaxZeros) ->
+    {[], not_shortened};
+format_ipv6([X | Xs], Zeros0, MaxZeros) ->
+    Zeros1 =
+        case X of
+            0 -> Zeros0 + 1;
+            _ -> 0
+        end,
+    Longest = Zeros1 > MaxZeros,
+    case format_ipv6(Xs, Zeros1, max(Zeros1, MaxZeros)) of
+        {Ys, not_shortened} ->
+            case Longest of
+                true  -> {Ys, shortening};
+                false -> {[integer_to_list(X, 16) | Ys], not_shortened}
+            end;
+        {Ys, shortening} when X =:= 0 ->
+            {Ys, shortening};
+        {Ys, _} ->
+            {[integer_to_list(X, 16) | Ys], shortened}
+    end.