guid.cpp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. #include "guid.h"
  2. #include "ylimits.h"
  3. #include "string.h"
  4. #include <util/string/ascii.h>
  5. #include <util/string/builder.h>
  6. #include <util/stream/format.h>
  7. #include <util/system/unaligned_mem.h>
  8. #include <util/random/easy.h>
  9. namespace {
  10. inline void LowerCaseHex(TString& s) {
  11. for (auto&& c : s) {
  12. c = AsciiToLower(c);
  13. }
  14. }
  15. } // namespace
  16. TString TGUID::AsGuidString() const {
  17. TStringBuilder s;
  18. s.reserve(50);
  19. s << Hex(dw[0], 0) << '-' << Hex(dw[1], 0) << '-' << Hex(dw[2], 0) << '-' << Hex(dw[3], 0);
  20. LowerCaseHex(s);
  21. return std::move(s);
  22. }
  23. TString TGUID::AsUuidString() const {
  24. TStringBuilder s;
  25. s.reserve(50);
  26. s << Hex(dw[0], HF_FULL) << '-';
  27. s << Hex(static_cast<ui16>(dw[1] >> 16), HF_FULL) << '-' << Hex(static_cast<ui16>(dw[1]), HF_FULL) << '-';
  28. s << Hex(static_cast<ui16>(dw[2] >> 16), HF_FULL) << '-' << Hex(static_cast<ui16>(dw[2]), HF_FULL);
  29. s << Hex(dw[3], HF_FULL);
  30. LowerCaseHex(s);
  31. return std::move(s);
  32. }
  33. TGUID TGUID::Create() {
  34. TGUID result;
  35. CreateGuid(&result);
  36. return result;
  37. }
  38. void CreateGuid(TGUID* res) {
  39. ui64* dw = reinterpret_cast<ui64*>(res->dw);
  40. WriteUnaligned<ui64>(&dw[0], RandomNumber<ui64>());
  41. WriteUnaligned<ui64>(&dw[1], RandomNumber<ui64>());
  42. }
  43. TGUID TGUID::CreateTimebased() {
  44. TGUID result;
  45. // GUID_EPOCH_OFFSET is the number of 100-ns intervals between the
  46. // UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00.
  47. constexpr ui64 GUID_EPOCH_OFFSET = 0x01b21dd213814000;
  48. const ui64 timestamp = Now().NanoSeconds() / 100 + GUID_EPOCH_OFFSET;
  49. result.dw[0] = ui32(timestamp & 0xffffffff); // time low
  50. const ui32 timeMid = ui32((timestamp >> 32) & 0xffff);
  51. constexpr ui32 UUID_VERSION = 1;
  52. const ui32 timeHighAndVersion = ui16((timestamp >> 48) & 0x0fff) | (UUID_VERSION << 12);
  53. result.dw[1] = (timeMid << 16) | timeHighAndVersion;
  54. const ui32 clockSeq = RandomNumber<ui32>(0x3fff) | 0x8000;
  55. result.dw[2] = (clockSeq << 16) | RandomNumber<ui16>();
  56. result.dw[3] = RandomNumber<ui32>() | (1 << 24);
  57. return result;
  58. }
  59. TString GetGuidAsString(const TGUID& g) {
  60. return g.AsGuidString();
  61. }
  62. TString CreateGuidAsString() {
  63. return TGUID::Create().AsGuidString();
  64. }
  65. static bool GetDigit(const char c, ui32& digit) {
  66. digit = 0;
  67. if ('0' <= c && c <= '9') {
  68. digit = c - '0';
  69. } else if ('a' <= c && c <= 'f') {
  70. digit = c - 'a' + 10;
  71. } else if ('A' <= c && c <= 'F') {
  72. digit = c - 'A' + 10;
  73. } else {
  74. return false; // non-hex character
  75. }
  76. return true;
  77. }
  78. bool GetGuid(const TStringBuf s, TGUID& result) {
  79. size_t partId = 0;
  80. ui64 partValue = 0;
  81. bool isEmptyPart = true;
  82. for (size_t i = 0; i != s.size(); ++i) {
  83. const char c = s[i];
  84. if (c == '-') {
  85. if (isEmptyPart || partId == 3) { // x-y--z, -x-y-z or x-y-z-m-...
  86. return false;
  87. }
  88. result.dw[partId] = static_cast<ui32>(partValue);
  89. ++partId;
  90. partValue = 0;
  91. isEmptyPart = true;
  92. continue;
  93. }
  94. ui32 digit = 0;
  95. if (!GetDigit(c, digit)) {
  96. return false;
  97. }
  98. partValue = partValue * 16 + digit;
  99. isEmptyPart = false;
  100. // overflow check
  101. if (partValue > Max<ui32>()) {
  102. return false;
  103. }
  104. }
  105. if (partId != 3 || isEmptyPart) { // x-y or x-y-z-
  106. return false;
  107. }
  108. result.dw[partId] = static_cast<ui32>(partValue);
  109. return true;
  110. }
  111. // Parses GUID from s and checks that it's valid.
  112. // In case of error returns TGUID().
  113. TGUID GetGuid(const TStringBuf s) {
  114. TGUID result;
  115. if (GetGuid(s, result)) {
  116. return result;
  117. }
  118. return TGUID();
  119. }
  120. bool GetUuid(const TStringBuf s, TGUID& result) {
  121. if (s.size() != 36) {
  122. return false;
  123. }
  124. size_t partId = 0;
  125. ui64 partValue = 0;
  126. size_t digitCount = 0;
  127. for (size_t i = 0; i < s.size(); ++i) {
  128. const char c = s[i];
  129. if (c == '-') {
  130. if (i != 8 && i != 13 && i != 18 && i != 23) {
  131. return false;
  132. }
  133. continue;
  134. }
  135. ui32 digit = 0;
  136. if (!GetDigit(c, digit)) {
  137. return false;
  138. }
  139. partValue = partValue * 16 + digit;
  140. if (++digitCount == 8) {
  141. result.dw[partId++] = partValue;
  142. digitCount = 0;
  143. }
  144. }
  145. return true;
  146. }
  147. // Parses GUID from uuid and checks that it's valid.
  148. // In case of error returns TGUID().
  149. TGUID GetUuid(const TStringBuf s) {
  150. TGUID result;
  151. if (GetUuid(s, result)) {
  152. return result;
  153. }
  154. return TGUID();
  155. }