BsonUtils.cs 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  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.Reflection;
  17. using System.Text;
  18. using System.Text.RegularExpressions;
  19. namespace MongoDB.Bson
  20. {
  21. /// <summary>
  22. /// A static class containing BSON utility methods.
  23. /// </summary>
  24. public static class BsonUtils
  25. {
  26. // public static methods
  27. /// <summary>
  28. /// Gets a friendly class name suitable for use in error messages.
  29. /// </summary>
  30. /// <param name="type">The type.</param>
  31. /// <returns>A friendly class name.</returns>
  32. public static string GetFriendlyTypeName(Type type)
  33. {
  34. var typeInfo = type.GetTypeInfo();
  35. if (!typeInfo.IsGenericType)
  36. {
  37. return type.Name;
  38. }
  39. var sb = new StringBuilder();
  40. sb.AppendFormat("{0}<", Regex.Replace(type.Name, @"\`\d+$", ""));
  41. foreach (var typeParameter in typeInfo.GetGenericArguments())
  42. {
  43. sb.AppendFormat("{0}, ", GetFriendlyTypeName(typeParameter));
  44. }
  45. sb.Remove(sb.Length - 2, 2);
  46. sb.Append(">");
  47. return sb.ToString();
  48. }
  49. /// <summary>
  50. /// Parses a hex string into its equivalent byte array.
  51. /// </summary>
  52. /// <param name="s">The hex string to parse.</param>
  53. /// <returns>The byte equivalent of the hex string.</returns>
  54. public static byte[] ParseHexString(string s)
  55. {
  56. if (s == null)
  57. {
  58. throw new ArgumentNullException(nameof(s));
  59. }
  60. byte[] bytes;
  61. if (!TryParseHexString(s, out bytes))
  62. {
  63. throw new FormatException("String should contain only hexadecimal digits.");
  64. }
  65. return bytes;
  66. }
  67. /// <summary>
  68. /// Converts from number of milliseconds since Unix epoch to DateTime.
  69. /// </summary>
  70. /// <param name="millisecondsSinceEpoch">The number of milliseconds since Unix epoch.</param>
  71. /// <returns>A DateTime.</returns>
  72. public static DateTime ToDateTimeFromMillisecondsSinceEpoch(long millisecondsSinceEpoch)
  73. {
  74. if (millisecondsSinceEpoch < BsonConstants.DateTimeMinValueMillisecondsSinceEpoch ||
  75. millisecondsSinceEpoch > BsonConstants.DateTimeMaxValueMillisecondsSinceEpoch)
  76. {
  77. var message = string.Format(
  78. "The value {0} for the BsonDateTime MillisecondsSinceEpoch is outside the" +
  79. "range that can be converted to a .NET DateTime.",
  80. millisecondsSinceEpoch);
  81. throw new ArgumentOutOfRangeException("millisecondsSinceEpoch", message);
  82. }
  83. // MaxValue has to be handled specially to avoid rounding errors
  84. if (millisecondsSinceEpoch == BsonConstants.DateTimeMaxValueMillisecondsSinceEpoch)
  85. {
  86. return DateTime.SpecifyKind(DateTime.MaxValue, DateTimeKind.Utc);
  87. }
  88. else
  89. {
  90. return BsonConstants.UnixEpoch.AddTicks(millisecondsSinceEpoch * 10000);
  91. }
  92. }
  93. /// <summary>
  94. /// Converts a value to a hex character.
  95. /// </summary>
  96. /// <param name="value">The value (assumed to be between 0 and 15).</param>
  97. /// <returns>The hex character.</returns>
  98. public static char ToHexChar(int value)
  99. {
  100. return (char)(value + (value < 10 ? '0' : 'a' - 10));
  101. }
  102. /// <summary>
  103. /// Converts a byte array to a hex string.
  104. /// </summary>
  105. /// <param name="bytes">The byte array.</param>
  106. /// <returns>A hex string.</returns>
  107. public static string ToHexString(byte[] bytes)
  108. {
  109. if (bytes == null)
  110. {
  111. throw new ArgumentNullException("bytes");
  112. }
  113. var length = bytes.Length;
  114. var c = new char[length * 2];
  115. for (int i = 0, j = 0; i < length; i++)
  116. {
  117. var b = bytes[i];
  118. c[j++] = ToHexChar(b >> 4);
  119. c[j++] = ToHexChar(b & 0x0f);
  120. }
  121. return new string(c);
  122. }
  123. /// <summary>
  124. /// Converts a DateTime to local time (with special handling for MinValue and MaxValue).
  125. /// </summary>
  126. /// <param name="dateTime">A DateTime.</param>
  127. /// <returns>The DateTime in local time.</returns>
  128. public static DateTime ToLocalTime(DateTime dateTime)
  129. {
  130. if (dateTime == DateTime.MinValue)
  131. {
  132. return DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Local);
  133. }
  134. else if (dateTime == DateTime.MaxValue)
  135. {
  136. return DateTime.SpecifyKind(DateTime.MaxValue, DateTimeKind.Local);
  137. }
  138. else
  139. {
  140. return dateTime.ToLocalTime();
  141. }
  142. }
  143. /// <summary>
  144. /// Converts a DateTime to number of milliseconds since Unix epoch.
  145. /// </summary>
  146. /// <param name="dateTime">A DateTime.</param>
  147. /// <returns>Number of milliseconds since Unix epoch.</returns>
  148. public static long ToMillisecondsSinceEpoch(DateTime dateTime)
  149. {
  150. var utcDateTime = ToUniversalTime(dateTime);
  151. return (utcDateTime - BsonConstants.UnixEpoch).Ticks / 10000;
  152. }
  153. /// <summary>
  154. /// Converts a DateTime to number of seconds since Unix epoch.
  155. /// </summary>
  156. /// <param name="dateTime">A DateTime.</param>
  157. /// <returns>Number of seconds since Unix epoch.</returns>
  158. public static long ToSecondsSinceEpoch(DateTime dateTime)
  159. {
  160. var utcDateTime = ToUniversalTime(dateTime);
  161. return (utcDateTime - BsonConstants.UnixEpoch).Ticks / TimeSpan.TicksPerSecond;
  162. }
  163. /// <summary>
  164. /// Converts a DateTime to UTC (with special handling for MinValue and MaxValue).
  165. /// </summary>
  166. /// <param name="dateTime">A DateTime.</param>
  167. /// <returns>The DateTime in UTC.</returns>
  168. public static DateTime ToUniversalTime(DateTime dateTime)
  169. {
  170. if (dateTime == DateTime.MinValue)
  171. {
  172. return DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc);
  173. }
  174. else if (dateTime == DateTime.MaxValue)
  175. {
  176. return DateTime.SpecifyKind(DateTime.MaxValue, DateTimeKind.Utc);
  177. }
  178. else
  179. {
  180. return dateTime.ToUniversalTime();
  181. }
  182. }
  183. /// <summary>
  184. /// Tries to parse a hex string to a byte array.
  185. /// </summary>
  186. /// <param name="s">The hex string.</param>
  187. /// <param name="bytes">A byte array.</param>
  188. /// <returns>True if the hex string was successfully parsed.</returns>
  189. public static bool TryParseHexString(string s, out byte[] bytes)
  190. {
  191. bytes = null;
  192. if (s == null)
  193. {
  194. return false;
  195. }
  196. var buffer = new byte[(s.Length + 1) / 2];
  197. var i = 0;
  198. var j = 0;
  199. if ((s.Length % 2) == 1)
  200. {
  201. // if s has an odd length assume an implied leading "0"
  202. int y;
  203. if (!TryParseHexChar(s[i++], out y))
  204. {
  205. return false;
  206. }
  207. buffer[j++] = (byte)y;
  208. }
  209. while (i < s.Length)
  210. {
  211. int x, y;
  212. if (!TryParseHexChar(s[i++], out x))
  213. {
  214. return false;
  215. }
  216. if (!TryParseHexChar(s[i++], out y))
  217. {
  218. return false;
  219. }
  220. buffer[j++] = (byte)((x << 4) | y);
  221. }
  222. bytes = buffer;
  223. return true;
  224. }
  225. // private static methods
  226. private static bool TryParseHexChar(char c, out int value)
  227. {
  228. if (c >= '0' && c <= '9')
  229. {
  230. value = c - '0';
  231. return true;
  232. }
  233. if (c >= 'a' && c <= 'f')
  234. {
  235. value = 10 + (c - 'a');
  236. return true;
  237. }
  238. if (c >= 'A' && c <= 'F')
  239. {
  240. value = 10 + (c - 'A');
  241. return true;
  242. }
  243. value = 0;
  244. return false;
  245. }
  246. }
  247. }