ObjectId.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707
  1. /* Copyright 2010-present MongoDB Inc.
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. using System;
  16. using System.Diagnostics;
  17. using System.Runtime.CompilerServices;
  18. using System.Security;
  19. using System.Threading;
  20. namespace MongoDB.Bson
  21. {
  22. /// <summary>
  23. /// Represents an ObjectId (see also BsonObjectId).
  24. /// </summary>
  25. [Serializable]
  26. public struct ObjectId : IComparable<ObjectId>, IEquatable<ObjectId>, IConvertible
  27. {
  28. // private static fields
  29. private static readonly ObjectId __emptyInstance = default(ObjectId);
  30. private static readonly long __random = CalculateRandomValue();
  31. private static int __staticIncrement = (new Random()).Next();
  32. // private fields
  33. private readonly int _a;
  34. private readonly int _b;
  35. private readonly int _c;
  36. // constructors
  37. /// <summary>
  38. /// Initializes a new instance of the ObjectId class.
  39. /// </summary>
  40. /// <param name="bytes">The bytes.</param>
  41. public ObjectId(byte[] bytes)
  42. {
  43. if (bytes == null)
  44. {
  45. throw new ArgumentNullException("bytes");
  46. }
  47. if (bytes.Length != 12)
  48. {
  49. throw new ArgumentException("Byte array must be 12 bytes long", "bytes");
  50. }
  51. FromByteArray(bytes, 0, out _a, out _b, out _c);
  52. }
  53. /// <summary>
  54. /// Initializes a new instance of the ObjectId class.
  55. /// </summary>
  56. /// <param name="bytes">The bytes.</param>
  57. /// <param name="index">The index into the byte array where the ObjectId starts.</param>
  58. internal ObjectId(byte[] bytes, int index)
  59. {
  60. FromByteArray(bytes, index, out _a, out _b, out _c);
  61. }
  62. /// <summary>
  63. /// Initializes a new instance of the ObjectId class.
  64. /// </summary>
  65. /// <param name="timestamp">The timestamp (expressed as a DateTime).</param>
  66. /// <param name="machine">The machine hash.</param>
  67. /// <param name="pid">The PID.</param>
  68. /// <param name="increment">The increment.</param>
  69. [Obsolete("This constructor will be removed in a later release.")]
  70. public ObjectId(DateTime timestamp, int machine, short pid, int increment)
  71. : this(GetTimestampFromDateTime(timestamp), machine, pid, increment)
  72. {
  73. }
  74. /// <summary>
  75. /// Initializes a new instance of the ObjectId class.
  76. /// </summary>
  77. /// <param name="timestamp">The timestamp.</param>
  78. /// <param name="machine">The machine hash.</param>
  79. /// <param name="pid">The PID.</param>
  80. /// <param name="increment">The increment.</param>
  81. [Obsolete("This constructor will be removed in a later release.")]
  82. public ObjectId(int timestamp, int machine, short pid, int increment)
  83. {
  84. if ((machine & 0xff000000) != 0)
  85. {
  86. throw new ArgumentOutOfRangeException("machine", "The machine value must be between 0 and 16777215 (it must fit in 3 bytes).");
  87. }
  88. if ((increment & 0xff000000) != 0)
  89. {
  90. throw new ArgumentOutOfRangeException("increment", "The increment value must be between 0 and 16777215 (it must fit in 3 bytes).");
  91. }
  92. _a = timestamp;
  93. _b = (machine << 8) | (((int)pid >> 8) & 0xff);
  94. _c = ((int)pid << 24) | increment;
  95. }
  96. /// <summary>
  97. /// Initializes a new instance of the ObjectId class.
  98. /// </summary>
  99. /// <param name="value">The value.</param>
  100. public ObjectId(string value)
  101. {
  102. if (value == null)
  103. {
  104. throw new ArgumentNullException("value");
  105. }
  106. var bytes = BsonUtils.ParseHexString(value);
  107. FromByteArray(bytes, 0, out _a, out _b, out _c);
  108. }
  109. private ObjectId(int a, int b, int c)
  110. {
  111. _a = a;
  112. _b = b;
  113. _c = c;
  114. }
  115. // public static properties
  116. /// <summary>
  117. /// Gets an instance of ObjectId where the value is empty.
  118. /// </summary>
  119. public static ObjectId Empty
  120. {
  121. get { return __emptyInstance; }
  122. }
  123. // public properties
  124. /// <summary>
  125. /// Gets the timestamp.
  126. /// </summary>
  127. public int Timestamp
  128. {
  129. get { return _a; }
  130. }
  131. /// <summary>
  132. /// Gets the machine.
  133. /// </summary>
  134. [Obsolete("This property will be removed in a later release.")]
  135. public int Machine
  136. {
  137. get { return (_b >> 8) & 0xffffff; }
  138. }
  139. /// <summary>
  140. /// Gets the PID.
  141. /// </summary>
  142. [Obsolete("This property will be removed in a later release.")]
  143. public short Pid
  144. {
  145. get { return (short)(((_b << 8) & 0xff00) | ((_c >> 24) & 0x00ff)); }
  146. }
  147. /// <summary>
  148. /// Gets the increment.
  149. /// </summary>
  150. [Obsolete("This property will be removed in a later release.")]
  151. public int Increment
  152. {
  153. get { return _c & 0xffffff; }
  154. }
  155. /// <summary>
  156. /// Gets the creation time (derived from the timestamp).
  157. /// </summary>
  158. public DateTime CreationTime
  159. {
  160. get { return BsonConstants.UnixEpoch.AddSeconds((uint)Timestamp); }
  161. }
  162. // public operators
  163. /// <summary>
  164. /// Compares two ObjectIds.
  165. /// </summary>
  166. /// <param name="lhs">The first ObjectId.</param>
  167. /// <param name="rhs">The other ObjectId</param>
  168. /// <returns>True if the first ObjectId is less than the second ObjectId.</returns>
  169. public static bool operator <(ObjectId lhs, ObjectId rhs)
  170. {
  171. return lhs.CompareTo(rhs) < 0;
  172. }
  173. /// <summary>
  174. /// Compares two ObjectIds.
  175. /// </summary>
  176. /// <param name="lhs">The first ObjectId.</param>
  177. /// <param name="rhs">The other ObjectId</param>
  178. /// <returns>True if the first ObjectId is less than or equal to the second ObjectId.</returns>
  179. public static bool operator <=(ObjectId lhs, ObjectId rhs)
  180. {
  181. return lhs.CompareTo(rhs) <= 0;
  182. }
  183. /// <summary>
  184. /// Compares two ObjectIds.
  185. /// </summary>
  186. /// <param name="lhs">The first ObjectId.</param>
  187. /// <param name="rhs">The other ObjectId.</param>
  188. /// <returns>True if the two ObjectIds are equal.</returns>
  189. public static bool operator ==(ObjectId lhs, ObjectId rhs)
  190. {
  191. return lhs.Equals(rhs);
  192. }
  193. /// <summary>
  194. /// Compares two ObjectIds.
  195. /// </summary>
  196. /// <param name="lhs">The first ObjectId.</param>
  197. /// <param name="rhs">The other ObjectId.</param>
  198. /// <returns>True if the two ObjectIds are not equal.</returns>
  199. public static bool operator !=(ObjectId lhs, ObjectId rhs)
  200. {
  201. return !(lhs == rhs);
  202. }
  203. /// <summary>
  204. /// Compares two ObjectIds.
  205. /// </summary>
  206. /// <param name="lhs">The first ObjectId.</param>
  207. /// <param name="rhs">The other ObjectId</param>
  208. /// <returns>True if the first ObjectId is greather than or equal to the second ObjectId.</returns>
  209. public static bool operator >=(ObjectId lhs, ObjectId rhs)
  210. {
  211. return lhs.CompareTo(rhs) >= 0;
  212. }
  213. /// <summary>
  214. /// Compares two ObjectIds.
  215. /// </summary>
  216. /// <param name="lhs">The first ObjectId.</param>
  217. /// <param name="rhs">The other ObjectId</param>
  218. /// <returns>True if the first ObjectId is greather than the second ObjectId.</returns>
  219. public static bool operator >(ObjectId lhs, ObjectId rhs)
  220. {
  221. return lhs.CompareTo(rhs) > 0;
  222. }
  223. // public static methods
  224. /// <summary>
  225. /// Generates a new ObjectId with a unique value.
  226. /// </summary>
  227. /// <returns>An ObjectId.</returns>
  228. public static ObjectId GenerateNewId()
  229. {
  230. return GenerateNewId(GetTimestampFromDateTime(DateTime.UtcNow));
  231. }
  232. /// <summary>
  233. /// Generates a new ObjectId with a unique value (with the timestamp component based on a given DateTime).
  234. /// </summary>
  235. /// <param name="timestamp">The timestamp component (expressed as a DateTime).</param>
  236. /// <returns>An ObjectId.</returns>
  237. public static ObjectId GenerateNewId(DateTime timestamp)
  238. {
  239. return GenerateNewId(GetTimestampFromDateTime(timestamp));
  240. }
  241. /// <summary>
  242. /// Generates a new ObjectId with a unique value (with the given timestamp).
  243. /// </summary>
  244. /// <param name="timestamp">The timestamp component.</param>
  245. /// <returns>An ObjectId.</returns>
  246. public static ObjectId GenerateNewId(int timestamp)
  247. {
  248. int increment = Interlocked.Increment(ref __staticIncrement) & 0x00ffffff; // only use low order 3 bytes
  249. return Create(timestamp, __random, increment);
  250. }
  251. /// <summary>
  252. /// Packs the components of an ObjectId into a byte array.
  253. /// </summary>
  254. /// <param name="timestamp">The timestamp.</param>
  255. /// <param name="machine">The machine hash.</param>
  256. /// <param name="pid">The PID.</param>
  257. /// <param name="increment">The increment.</param>
  258. /// <returns>A byte array.</returns>
  259. [Obsolete("This method will be removed in a later release.")]
  260. public static byte[] Pack(int timestamp, int machine, short pid, int increment)
  261. {
  262. if ((machine & 0xff000000) != 0)
  263. {
  264. throw new ArgumentOutOfRangeException("machine", "The machine value must be between 0 and 16777215 (it must fit in 3 bytes).");
  265. }
  266. if ((increment & 0xff000000) != 0)
  267. {
  268. throw new ArgumentOutOfRangeException("increment", "The increment value must be between 0 and 16777215 (it must fit in 3 bytes).");
  269. }
  270. byte[] bytes = new byte[12];
  271. bytes[0] = (byte)(timestamp >> 24);
  272. bytes[1] = (byte)(timestamp >> 16);
  273. bytes[2] = (byte)(timestamp >> 8);
  274. bytes[3] = (byte)(timestamp);
  275. bytes[4] = (byte)(machine >> 16);
  276. bytes[5] = (byte)(machine >> 8);
  277. bytes[6] = (byte)(machine);
  278. bytes[7] = (byte)(pid >> 8);
  279. bytes[8] = (byte)(pid);
  280. bytes[9] = (byte)(increment >> 16);
  281. bytes[10] = (byte)(increment >> 8);
  282. bytes[11] = (byte)(increment);
  283. return bytes;
  284. }
  285. /// <summary>
  286. /// Parses a string and creates a new ObjectId.
  287. /// </summary>
  288. /// <param name="s">The string value.</param>
  289. /// <returns>A ObjectId.</returns>
  290. public static ObjectId Parse(string s)
  291. {
  292. if (s == null)
  293. {
  294. throw new ArgumentNullException("s");
  295. }
  296. ObjectId objectId;
  297. if (TryParse(s, out objectId))
  298. {
  299. return objectId;
  300. }
  301. else
  302. {
  303. var message = string.Format("'{0}' is not a valid 24 digit hex string.", s);
  304. throw new FormatException(message);
  305. }
  306. }
  307. /// <summary>
  308. /// Tries to parse a string and create a new ObjectId.
  309. /// </summary>
  310. /// <param name="s">The string value.</param>
  311. /// <param name="objectId">The new ObjectId.</param>
  312. /// <returns>True if the string was parsed successfully.</returns>
  313. public static bool TryParse(string s, out ObjectId objectId)
  314. {
  315. // don't throw ArgumentNullException if s is null
  316. if (s != null && s.Length == 24)
  317. {
  318. byte[] bytes;
  319. if (BsonUtils.TryParseHexString(s, out bytes))
  320. {
  321. objectId = new ObjectId(bytes);
  322. return true;
  323. }
  324. }
  325. objectId = default(ObjectId);
  326. return false;
  327. }
  328. /// <summary>
  329. /// Unpacks a byte array into the components of an ObjectId.
  330. /// </summary>
  331. /// <param name="bytes">A byte array.</param>
  332. /// <param name="timestamp">The timestamp.</param>
  333. /// <param name="machine">The machine hash.</param>
  334. /// <param name="pid">The PID.</param>
  335. /// <param name="increment">The increment.</param>
  336. [Obsolete("This method will be removed in a later release.")]
  337. public static void Unpack(byte[] bytes, out int timestamp, out int machine, out short pid, out int increment)
  338. {
  339. if (bytes == null)
  340. {
  341. throw new ArgumentNullException("bytes");
  342. }
  343. if (bytes.Length != 12)
  344. {
  345. throw new ArgumentOutOfRangeException("bytes", "Byte array must be 12 bytes long.");
  346. }
  347. timestamp = (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3];
  348. machine = (bytes[4] << 16) + (bytes[5] << 8) + bytes[6];
  349. pid = (short)((bytes[7] << 8) + bytes[8]);
  350. increment = (bytes[9] << 16) + (bytes[10] << 8) + bytes[11];
  351. }
  352. // private static methods
  353. private static long CalculateRandomValue()
  354. {
  355. var seed = (int)DateTime.UtcNow.Ticks ^ GetMachineHash() ^ GetPid();
  356. var random = new Random(seed);
  357. var high = random.Next();
  358. var low = random.Next();
  359. var combined = (long)((ulong)(uint)high << 32 | (ulong)(uint)low);
  360. return combined & 0xffffffffff; // low order 5 bytes
  361. }
  362. private static ObjectId Create(int timestamp, long random, int increment)
  363. {
  364. if (random < 0 || random > 0xffffffffff)
  365. {
  366. throw new ArgumentOutOfRangeException(nameof(random), "The random value must be between 0 and 1099511627775 (it must fit in 5 bytes).");
  367. }
  368. if (increment < 0 || increment > 0xffffff)
  369. {
  370. throw new ArgumentOutOfRangeException(nameof(increment), "The increment value must be between 0 and 16777215 (it must fit in 3 bytes).");
  371. }
  372. var a = timestamp;
  373. var b = (int)(random >> 8); // first 4 bytes of random
  374. var c = (int)(random << 24) | increment; // 5th byte of random and 3 byte increment
  375. return new ObjectId(a, b, c);
  376. }
  377. /// <summary>
  378. /// Gets the current process id. This method exists because of how CAS operates on the call stack, checking
  379. /// for permissions before executing the method. Hence, if we inlined this call, the calling method would not execute
  380. /// before throwing an exception requiring the try/catch at an even higher level that we don't necessarily control.
  381. /// </summary>
  382. [MethodImpl(MethodImplOptions.NoInlining)]
  383. private static int GetCurrentProcessId()
  384. {
  385. return Process.GetCurrentProcess().Id;
  386. }
  387. private static int GetMachineHash()
  388. {
  389. // use instead of Dns.HostName so it will work offline
  390. var machineName = GetMachineName();
  391. return 0x00ffffff & machineName.GetHashCode(); // use first 3 bytes of hash
  392. }
  393. private static string GetMachineName()
  394. {
  395. return Environment.MachineName;
  396. }
  397. private static short GetPid()
  398. {
  399. try
  400. {
  401. return (short)GetCurrentProcessId(); // use low order two bytes only
  402. }
  403. catch (SecurityException)
  404. {
  405. return 0;
  406. }
  407. }
  408. private static int GetTimestampFromDateTime(DateTime timestamp)
  409. {
  410. var secondsSinceEpoch = (long)Math.Floor((BsonUtils.ToUniversalTime(timestamp) - BsonConstants.UnixEpoch).TotalSeconds);
  411. if (secondsSinceEpoch < uint.MinValue || secondsSinceEpoch > uint.MaxValue)
  412. {
  413. throw new ArgumentOutOfRangeException("timestamp");
  414. }
  415. return (int)(uint)secondsSinceEpoch;
  416. }
  417. private static void FromByteArray(byte[] bytes, int offset, out int a, out int b, out int c)
  418. {
  419. a = (bytes[offset] << 24) | (bytes[offset + 1] << 16) | (bytes[offset + 2] << 8) | bytes[offset + 3];
  420. b = (bytes[offset + 4] << 24) | (bytes[offset + 5] << 16) | (bytes[offset + 6] << 8) | bytes[offset + 7];
  421. c = (bytes[offset + 8] << 24) | (bytes[offset + 9] << 16) | (bytes[offset + 10] << 8) | bytes[offset + 11];
  422. }
  423. // public methods
  424. /// <summary>
  425. /// Compares this ObjectId to another ObjectId.
  426. /// </summary>
  427. /// <param name="other">The other ObjectId.</param>
  428. /// <returns>A 32-bit signed integer that indicates whether this ObjectId is less than, equal to, or greather than the other.</returns>
  429. public int CompareTo(ObjectId other)
  430. {
  431. int result = ((uint)_a).CompareTo((uint)other._a);
  432. if (result != 0) { return result; }
  433. result = ((uint)_b).CompareTo((uint)other._b);
  434. if (result != 0) { return result; }
  435. return ((uint)_c).CompareTo((uint)other._c);
  436. }
  437. /// <summary>
  438. /// Compares this ObjectId to another ObjectId.
  439. /// </summary>
  440. /// <param name="rhs">The other ObjectId.</param>
  441. /// <returns>True if the two ObjectIds are equal.</returns>
  442. public bool Equals(ObjectId rhs)
  443. {
  444. return
  445. _a == rhs._a &&
  446. _b == rhs._b &&
  447. _c == rhs._c;
  448. }
  449. /// <summary>
  450. /// Compares this ObjectId to another object.
  451. /// </summary>
  452. /// <param name="obj">The other object.</param>
  453. /// <returns>True if the other object is an ObjectId and equal to this one.</returns>
  454. public override bool Equals(object obj)
  455. {
  456. if (obj is ObjectId)
  457. {
  458. return Equals((ObjectId)obj);
  459. }
  460. else
  461. {
  462. return false;
  463. }
  464. }
  465. /// <summary>
  466. /// Gets the hash code.
  467. /// </summary>
  468. /// <returns>The hash code.</returns>
  469. public override int GetHashCode()
  470. {
  471. int hash = 17;
  472. hash = 37 * hash + _a.GetHashCode();
  473. hash = 37 * hash + _b.GetHashCode();
  474. hash = 37 * hash + _c.GetHashCode();
  475. return hash;
  476. }
  477. /// <summary>
  478. /// Converts the ObjectId to a byte array.
  479. /// </summary>
  480. /// <returns>A byte array.</returns>
  481. public byte[] ToByteArray()
  482. {
  483. var bytes = new byte[12];
  484. ToByteArray(bytes, 0);
  485. return bytes;
  486. }
  487. /// <summary>
  488. /// Converts the ObjectId to a byte array.
  489. /// </summary>
  490. /// <param name="destination">The destination.</param>
  491. /// <param name="offset">The offset.</param>
  492. public void ToByteArray(byte[] destination, int offset)
  493. {
  494. if (destination == null)
  495. {
  496. throw new ArgumentNullException("destination");
  497. }
  498. if (offset + 12 > destination.Length)
  499. {
  500. throw new ArgumentException("Not enough room in destination buffer.", "offset");
  501. }
  502. destination[offset + 0] = (byte)(_a >> 24);
  503. destination[offset + 1] = (byte)(_a >> 16);
  504. destination[offset + 2] = (byte)(_a >> 8);
  505. destination[offset + 3] = (byte)(_a);
  506. destination[offset + 4] = (byte)(_b >> 24);
  507. destination[offset + 5] = (byte)(_b >> 16);
  508. destination[offset + 6] = (byte)(_b >> 8);
  509. destination[offset + 7] = (byte)(_b);
  510. destination[offset + 8] = (byte)(_c >> 24);
  511. destination[offset + 9] = (byte)(_c >> 16);
  512. destination[offset + 10] = (byte)(_c >> 8);
  513. destination[offset + 11] = (byte)(_c);
  514. }
  515. /// <summary>
  516. /// Returns a string representation of the value.
  517. /// </summary>
  518. /// <returns>A string representation of the value.</returns>
  519. public override string ToString()
  520. {
  521. var c = new char[24];
  522. c[0] = BsonUtils.ToHexChar((_a >> 28) & 0x0f);
  523. c[1] = BsonUtils.ToHexChar((_a >> 24) & 0x0f);
  524. c[2] = BsonUtils.ToHexChar((_a >> 20) & 0x0f);
  525. c[3] = BsonUtils.ToHexChar((_a >> 16) & 0x0f);
  526. c[4] = BsonUtils.ToHexChar((_a >> 12) & 0x0f);
  527. c[5] = BsonUtils.ToHexChar((_a >> 8) & 0x0f);
  528. c[6] = BsonUtils.ToHexChar((_a >> 4) & 0x0f);
  529. c[7] = BsonUtils.ToHexChar(_a & 0x0f);
  530. c[8] = BsonUtils.ToHexChar((_b >> 28) & 0x0f);
  531. c[9] = BsonUtils.ToHexChar((_b >> 24) & 0x0f);
  532. c[10] = BsonUtils.ToHexChar((_b >> 20) & 0x0f);
  533. c[11] = BsonUtils.ToHexChar((_b >> 16) & 0x0f);
  534. c[12] = BsonUtils.ToHexChar((_b >> 12) & 0x0f);
  535. c[13] = BsonUtils.ToHexChar((_b >> 8) & 0x0f);
  536. c[14] = BsonUtils.ToHexChar((_b >> 4) & 0x0f);
  537. c[15] = BsonUtils.ToHexChar(_b & 0x0f);
  538. c[16] = BsonUtils.ToHexChar((_c >> 28) & 0x0f);
  539. c[17] = BsonUtils.ToHexChar((_c >> 24) & 0x0f);
  540. c[18] = BsonUtils.ToHexChar((_c >> 20) & 0x0f);
  541. c[19] = BsonUtils.ToHexChar((_c >> 16) & 0x0f);
  542. c[20] = BsonUtils.ToHexChar((_c >> 12) & 0x0f);
  543. c[21] = BsonUtils.ToHexChar((_c >> 8) & 0x0f);
  544. c[22] = BsonUtils.ToHexChar((_c >> 4) & 0x0f);
  545. c[23] = BsonUtils.ToHexChar(_c & 0x0f);
  546. return new string(c);
  547. }
  548. // explicit IConvertible implementation
  549. TypeCode IConvertible.GetTypeCode()
  550. {
  551. return TypeCode.Object;
  552. }
  553. bool IConvertible.ToBoolean(IFormatProvider provider)
  554. {
  555. throw new InvalidCastException();
  556. }
  557. byte IConvertible.ToByte(IFormatProvider provider)
  558. {
  559. throw new InvalidCastException();
  560. }
  561. char IConvertible.ToChar(IFormatProvider provider)
  562. {
  563. throw new InvalidCastException();
  564. }
  565. DateTime IConvertible.ToDateTime(IFormatProvider provider)
  566. {
  567. throw new InvalidCastException();
  568. }
  569. decimal IConvertible.ToDecimal(IFormatProvider provider)
  570. {
  571. throw new InvalidCastException();
  572. }
  573. double IConvertible.ToDouble(IFormatProvider provider)
  574. {
  575. throw new InvalidCastException();
  576. }
  577. short IConvertible.ToInt16(IFormatProvider provider)
  578. {
  579. throw new InvalidCastException();
  580. }
  581. int IConvertible.ToInt32(IFormatProvider provider)
  582. {
  583. throw new InvalidCastException();
  584. }
  585. long IConvertible.ToInt64(IFormatProvider provider)
  586. {
  587. throw new InvalidCastException();
  588. }
  589. sbyte IConvertible.ToSByte(IFormatProvider provider)
  590. {
  591. throw new InvalidCastException();
  592. }
  593. float IConvertible.ToSingle(IFormatProvider provider)
  594. {
  595. throw new InvalidCastException();
  596. }
  597. string IConvertible.ToString(IFormatProvider provider)
  598. {
  599. return ToString();
  600. }
  601. object IConvertible.ToType(Type conversionType, IFormatProvider provider)
  602. {
  603. switch (Type.GetTypeCode(conversionType))
  604. {
  605. case TypeCode.String:
  606. return ((IConvertible)this).ToString(provider);
  607. case TypeCode.Object:
  608. if (conversionType == typeof(object) || conversionType == typeof(ObjectId))
  609. {
  610. return this;
  611. }
  612. if (conversionType == typeof(BsonObjectId))
  613. {
  614. return new BsonObjectId(this);
  615. }
  616. if (conversionType == typeof(BsonString))
  617. {
  618. return new BsonString(((IConvertible)this).ToString(provider));
  619. }
  620. break;
  621. }
  622. throw new InvalidCastException();
  623. }
  624. ushort IConvertible.ToUInt16(IFormatProvider provider)
  625. {
  626. throw new InvalidCastException();
  627. }
  628. uint IConvertible.ToUInt32(IFormatProvider provider)
  629. {
  630. throw new InvalidCastException();
  631. }
  632. ulong IConvertible.ToUInt64(IFormatProvider provider)
  633. {
  634. throw new InvalidCastException();
  635. }
  636. }
  637. }