UInt128.cs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. /* Copyright 2016 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.Globalization;
  17. using System.Text;
  18. using System.Text.RegularExpressions;
  19. namespace MongoDB.Bson
  20. {
  21. // this is a minimal implementation of UInt128 containing only what we need for Decimal128
  22. internal struct UInt128 : IComparable<UInt128>, IEquatable<UInt128>
  23. {
  24. #region static
  25. // public static properties
  26. public static UInt128 Zero
  27. {
  28. get { return new UInt128(0, 0); }
  29. }
  30. // public static methods
  31. public static UInt128 Add(UInt128 x, UInt128 y)
  32. {
  33. var high = x.High + y.High;
  34. var low = x.Low + y.Low;
  35. if (low < x.Low)
  36. {
  37. high += 1;
  38. }
  39. return new UInt128(high, low);
  40. }
  41. public static int Compare(UInt128 x, UInt128 y)
  42. {
  43. var result = x.High.CompareTo(y.High);
  44. if (result == 0)
  45. {
  46. result = x.Low.CompareTo(y.Low);
  47. }
  48. return result;
  49. }
  50. public static UInt128 Divide(UInt128 x, uint divisor, out uint remainder)
  51. {
  52. if (x.High == 0 && x.Low == 0)
  53. {
  54. remainder = 0;
  55. return UInt128.Zero;
  56. }
  57. var a = x.High >> 32;
  58. var b = x.High & 0xffffffff;
  59. var c = x.Low >> 32;
  60. var d = x.Low & 0xffffffff;
  61. var temp = a;
  62. a = (temp / divisor) & 0xffffffff;
  63. temp = ((temp % divisor) << 32) + b;
  64. b = (temp / divisor) & 0xffffffff;
  65. temp = ((temp % divisor) << 32) + c;
  66. c = (temp / divisor) & 0xffffffff;
  67. temp = ((temp % divisor) << 32) + d;
  68. d = (temp / divisor) & 0xffffffff;
  69. var high = (a << 32) + b;
  70. var low = (c << 32) + d;
  71. remainder = (uint)(temp % divisor);
  72. return new UInt128(high, low);
  73. }
  74. public static bool Equals(UInt128 x, UInt128 y)
  75. {
  76. return x.High == y.High && x.Low == y.Low;
  77. }
  78. public static UInt128 Multiply(UInt128 x, uint y)
  79. {
  80. var a = x.High >> 32;
  81. var b = x.High & 0xffffffff;
  82. var c = x.Low >> 32;
  83. var d = x.Low & 0xffffffff;
  84. d = d * y;
  85. c = c * y + (d >> 32);
  86. b = b * y + (c >> 32);
  87. a = a * y + (b >> 32);
  88. var low = (c << 32) + (d & 0xffffffff);
  89. var high = (a << 32) + (b & 0xffffffff);
  90. return new UInt128(high, low);
  91. }
  92. public static UInt128 Multiply(ulong x, ulong y)
  93. {
  94. // x = a * 2^32 + b
  95. // y = c * 2^32 + d
  96. // xy = ac * 2^64 + (ad + bc) * 2^32 + bd
  97. var a = x >> 32;
  98. var b = x & 0xffffffff;
  99. var c = y >> 32;
  100. var d = y & 0xffffffff;
  101. var ac = a * c;
  102. var ad = a * d;
  103. var bc = b * c;
  104. var bd = b * d;
  105. var mid = (ad & 0xffffffff) + (bc & 0xffffffff) + (bd >> 32);
  106. var high = ac + (ad >> 32) + (bc >> 32) + (mid >> 32);
  107. var low = (mid << 32) + (bd & 0xffffffff);
  108. return new UInt128(high, low);
  109. }
  110. public static UInt128 Parse(string s)
  111. {
  112. UInt128 value;
  113. if (!UInt128.TryParse(s, out value))
  114. {
  115. throw new FormatException($"Error parsing UInt128 string: \"{s}\".");
  116. }
  117. return value;
  118. }
  119. public static bool TryParse(string s, out UInt128 value)
  120. {
  121. if (s == null || s.Length == 0)
  122. {
  123. value = default(UInt128);
  124. return false;
  125. }
  126. // remove leading zeroes (and return true if value is zero)
  127. if (s[0] == '0')
  128. {
  129. if (s.Length == 1)
  130. {
  131. value = UInt128.Zero;
  132. return true;
  133. }
  134. else
  135. {
  136. s = Regex.Replace(s, "^0+", "");
  137. if (s.Length == 0)
  138. {
  139. value = UInt128.Zero;
  140. return true;
  141. }
  142. }
  143. }
  144. // parse 9 or fewer decimal digits at a time
  145. value = UInt128.Zero;
  146. while (s.Length > 0)
  147. {
  148. int fragmentSize = s.Length % 9;
  149. if (fragmentSize == 0)
  150. {
  151. fragmentSize = 9;
  152. }
  153. var fragmentString = s.Substring(0, fragmentSize);
  154. uint fragmentValue;
  155. if (!uint.TryParse(fragmentString, out fragmentValue))
  156. {
  157. value = default(UInt128);
  158. return false;
  159. }
  160. var combinedValue = UInt128.Multiply(value, (uint)1000000000);
  161. combinedValue = UInt128.Add(combinedValue, new UInt128(0, fragmentValue));
  162. if (UInt128.Compare(combinedValue, value) < 0)
  163. {
  164. // overflow means s represents a value larger than UInt128.MaxValue
  165. value = default(UInt128);
  166. return false;
  167. }
  168. value = combinedValue;
  169. s = s.Substring(fragmentSize);
  170. }
  171. return true;
  172. }
  173. #endregion
  174. // private fields
  175. private readonly ulong _high;
  176. private readonly ulong _low;
  177. // constructors
  178. public UInt128(ulong low)
  179. {
  180. _high = 0;
  181. _low = low;
  182. }
  183. public UInt128(ulong high, ulong low)
  184. {
  185. _high = high;
  186. _low = low;
  187. }
  188. // public properties
  189. public ulong High
  190. {
  191. get { return _high; }
  192. }
  193. public ulong Low
  194. {
  195. get { return _low; }
  196. }
  197. // public methods
  198. public int CompareTo(UInt128 other)
  199. {
  200. return UInt128.Compare(this, other);
  201. }
  202. public override bool Equals(object obj)
  203. {
  204. if (obj == null || obj.GetType() != typeof(UInt128))
  205. {
  206. return false;
  207. }
  208. return Equals((UInt128)obj);
  209. }
  210. public bool Equals(UInt128 other)
  211. {
  212. return _high == other._high && _low == other._low;
  213. }
  214. public override int GetHashCode()
  215. {
  216. return 37 * _high.GetHashCode() + _low.GetHashCode();
  217. }
  218. public override string ToString()
  219. {
  220. StringBuilder builder = null; // don't create the builder until we actually need it
  221. var value = this;
  222. while (true)
  223. {
  224. // convert 9 decimal digits at a time to a string
  225. uint remainder;
  226. value = UInt128.Divide(value, (uint)1000000000, out remainder);
  227. var fragmentString = remainder.ToString(NumberFormatInfo.InvariantInfo);
  228. if (UInt128.Equals(value, UInt128.Zero))
  229. {
  230. if (builder == null)
  231. {
  232. return fragmentString; // values with 9 decimal digits or less don't need the builder
  233. }
  234. else
  235. {
  236. builder.Insert(0, fragmentString);
  237. return builder.ToString();
  238. }
  239. }
  240. if (builder == null)
  241. {
  242. builder = new StringBuilder(38);
  243. }
  244. builder.Insert(0, fragmentString);
  245. builder.Insert(0, "0", 9 - fragmentString.Length);
  246. }
  247. }
  248. }
  249. }