forked from dgiot/dgiot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
emqx_guid.erl
150 lines (127 loc) · 4.62 KB
/
emqx_guid.erl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
%%--------------------------------------------------------------------
%% Copyright (c) 2017-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%--------------------------------------------------------------------
%% @doc Generate global unique id for mqtt message.
%%
%% --------------------------------------------------------
%% | Timestamp | NodeID + PID | Sequence |
%% |<------- 64bits ------->|<--- 48bits --->|<- 16bits ->|
%% --------------------------------------------------------
%%
%% 1. Timestamp: erlang:system_time if Erlang >= R18, otherwise os:timestamp
%% 2. NodeId: encode node() to 2 bytes integer
%% 3. Pid: encode pid to 4 bytes integer
%% 4. Sequence: 2 bytes sequence in one process
%%
%% @end
-module(emqx_guid).
-export([ gen/0
, new/0
, timestamp/1
, to_hexstr/1
, from_hexstr/1
, to_base62/1
, from_base62/1
]).
-define(TAG_VERSION, 131).
-define(PID_EXT, 103).
-define(NEW_PID_EXT, 88).
-define(MAX_SEQ, 16#FFFF).
-type(guid() :: <<_:128>>).
%% @doc Generate a global unique id.
-spec(gen() -> guid()).
gen() ->
Guid = case get(guid) of
undefined -> new();
{_Ts, NPid, Seq} -> next(NPid, Seq)
end,
put(guid, Guid), bin(Guid).
new() ->
{ts(), npid(), 0}.
-spec(timestamp(guid()) -> integer()).
timestamp(<<Ts:64, _/binary>>) ->
Ts.
next(NPid, Seq) when Seq >= ?MAX_SEQ ->
{ts(), NPid, 0};
next(NPid, Seq) ->
{ts(), NPid, Seq + 1}.
bin({Ts, NPid, Seq}) ->
<<Ts:64, NPid:48, Seq:16>>.
ts() -> erlang:system_time(micro_seconds).
%% Copied from https://github.com/okeuday/uuid.git.
npid() ->
<<NodeD01, NodeD02, NodeD03, NodeD04, NodeD05,
NodeD06, NodeD07, NodeD08, NodeD09, NodeD10,
NodeD11, NodeD12, NodeD13, NodeD14, NodeD15,
NodeD16, NodeD17, NodeD18, NodeD19, NodeD20>> =
crypto:hash(sha, erlang:list_to_binary(erlang:atom_to_list(node()))),
PidBin =
case erlang:term_to_binary(self()) of
<<?TAG_VERSION, ?PID_EXT, B/binary>> ->
binary:part(B, erlang:byte_size(B), -9);
% format supported in Erlang/OTP 19.0-rc1
% required for Erlang/OTP 23.0 (and Erlang/OTP 22.0-rc2)
<<?TAG_VERSION, ?NEW_PID_EXT, B/binary>> ->
binary:part(B, erlang:byte_size(B), -12)
end,
% 72/86 bits for the Erlang pid
<<PidID1:8, PidID2:8, PidID3:8, PidID4:8, % ID (Node specific, 15 bits)
PidSR1:8, PidSR2:8, PidSR3:8, PidSR4:8, % Serial (extra uniqueness)
PidCreation/binary % Node Creation Count
>> = PidBin,
PidCR1 = case PidCreation of
<<D1>> ->
D1;
<<D1, D2, D3, D4>> ->
D1 bxor D2 bxor D3 bxor D4
end,
% reduce the 160 bit NodeData checksum to 16 bits
NodeByte1 = ((((((((NodeD01 bxor NodeD02)
bxor NodeD03)
bxor NodeD04)
bxor NodeD05)
bxor NodeD06)
bxor NodeD07)
bxor NodeD08)
bxor NodeD09)
bxor NodeD10,
NodeByte2 = (((((((((NodeD11 bxor NodeD12)
bxor NodeD13)
bxor NodeD14)
bxor NodeD15)
bxor NodeD16)
bxor NodeD17)
bxor NodeD18)
bxor NodeD19)
bxor NodeD20)
bxor PidCR1,
% reduce the Erlang pid to 32 bits
PidByte1 = PidID1 bxor PidSR4,
PidByte2 = PidID2 bxor PidSR3,
PidByte3 = PidID3 bxor PidSR2,
PidByte4 = PidID4 bxor PidSR1,
<<NPid:48>> = <<NodeByte1:8, NodeByte2:8,
PidByte1:8, PidByte2:8,
PidByte3:8, PidByte4:8>>,
NPid.
to_hexstr(I) when byte_size(I) =:= 16 ->
emqx_misc:bin2hexstr_A_F(I).
from_hexstr(S) when byte_size(S) =:= 32 ->
emqx_misc:hexstr2bin(S).
to_base62(<<I:128>>) ->
emqx_base62:encode(I).
from_base62(S) ->
I = binary_to_integer( emqx_base62:decode(S)),
<<I:128>>.