1%% This Source Code Form is subject to the terms of the Mozilla Public
2%% License, v. 2.0. If a copy of the MPL was not distributed with this
3%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
4%%
5%% Copyright (c) 2007-2021 VMware, Inc. or its affiliates.  All rights reserved.
6%%
7
8-module(pid_recomposition).
9
10
11%% API
12-export([
13    to_binary/1,
14    from_binary/1,
15    decompose/1,
16    recompose/1
17]).
18
19-define(TTB_PREFIX, 131).
20
21-define(NEW_PID_EXT, 88).
22-define(PID_EXT, 103).
23-define(ATOM_UTF8_EXT, 118).
24-define(SMALL_ATOM_UTF8_EXT, 119).
25
26%%
27%% API
28%%
29
30-spec decompose(pid()) -> #{atom() => any()}.
31decompose(Pid) ->
32    from_binary(term_to_binary(Pid, [{minor_version, 2}])).
33
34-spec from_binary(binary()) -> #{atom() => any()}.
35from_binary(Bin) ->
36    PidData = case Bin of
37        %% Erlang 23+
38        <<?TTB_PREFIX, ?NEW_PID_EXT, Val0/binary>> -> Val0;
39        %% Erlang 22
40        <<?TTB_PREFIX, ?PID_EXT, Val1/binary>> -> Val1
41    end,
42    {Node, Rest2} = case PidData of
43        <<?ATOM_UTF8_EXT, AtomLen:16/integer, Node0:AtomLen/binary, Rest1/binary>> ->
44            {Node0, Rest1};
45        <<?SMALL_ATOM_UTF8_EXT, AtomLen/integer, Node0:AtomLen/binary, Rest1/binary>> ->
46            {Node0, Rest1}
47    end,
48    {ID, Serial, Creation} = case Rest2 of
49        %% NEW_PID_EXT on Erlang 23+
50        <<ID0:32/integer, Serial0:32/integer, Creation0:32/integer>> ->
51            {ID0, Serial0, Creation0};
52        %% PID_EXT on Erlang 22
53        <<ID1:32/integer, Serial1:32/integer, Creation1:8/integer>> ->
54            {ID1, Serial1, Creation1}
55    end,
56    #{
57        node     => binary_to_atom(Node, utf8),
58        id       => ID,
59        serial   => Serial,
60        creation => Creation
61    }.
62
63-spec to_binary(#{atom() => any()}) -> binary().
64to_binary(#{node := Node, id := ID, serial := Serial, creation := Creation}) ->
65    BinNode = atom_to_binary(Node, utf8),
66    NodeLen = byte_size(BinNode),
67    <<?TTB_PREFIX:8/unsigned, ?NEW_PID_EXT:8/unsigned, ?ATOM_UTF8_EXT:8/unsigned, NodeLen:16/unsigned, BinNode/binary, ID:32, Serial:32, Creation:32>>.
68
69-spec recompose(#{atom() => any()}) -> pid().
70recompose(M) ->
71    binary_to_term(to_binary(M)).
72