emqx_guid.erl 4.5 KB

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