uuid.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. {
  2. This file is part of the Free Pascal packages.
  3. Copyright (c) 1999-2006 by the Free Pascal development team
  4. Implements a UUID generation algorithm (RFC 4122)
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$IFNDEF FPC_DOTTEDUNITS}
  12. unit uuid;
  13. {$ENDIF FPC_DOTTEDUNITS}
  14. interface
  15. {$mode objfpc}
  16. {$h+}
  17. {$IFDEF FPC_DOTTEDUNITS}
  18. uses
  19. System.SysUtils, System.DateUtils, System.Hash.Md5, System.Hash.Sha1;
  20. {$ELSE FPC_DOTTEDUNITS}
  21. uses
  22. SysUtils, DateUtils, md5, sha1;
  23. {$ENDIF FPC_DOTTEDUNITS}
  24. (******************************************************************************
  25. * types and constants
  26. ******************************************************************************)
  27. type
  28. {$ifdef VER2_0}
  29. uuid_t = packed record
  30. case integer of
  31. 1 : (
  32. Data1 : DWord;
  33. Data2 : word;
  34. Data3 : word;
  35. Data4 : array[0..7] of byte;
  36. );
  37. 2 : (
  38. D1 : DWord;
  39. D2 : word;
  40. D3 : word;
  41. D4 : array[0..7] of byte;
  42. );
  43. 3 : ( { uuid fields according to RFC4122 }
  44. time_low : dword; // The low field of the timestamp
  45. time_mid : word; // The middle field of the timestamp
  46. time_hi_and_version : word; // The high field of the timestamp multiplexed with the version
  47. clock_seq_hi_and_reserved : byte; // The high field of the clock sequence multiplexed with the var
  48. clock_seq_low : byte; // The low field of the clock sequence
  49. node : array[0..5] of byte; // The spatially unique node identifier
  50. );
  51. end;
  52. {$else VER2_0}
  53. uuid_t = TGuid;
  54. {$endif VER2_0}
  55. uuid_time_t = qword;
  56. uuid_node_t = array[0..5] of byte;
  57. unsigned16 = word;
  58. uuid_state = record
  59. ts : uuid_time_t; // saved timestamp
  60. node : uuid_node_t; // saved node ID
  61. cs : unsigned16; // saved clock sequence
  62. end;
  63. const
  64. UUID_VERSION_1 = $1; // The time-based version specified in this document.
  65. UUID_VERSION_2 = $2; // DCE Security version, with embedded POSIX UIDs.
  66. UUID_VERSION_3 = $3; // The name-based version specified in this document that uses MD5 hashing.
  67. UUID_VERSION_4 = $4; // The randomly or pseudo-randomly generated version specified in this document.
  68. UUID_VERSION_5 = $5; // The name-based version specified in this document that uses SHA-1 hashing.
  69. { set the following to the number of 100ns ticks of the actual resolution of your system's clock }
  70. UUIDS_PER_TICK = 1024;
  71. (******************************************************************************
  72. * core uuid functions
  73. ******************************************************************************)
  74. { uuid_initialize -- used to initialize the uuid_create function }
  75. procedure uuid_initialize(const state: uuid_state);
  76. { uuid_create -- generator a UUID }
  77. function uuid_create(out uuid: uuid_t): boolean;
  78. { uuid_finalize -- returns the current state }
  79. procedure uuid_finalize(out state: uuid_state);
  80. { uuid_create_md5_from_name -- create a version 3 (MD5) UUID using a "name" from a "name space" }
  81. procedure uuid_create_md5_from_name(out uuid: uuid_t; const nsid: uuid_t; const name: RawByteString);
  82. { uuid_create_sha1_from_name -- create a version 5 (SHA-1) UUID using a "name" from a "name space" }
  83. procedure uuid_create_sha1_from_name(out uuid: uuid_t; const nsid: uuid_t; const name: RawByteString);
  84. { uuid_compare -- Compare two UUID's "lexically" }
  85. function uuid_compare(const u1, u2: uuid_t): integer;
  86. (******************************************************************************
  87. * auxilary functions
  88. ******************************************************************************)
  89. { read_state -- read UUID generator state from non-volatile store }
  90. function read_state(out clockseq: unsigned16; out timestamp: uuid_time_t; out node: uuid_node_t): boolean;
  91. { write_state -- save UUID generator state back to non-volatile storage }
  92. procedure write_state(var clockseq: unsigned16; const timestamp: uuid_time_t; const node: uuid_node_t);
  93. { format_uuid_v1 -- make a UUID from the timestamp, clockseq, and node ID }
  94. procedure format_uuid_v1(out uuid: uuid_t; const clockseq: unsigned16; const timestamp: uuid_time_t; const node: uuid_node_t);
  95. { format_uuid_v3or5 -- make a UUID from a (pseudo)random 128-bit number }
  96. procedure format_uuid_v3or5(out uuid: uuid_t; const hash: pointer; const v: integer);
  97. { get_current_time -- get time as 60-bit 100ns ticks since UUID epoch. Compensate for the fact that real clock resolution is less than 100ns. }
  98. procedure get_current_time(out timestamp: uuid_time_t);
  99. (******************************************************************************
  100. * system functions
  101. ******************************************************************************)
  102. { get_system_time -- system dependent call to get the current system time. Returned as 100ns ticks since UUID epoch, but resolution may be less than 100ns. }
  103. procedure get_system_time(out timestamp: uuid_time_t);
  104. { true_random -- generate a crypto-quality random number. }
  105. function true_random: unsigned16;
  106. implementation
  107. { uuid_initialize }
  108. var
  109. current_state : uuid_state;
  110. current_node : uuid_node_t;
  111. procedure uuid_initialize(const state: uuid_state);
  112. begin
  113. Randomize;
  114. current_node[0] := Random($100);
  115. current_node[1] := Random($100);
  116. current_node[2] := Random($100);
  117. current_node[3] := Random($100);
  118. current_node[4] := Random($100);
  119. current_node[5] := Random($100);
  120. current_state := state;
  121. end;
  122. { uuid_finalize }
  123. procedure uuid_finalize(out state: uuid_state);
  124. begin
  125. state := current_state;
  126. end;
  127. { uuid_create }
  128. function uuid_create(out uuid: uuid_t): boolean;
  129. var
  130. timestamp: uuid_time_t;
  131. last_time: uuid_time_t;
  132. clockseq: unsigned16;
  133. last_node: uuid_node_t;
  134. f: boolean;
  135. begin
  136. (* acquire system-wide lock so we're alone *)
  137. // LOCK;
  138. (* get time, node ID, saved state from non-volatile storage *)
  139. get_current_time(timestamp);
  140. f := read_state(clockseq, last_time, last_node);
  141. (* if no NV state, or if clock went backwards, or node ID
  142. changed (e.g., new network card) change clockseq *)
  143. if not f or not CompareMem(@current_node, @last_node, sizeof(uuid_node_t)) then
  144. clockseq := true_random() else
  145. if timestamp < last_time then
  146. clockseq := clockseq + 1;
  147. (* save the state for next time *)
  148. write_state(clockseq, timestamp, current_node);
  149. // UNLOCK;
  150. (* stuff fields into the UUID *)
  151. format_uuid_v1(uuid, clockseq, timestamp, current_node);
  152. Result := true;
  153. end;
  154. { uuid_create_md5_from_name }
  155. procedure uuid_create_md5_from_name(out uuid: uuid_t; const nsid: uuid_t; const name: RawByteString);
  156. var
  157. net_nsid: uuid_t;
  158. c: TMDContext;
  159. hash: TMDDigest;
  160. begin
  161. (* put name space ID in network byte order so it hashes the same
  162. no matter what endian machine we're on *)
  163. net_nsid := nsid;
  164. net_nsid.time_low := ntobe(net_nsid.time_low);
  165. net_nsid.time_mid := ntobe(net_nsid.time_mid);
  166. net_nsid.time_hi_and_version := ntobe(net_nsid.time_hi_and_version);
  167. MDInit(c, MD_VERSION_5);
  168. MDUpdate(c, net_nsid, sizeof(net_nsid));
  169. MDUpdate(c, PAnsiChar(name)^, Length(name));
  170. MDFinal(c, hash);
  171. (* the hash is in network byte order at this point *)
  172. format_uuid_v3or5(uuid, @hash, UUID_VERSION_3);
  173. end;
  174. { uuid_create_sha1_from_name }
  175. procedure uuid_create_sha1_from_name(out uuid: uuid_t; const nsid: uuid_t; const name: RawByteString);
  176. var
  177. net_nsid: uuid_t;
  178. c: TSHA1Context;
  179. hash: TSHA1Digest;
  180. begin
  181. (* put name space ID in network byte order so it hashes the same
  182. no matter what endian machine we're on *)
  183. net_nsid := nsid;
  184. net_nsid.time_low := ntobe(net_nsid.time_low);
  185. net_nsid.time_mid := ntobe(net_nsid.time_mid);
  186. net_nsid.time_hi_and_version := ntobe(net_nsid.time_hi_and_version);
  187. SHA1Init(c);
  188. SHA1Update(c, net_nsid, sizeof(net_nsid));
  189. SHA1Update(c, PAnsiChar(name)^, Length(name));
  190. SHA1Final(c, hash);
  191. (* the hash is in network byte order at this point *)
  192. format_uuid_v3or5(uuid, @hash, UUID_VERSION_5);
  193. end;
  194. { uuid_compare }
  195. function uuid_compare(const u1, u2: uuid_t): integer;
  196. begin
  197. Result := pinteger(@u1)[0] - pinteger(@u2)[0];
  198. if Result <> 0 then Exit;
  199. Result := pinteger(@u1)[1] - pinteger(@u2)[1];
  200. if Result <> 0 then Exit;
  201. Result := pinteger(@u1)[2] - pinteger(@u2)[2];
  202. if Result <> 0 then Exit;
  203. Result := pinteger(@u1)[3] - pinteger(@u2)[3];
  204. end;
  205. { read_state }
  206. function read_state(out clockseq: unsigned16; out timestamp: uuid_time_t; out node: uuid_node_t): boolean;
  207. begin
  208. clockseq := current_state.cs;
  209. timestamp := current_state.ts;
  210. node := current_state.node;
  211. Result := true;
  212. end;
  213. { write_state }
  214. procedure write_state(var clockseq: unsigned16; const timestamp: uuid_time_t; const node: uuid_node_t);
  215. begin
  216. (* always save state to volatile shared state *)
  217. current_state.cs := clockseq;
  218. current_state.ts := timestamp;
  219. current_state.node := node;
  220. end;
  221. { format_uuid_v1 }
  222. procedure format_uuid_v1(out uuid: uuid_t; const clockseq: unsigned16; const timestamp: uuid_time_t; const node: uuid_node_t);
  223. begin
  224. uuid.time_low := timestamp and $FFFFFFFF;
  225. uuid.time_mid := (timestamp shr 32) and $FFFF;
  226. uuid.time_hi_and_version := (timestamp shr 48) and $0FFF;
  227. uuid.time_hi_and_version := uuid.time_hi_and_version or (UUID_VERSION_1 shl 12);
  228. uuid.clock_seq_low := clockseq and $FF;
  229. uuid.clock_seq_hi_and_reserved := (clockseq shr 8) and $3F;
  230. uuid.clock_seq_hi_and_reserved := uuid.clock_seq_hi_and_reserved or $80;
  231. uuid.node := node;
  232. end;
  233. { format_uuid_v3or5 }
  234. procedure format_uuid_v3or5(out uuid: uuid_t; const hash: pointer; const v: integer);
  235. begin
  236. (* convert UUID to local byte order *)
  237. move(hash^, uuid, sizeof(uuid));
  238. uuid.time_low := beton(uuid.time_low);
  239. uuid.time_mid := beton(uuid.time_mid);
  240. uuid.time_hi_and_version := beton(uuid.time_hi_and_version);
  241. (* put in the variant and version bits *)
  242. uuid.time_hi_and_version := uuid.time_hi_and_version and $0FFF;
  243. uuid.time_hi_and_version := uuid.time_hi_and_version or (v shl 12);
  244. uuid.clock_seq_hi_and_reserved := $3F;
  245. uuid.clock_seq_hi_and_reserved := uuid.clock_seq_hi_and_reserved or $80;
  246. end;
  247. { get_current_time }
  248. var
  249. time_last: uuid_time_t;
  250. uuids_this_tick: unsigned16 = UUIDS_PER_TICK;
  251. procedure get_current_time(out timestamp: uuid_time_t);
  252. var
  253. time_now: uuid_time_t;
  254. begin
  255. while true do
  256. begin
  257. get_system_time(time_now);
  258. (* if clock reading changed since last UUID generated, *)
  259. if time_last <> time_now then
  260. begin
  261. (* reset count of uuids gen'd with this clock reading *)
  262. uuids_this_tick := 0;
  263. time_last := time_now;
  264. Break;
  265. end;
  266. if uuids_this_tick < UUIDS_PER_TICK then
  267. begin
  268. uuids_this_tick := uuids_this_tick + 1;
  269. Break;
  270. end;
  271. (* going too fast for our clock; spin *)
  272. end;
  273. (* add the count of uuids to low order bits of the clock reading *)
  274. timestamp := time_now + uuids_this_tick;
  275. end;
  276. { get_system_time }
  277. procedure get_system_time(out timestamp: uuid_time_t);
  278. var
  279. Epoch:TDateTime;
  280. begin
  281. Epoch := EncodeDateTime(1582, 10, 15, 0, 0, 0, 0);
  282. timestamp := 10000*MilliSecondsBetween(Epoch, Now);
  283. end;
  284. { true_random }
  285. function true_random: unsigned16;
  286. begin
  287. Result := Random($10000);
  288. end;
  289. end.