You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

376 lines
8.6 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2020 Ian McInerney <ian.s.mcinerney@ieee.org>
  5. * Copyright (C) 2007-2014 Jean-Pierre Charras, jp.charras at wanadoo.fr
  6. * Copyright The KiCad Developers, see AUTHORS.TXT for contributors.
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, you may find one here:
  20. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  21. * or you may search the http://www.gnu.org website for the version 2 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. #include <kiid.h>
  26. #include <boost/random/mersenne_twister.hpp>
  27. #include <boost/uuid/uuid_generators.hpp>
  28. #include <boost/uuid/uuid_io.hpp>
  29. #if BOOST_VERSION >= 106700
  30. #include <boost/uuid/entropy_error.hpp>
  31. #endif
  32. #include <json_common.h>
  33. #include <cctype>
  34. #include <mutex>
  35. #include <utility>
  36. #include <stdlib.h>
  37. #include <wx/log.h>
  38. // boost:mt19937 is not thread-safe
  39. static std::mutex rng_mutex;
  40. // Static rng and generators are used because the overhead of constant seeding is expensive
  41. // We rely on the default non-arg constructor of basic_random_generator to provide a random seed.
  42. // We use a separate rng object for cases where we want to control the basic_random_generator
  43. // initial seed by calling SeedGenerator from unit tests and other special cases.
  44. static boost::mt19937 rng;
  45. static boost::uuids::basic_random_generator<boost::mt19937> randomGenerator;
  46. // These don't have the same performance penalty, but we might as well be consistent
  47. static boost::uuids::string_generator stringGenerator;
  48. static boost::uuids::nil_generator nilGenerator;
  49. // Global nil reference
  50. KIID niluuid( 0 );
  51. // When true, always create nil uuids for performance, when valid ones aren't needed
  52. static bool g_createNilUuids = false;
  53. // For static initialization
  54. KIID& NilUuid()
  55. {
  56. static KIID nil( 0 );
  57. return nil;
  58. }
  59. KIID::KIID()
  60. {
  61. #if BOOST_VERSION >= 106700
  62. try
  63. {
  64. #endif
  65. if( g_createNilUuids )
  66. {
  67. m_uuid = nilGenerator();
  68. }
  69. else
  70. {
  71. std::lock_guard<std::mutex> lock( rng_mutex );
  72. m_uuid = randomGenerator();
  73. }
  74. #if BOOST_VERSION >= 106700
  75. }
  76. catch( const boost::uuids::entropy_error& )
  77. {
  78. wxLogFatalError( "A Boost UUID entropy exception was thrown in %s:%s.",
  79. __FILE__, __FUNCTION__ );
  80. }
  81. #endif
  82. }
  83. KIID::KIID( int null ) :
  84. m_uuid( nilGenerator() )
  85. {
  86. wxASSERT( null == 0 );
  87. }
  88. KIID::KIID( const std::string& aString ) :
  89. m_uuid()
  90. {
  91. if( !aString.empty() && aString.length() <= 8
  92. && std::all_of( aString.begin(), aString.end(),
  93. []( unsigned char c )
  94. {
  95. return std::isxdigit( c );
  96. } ) )
  97. {
  98. // A legacy-timestamp-based UUID has only the last 4 octets filled in.
  99. // Convert them individually to avoid stepping in the little-endian/big-endian
  100. // doo-doo.
  101. for( int i = 0; i < 4; i++ )
  102. {
  103. int start = static_cast<int>( aString.length() ) - 8 + i * 2;
  104. int end = start + 2;
  105. start = std::max( 0, start );
  106. int len = std::max( 0, end - start );
  107. std::string octet = aString.substr( start, len );
  108. m_uuid.data[i + 12] = strtol( octet.data(), nullptr, 16 );
  109. }
  110. }
  111. else
  112. {
  113. try
  114. {
  115. m_uuid = stringGenerator( aString );
  116. }
  117. catch( ... )
  118. {
  119. // Failed to parse string representation; best we can do is assign a new
  120. // random one.
  121. #if BOOST_VERSION >= 106700
  122. try
  123. {
  124. #endif
  125. m_uuid = randomGenerator();
  126. #if BOOST_VERSION >= 106700
  127. }
  128. catch( const boost::uuids::entropy_error& )
  129. {
  130. wxLogFatalError( "A Boost UUID entropy exception was thrown in %s:%s.",
  131. __FILE__, __FUNCTION__ );
  132. }
  133. #endif
  134. }
  135. }
  136. }
  137. KIID::KIID( const char* aString ) :
  138. KIID( std::string( aString ) )
  139. {
  140. }
  141. KIID::KIID( const wxString& aString ) :
  142. KIID( std::string( aString.ToUTF8() ) )
  143. {
  144. }
  145. bool KIID::SniffTest( const wxString& aCandidate )
  146. {
  147. static wxString niluuidStr = niluuid.AsString();
  148. if( aCandidate.Length() != niluuidStr.Length() )
  149. return false;
  150. for( wxChar c : aCandidate )
  151. {
  152. if( c >= '0' && c <= '9' )
  153. continue;
  154. if( c >= 'a' && c <= 'f' )
  155. continue;
  156. if( c >= 'A' && c <= 'F' )
  157. continue;
  158. if( c == '-' )
  159. continue;
  160. return false;
  161. }
  162. return true;
  163. }
  164. KIID::KIID( timestamp_t aTimestamp )
  165. {
  166. m_uuid.data[12] = static_cast<uint8_t>( aTimestamp >> 24 );
  167. m_uuid.data[13] = static_cast<uint8_t>( aTimestamp >> 16 );
  168. m_uuid.data[14] = static_cast<uint8_t>( aTimestamp >> 8 );
  169. m_uuid.data[15] = static_cast<uint8_t>( aTimestamp );
  170. }
  171. bool KIID::IsLegacyTimestamp() const
  172. {
  173. return !m_uuid.data[8] && !m_uuid.data[9] && !m_uuid.data[10] && !m_uuid.data[11];
  174. }
  175. timestamp_t KIID::AsLegacyTimestamp() const
  176. {
  177. timestamp_t ret = 0;
  178. ret |= m_uuid.data[12] << 24;
  179. ret |= m_uuid.data[13] << 16;
  180. ret |= m_uuid.data[14] << 8;
  181. ret |= m_uuid.data[15];
  182. return ret;
  183. }
  184. size_t KIID::Hash() const
  185. {
  186. return boost::uuids::hash_value( m_uuid );
  187. }
  188. void KIID::Clone( const KIID& aUUID )
  189. {
  190. m_uuid = aUUID.m_uuid;
  191. }
  192. wxString KIID::AsString() const
  193. {
  194. return boost::uuids::to_string( m_uuid );
  195. }
  196. std::string KIID::AsStdString() const
  197. {
  198. return boost::uuids::to_string( m_uuid );
  199. }
  200. wxString KIID::AsLegacyTimestampString() const
  201. {
  202. return wxString::Format( "%8.8lX", (unsigned long) AsLegacyTimestamp() );
  203. }
  204. void KIID::ConvertTimestampToUuid()
  205. {
  206. if( !IsLegacyTimestamp() )
  207. return;
  208. m_uuid = randomGenerator();
  209. }
  210. void KIID::Increment()
  211. {
  212. // This obviously destroys uniform distribution, but it can be useful when a
  213. // deterministic replacement for a duplicate ID is required.
  214. for( int i = 15; i >= 0; --i )
  215. {
  216. m_uuid.data[i]++;
  217. if( m_uuid.data[i] != 0 )
  218. break;
  219. }
  220. }
  221. void KIID::CreateNilUuids( bool aNil )
  222. {
  223. g_createNilUuids = aNil;
  224. }
  225. void KIID::SeedGenerator( unsigned int aSeed )
  226. {
  227. rng.seed( aSeed );
  228. randomGenerator = boost::uuids::basic_random_generator<boost::mt19937>( rng );
  229. }
  230. KIID_PATH::KIID_PATH( const wxString& aString )
  231. {
  232. for( const wxString& pathStep : wxSplit( aString, '/' ) )
  233. {
  234. if( !pathStep.empty() )
  235. emplace_back( KIID( pathStep ) );
  236. }
  237. }
  238. bool KIID_PATH::MakeRelativeTo( const KIID_PATH& aPath )
  239. {
  240. KIID_PATH copy = *this;
  241. clear();
  242. if( aPath.size() > copy.size() )
  243. return false; // this path is not contained within aPath
  244. for( size_t i = 0; i < aPath.size(); ++i )
  245. {
  246. if( copy.at( i ) != aPath.at( i ) )
  247. {
  248. *this = copy;
  249. return false; // this path is not contained within aPath
  250. }
  251. }
  252. for( size_t i = aPath.size(); i < copy.size(); ++i )
  253. push_back( copy.at( i ) );
  254. return true;
  255. }
  256. bool KIID_PATH::EndsWith( const KIID_PATH& aPath ) const
  257. {
  258. if( aPath.size() > size() )
  259. return false; // this path can not end aPath
  260. KIID_PATH copyThis = *this;
  261. KIID_PATH copyThat = aPath;
  262. while( !copyThat.empty() )
  263. {
  264. if( *std::prev( copyThis.end() ) != *std::prev( copyThat.end() ) )
  265. return false;
  266. copyThis.pop_back();
  267. copyThat.pop_back();
  268. }
  269. return true;
  270. }
  271. wxString KIID_PATH::AsString() const
  272. {
  273. wxString path;
  274. for( const KIID& pathStep : *this )
  275. path += '/' + pathStep.AsString();
  276. return path;
  277. }
  278. void to_json( nlohmann::json& aJson, const KIID& aKIID )
  279. {
  280. aJson = aKIID.AsString().ToUTF8();
  281. }
  282. void from_json( const nlohmann::json& aJson, KIID& aKIID )
  283. {
  284. aKIID = KIID( aJson.get<std::string>() );
  285. }