BsonBinaryData.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  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.Linq;
  17. namespace MongoDB.Bson
  18. {
  19. /// <summary>
  20. /// Represents BSON binary data.
  21. /// </summary>
  22. [Serializable]
  23. public class BsonBinaryData : BsonValue, IComparable<BsonBinaryData>, IEquatable<BsonBinaryData>
  24. {
  25. // private fields
  26. private readonly byte[] _bytes;
  27. private readonly BsonBinarySubType _subType;
  28. private readonly GuidRepresentation _guidRepresentation; // only relevant if subType is UuidStandard or UuidLegacy
  29. // constructors
  30. /// <summary>
  31. /// Initializes a new instance of the BsonBinaryData class.
  32. /// </summary>
  33. /// <param name="bytes">The binary data.</param>
  34. public BsonBinaryData(byte[] bytes)
  35. : this(bytes, BsonBinarySubType.Binary)
  36. {
  37. }
  38. /// <summary>
  39. /// Initializes a new instance of the BsonBinaryData class.
  40. /// </summary>
  41. /// <param name="bytes">The binary data.</param>
  42. /// <param name="subType">The binary data subtype.</param>
  43. public BsonBinaryData(byte[] bytes, BsonBinarySubType subType)
  44. {
  45. if (bytes == null)
  46. {
  47. throw new ArgumentNullException(nameof(bytes));
  48. }
  49. if (subType == BsonBinarySubType.UuidStandard || subType == BsonBinarySubType.UuidLegacy)
  50. {
  51. if (bytes.Length != 16)
  52. {
  53. var message = string.Format(
  54. "Length must be 16, not {0}, when subType is {1}.",
  55. bytes.Length, subType);
  56. throw new ArgumentException(message, nameof(bytes));
  57. }
  58. }
  59. _bytes = bytes;
  60. _subType = subType;
  61. }
  62. /// <summary>
  63. /// Initializes a new instance of the BsonBinaryData class.
  64. /// </summary>
  65. /// <param name="bytes">The binary data.</param>
  66. /// <param name="subType">The binary data subtype.</param>
  67. /// <param name="guidRepresentation">The representation for Guids.</param>
  68. [Obsolete("This constructor will be removed in a later release.")]
  69. public BsonBinaryData(byte[] bytes, BsonBinarySubType subType, GuidRepresentation guidRepresentation)
  70. : this(bytes, subType)
  71. {
  72. if (BsonDefaults.GuidRepresentationMode != GuidRepresentationMode.V2)
  73. {
  74. throw new InvalidOperationException("BsonBinaryData constructor that takes a GuidRepresentation can only be used when GuidRepresentationMode is V2.");
  75. }
  76. if (subType == BsonBinarySubType.UuidStandard || subType == BsonBinarySubType.UuidLegacy)
  77. {
  78. BsonBinarySubType expectedSubType;
  79. switch (guidRepresentation)
  80. {
  81. case GuidRepresentation.CSharpLegacy:
  82. case GuidRepresentation.JavaLegacy:
  83. case GuidRepresentation.PythonLegacy:
  84. case GuidRepresentation.Unspecified:
  85. expectedSubType = BsonBinarySubType.UuidLegacy;
  86. break;
  87. case GuidRepresentation.Standard:
  88. expectedSubType = BsonBinarySubType.UuidStandard;
  89. break;
  90. default:
  91. throw new ArgumentException($"Invalid guidRepresentation: {guidRepresentation}.", nameof(guidRepresentation));
  92. }
  93. if (subType != expectedSubType)
  94. {
  95. throw new ArgumentException($"GuidRepresentation {guidRepresentation} is only valid with subType {expectedSubType}, not with subType {subType}.", nameof(guidRepresentation));
  96. }
  97. }
  98. else
  99. {
  100. if (guidRepresentation != GuidRepresentation.Unspecified)
  101. {
  102. var message = string.Format(
  103. "GuidRepresentation must be Unspecified, not {0}, when subType is not UuidStandard or UuidLegacy.",
  104. guidRepresentation);
  105. throw new ArgumentException(message, nameof(guidRepresentation));
  106. }
  107. }
  108. _guidRepresentation = guidRepresentation;
  109. }
  110. /// <summary>
  111. /// Initializes a new instance of the BsonBinaryData class.
  112. /// </summary>
  113. /// <param name="guid">A Guid.</param>
  114. #pragma warning disable 618
  115. [Obsolete("Use the constructor that also takes a GuidRepresentation instead.")]
  116. public BsonBinaryData(Guid guid)
  117. : this(
  118. guid,
  119. BsonDefaults.GuidRepresentationMode == GuidRepresentationMode.V2
  120. ? BsonDefaults.GuidRepresentation
  121. : throw new InvalidOperationException("This constructor can only be used when BsonDefaults.GuidRepresentationMode is V2."))
  122. {
  123. }
  124. #pragma warning restore 618
  125. /// <summary>
  126. /// Initializes a new instance of the BsonBinaryData class.
  127. /// </summary>
  128. /// <param name="guid">A Guid.</param>
  129. /// <param name="guidRepresentation">The representation for Guids.</param>
  130. public BsonBinaryData(Guid guid, GuidRepresentation guidRepresentation)
  131. {
  132. _bytes = GuidConverter.ToBytes(guid, guidRepresentation);
  133. _subType = GuidConverter.GetSubType(guidRepresentation);
  134. #pragma warning disable 618
  135. if (BsonDefaults.GuidRepresentationMode == GuidRepresentationMode.V2)
  136. {
  137. _guidRepresentation = guidRepresentation;
  138. }
  139. #pragma warning restore 618
  140. }
  141. // public properties
  142. /// <summary>
  143. /// Gets the BsonType of this BsonValue.
  144. /// </summary>
  145. public override BsonType BsonType
  146. {
  147. get { return BsonType.Binary; }
  148. }
  149. /// <summary>
  150. /// Gets the binary data.
  151. /// </summary>
  152. public byte[] Bytes
  153. {
  154. get { return _bytes; }
  155. }
  156. /// <summary>
  157. /// Gets the representation to use when representing the Guid as BSON binary data.
  158. /// </summary>
  159. [Obsolete("This property will be removed in a later release.")]
  160. public GuidRepresentation GuidRepresentation
  161. {
  162. get
  163. {
  164. if (BsonDefaults.GuidRepresentationMode != GuidRepresentationMode.V2)
  165. {
  166. throw new InvalidOperationException("GuidRepresentation property can only be used when GuidRepresentationMode is V2.");
  167. }
  168. return _guidRepresentation;
  169. }
  170. }
  171. /// <summary>
  172. /// Gets the BsonBinaryData as a Guid if the subtype is UuidStandard or UuidLegacy, otherwise null.
  173. /// </summary>
  174. #pragma warning disable 618 // about obsolete BsonBinarySubType.OldBinary
  175. [Obsolete("Use Value instead.")]
  176. public override object RawValue
  177. {
  178. get
  179. {
  180. if (_subType == BsonBinarySubType.Binary || _subType == BsonBinarySubType.OldBinary)
  181. {
  182. return _bytes;
  183. }
  184. else if (_subType == BsonBinarySubType.UuidStandard || _subType == BsonBinarySubType.UuidLegacy)
  185. {
  186. return ToGuid();
  187. }
  188. else
  189. {
  190. return null;
  191. }
  192. }
  193. }
  194. #pragma warning restore 618
  195. /// <summary>
  196. /// Gets the binary data subtype.
  197. /// </summary>
  198. public BsonBinarySubType SubType
  199. {
  200. get { return _subType; }
  201. }
  202. // public operators
  203. /// <summary>
  204. /// Converts a byte array to a BsonBinaryData.
  205. /// </summary>
  206. /// <param name="bytes">A byte array.</param>
  207. /// <returns>A BsonBinaryData.</returns>
  208. public static implicit operator BsonBinaryData(byte[] bytes)
  209. {
  210. return new BsonBinaryData(bytes);
  211. }
  212. /// <summary>
  213. /// Converts a Guid to a BsonBinaryData.
  214. /// </summary>
  215. /// <param name="value">A Guid.</param>
  216. /// <returns>A BsonBinaryData.</returns>
  217. [Obsolete("Use the BsonBinaryData constructor instead and specify a Guid representation.")]
  218. public static implicit operator BsonBinaryData(Guid value)
  219. {
  220. if (BsonDefaults.GuidRepresentationMode != GuidRepresentationMode.V2)
  221. {
  222. throw new InvalidOperationException("Implicit conversion from Guid to BsonBinaryData is only valid when BsonDefaults.GuidRepresentationMode is V2.");
  223. }
  224. return new BsonBinaryData(value);
  225. }
  226. /// <summary>
  227. /// Compares two BsonBinaryData values.
  228. /// </summary>
  229. /// <param name="lhs">The first BsonBinaryData.</param>
  230. /// <param name="rhs">The other BsonBinaryData.</param>
  231. /// <returns>True if the two BsonBinaryData values are not equal according to ==.</returns>
  232. public static bool operator !=(BsonBinaryData lhs, BsonBinaryData rhs)
  233. {
  234. return !(lhs == rhs);
  235. }
  236. /// <summary>
  237. /// Compares two BsonBinaryData values.
  238. /// </summary>
  239. /// <param name="lhs">The first BsonBinaryData.</param>
  240. /// <param name="rhs">The other BsonBinaryData.</param>
  241. /// <returns>True if the two BsonBinaryData values are equal according to ==.</returns>
  242. public static bool operator ==(BsonBinaryData lhs, BsonBinaryData rhs)
  243. {
  244. if (object.ReferenceEquals(lhs, null)) { return object.ReferenceEquals(rhs, null); }
  245. return lhs.Equals(rhs);
  246. }
  247. // public static methods
  248. /// <summary>
  249. /// Creates a new BsonBinaryData.
  250. /// </summary>
  251. /// <param name="value">An object to be mapped to a BsonBinaryData.</param>
  252. /// <returns>A BsonBinaryData or null.</returns>
  253. public new static BsonBinaryData Create(object value)
  254. {
  255. if (value == null)
  256. {
  257. throw new ArgumentNullException("value");
  258. }
  259. return (BsonBinaryData)BsonTypeMapper.MapToBsonValue(value, BsonType.Binary);
  260. }
  261. // public methods
  262. /// <summary>
  263. /// Compares this BsonBinaryData to another BsonBinaryData.
  264. /// </summary>
  265. /// <param name="other">The other BsonBinaryData.</param>
  266. /// <returns>A 32-bit signed integer that indicates whether this BsonBinaryData is less than, equal to, or greather than the other.</returns>
  267. public int CompareTo(BsonBinaryData other)
  268. {
  269. if (other == null) { return 1; }
  270. int r = _subType.CompareTo(other._subType);
  271. if (r != 0) { return r; }
  272. for (int i = 0; i < _bytes.Length && i < other._bytes.Length; i++)
  273. {
  274. r = _bytes[i].CompareTo(other._bytes[i]);
  275. if (r != 0) { return r; }
  276. }
  277. return _bytes.Length.CompareTo(other._bytes.Length);
  278. }
  279. /// <summary>
  280. /// Compares the BsonBinaryData to another BsonValue.
  281. /// </summary>
  282. /// <param name="other">The other BsonValue.</param>
  283. /// <returns>A 32-bit signed integer that indicates whether this BsonBinaryData is less than, equal to, or greather than the other BsonValue.</returns>
  284. public override int CompareTo(BsonValue other)
  285. {
  286. if (other == null) { return 1; }
  287. var otherBinaryData = other as BsonBinaryData;
  288. if (otherBinaryData != null)
  289. {
  290. return CompareTo(otherBinaryData);
  291. }
  292. return CompareTypeTo(other);
  293. }
  294. /// <summary>
  295. /// Compares this BsonBinaryData to another BsonBinaryData.
  296. /// </summary>
  297. /// <param name="rhs">The other BsonBinaryData.</param>
  298. /// <returns>True if the two BsonBinaryData values are equal.</returns>
  299. public bool Equals(BsonBinaryData rhs)
  300. {
  301. if (object.ReferenceEquals(rhs, null) || GetType() != rhs.GetType()) { return false; }
  302. // note: guidRepresentation is not considered when testing for Equality
  303. return object.ReferenceEquals(this, rhs) || _subType == rhs._subType && _bytes.SequenceEqual(rhs._bytes);
  304. }
  305. /// <summary>
  306. /// Compares this BsonBinaryData to another object.
  307. /// </summary>
  308. /// <param name="obj">The other object.</param>
  309. /// <returns>True if the other object is a BsonBinaryData and equal to this one.</returns>
  310. public override bool Equals(object obj)
  311. {
  312. return Equals(obj as BsonBinaryData); // works even if obj is null or of a different type
  313. }
  314. /// <summary>
  315. /// Gets the hash code.
  316. /// </summary>
  317. /// <returns>The hash code.</returns>
  318. public override int GetHashCode()
  319. {
  320. // see Effective Java by Joshua Bloch
  321. // note: guidRepresentation is not considered when computing the hash code
  322. int hash = 17;
  323. hash = 37 * hash + BsonType.GetHashCode();
  324. foreach (byte b in _bytes)
  325. {
  326. hash = 37 * hash + b;
  327. }
  328. hash = 37 * hash + _subType.GetHashCode();
  329. return hash;
  330. }
  331. /// <summary>
  332. /// Converts this BsonBinaryData to a Guid.
  333. /// </summary>
  334. /// <returns>A Guid.</returns>
  335. public Guid ToGuid()
  336. {
  337. #pragma warning disable 618
  338. if (BsonDefaults.GuidRepresentationMode == GuidRepresentationMode.V2)
  339. {
  340. return ToGuid(_guidRepresentation);
  341. }
  342. else
  343. {
  344. if (_subType != BsonBinarySubType.UuidStandard)
  345. {
  346. throw new InvalidOperationException("ToGuid without a Guid representation can only be called when sub type is UuidStandard.");
  347. }
  348. return GuidConverter.FromBytes(_bytes, GuidRepresentation.Standard);
  349. }
  350. #pragma warning restore 618
  351. }
  352. /// <summary>
  353. /// Converts this BsonBinaryData to a Guid.
  354. /// </summary>
  355. /// <param name="guidRepresentation">The representation for Guids.</param>
  356. /// <returns>A Guid.</returns>
  357. public Guid ToGuid(GuidRepresentation guidRepresentation)
  358. {
  359. if (_subType != BsonBinarySubType.UuidStandard && _subType != BsonBinarySubType.UuidLegacy)
  360. {
  361. var message = string.Format("SubType must be UuidStandard or UuidLegacy, not {0}.", _subType);
  362. throw new InvalidOperationException(message);
  363. }
  364. if (guidRepresentation == GuidRepresentation.Unspecified)
  365. {
  366. throw new ArgumentException("GuidRepresentation cannot be Unspecified.");
  367. }
  368. #pragma warning disable 618
  369. if (BsonDefaults.GuidRepresentationMode == GuidRepresentationMode.V3)
  370. {
  371. var expectedSubType = GuidConverter.GetSubType(guidRepresentation);
  372. if (_subType != expectedSubType)
  373. {
  374. throw new InvalidOperationException($"ToGuid with Guid representation {guidRepresentation} can only be called when sub type is {expectedSubType}.");
  375. }
  376. }
  377. #pragma warning restore 618
  378. return GuidConverter.FromBytes(_bytes, guidRepresentation);
  379. }
  380. /// <summary>
  381. /// Returns a string representation of the binary data.
  382. /// </summary>
  383. /// <returns>A string representation of the binary data.</returns>
  384. public override string ToString()
  385. {
  386. return string.Format("{0}:0x{1}", _subType, BsonUtils.ToHexString(_bytes));
  387. }
  388. }
  389. }