diff --git a/src/MongoDB.Bson/ObjectModel/ObjectId.cs b/src/MongoDB.Bson/ObjectModel/ObjectId.cs index d249e6cf1b2..9e3430e78e9 100644 --- a/src/MongoDB.Bson/ObjectModel/ObjectId.cs +++ b/src/MongoDB.Bson/ObjectModel/ObjectId.cs @@ -41,6 +41,17 @@ public struct ObjectId : IComparable<ObjectId>, IEquatable<ObjectId>, IConvertib private readonly int _c; // constructors + /// <summary> + /// Initializes a new instance of the ObjectId class. + /// </summary> + /// <param name="a">First 32 bits</param> + /// <param name="b">Second 32 bits</param> + /// <param name="c">Third 32 bits</param> + private ObjectId(int a, int b, int c) + { + _a = a; _b = b; _c = c; + } + /// <summary> /// Initializes a new instance of the ObjectId class. /// </summary> @@ -49,11 +60,11 @@ public ObjectId(byte[] bytes) { if (bytes == null) { - throw new ArgumentNullException("bytes"); + throw new ArgumentNullException(nameof(bytes)); } if (bytes.Length != 12) { - throw new ArgumentException("Byte array must be 12 bytes long", "bytes"); + throw new ArgumentException("Byte array must be 12 bytes long", nameof(bytes)); } FromByteArray(bytes, 0, out _a, out _b, out _c); @@ -92,16 +103,16 @@ public ObjectId(int timestamp, int machine, short pid, int increment) { if ((machine & 0xff000000) != 0) { - throw new ArgumentOutOfRangeException("machine", "The machine value must be between 0 and 16777215 (it must fit in 3 bytes)."); + throw new ArgumentOutOfRangeException(nameof(machine), "The machine value must be between 0 and 16777215 (it must fit in 3 bytes)."); } if ((increment & 0xff000000) != 0) { - throw new ArgumentOutOfRangeException("increment", "The increment value must be between 0 and 16777215 (it must fit in 3 bytes)."); + throw new ArgumentOutOfRangeException(nameof(increment), "The increment value must be between 0 and 16777215 (it must fit in 3 bytes)."); } _a = timestamp; - _b = (machine << 8) | (((int)pid >> 8) & 0xff); - _c = ((int)pid << 24) | increment; + _b = (machine << 8) | ((pid >> 8) & 0xff); + _c = (pid << 24) | increment; } /// <summary> @@ -112,11 +123,13 @@ public ObjectId(string value) { if (value == null) { - throw new ArgumentNullException("value"); + throw new ArgumentNullException(nameof(value)); } - var bytes = BsonUtils.ParseHexString(value); - FromByteArray(bytes, 0, out _a, out _b, out _c); + if (!TryParse(value, out _a, out _b, out _c)) + { + throw new ArgumentException("String value is not valid.", nameof(value)); + } } // public static properties @@ -279,11 +292,11 @@ public static byte[] Pack(int timestamp, int machine, short pid, int increment) { if ((machine & 0xff000000) != 0) { - throw new ArgumentOutOfRangeException("machine", "The machine value must be between 0 and 16777215 (it must fit in 3 bytes)."); + throw new ArgumentOutOfRangeException(nameof(machine), "The machine value must be between 0 and 16777215 (it must fit in 3 bytes)."); } if ((increment & 0xff000000) != 0) { - throw new ArgumentOutOfRangeException("increment", "The increment value must be between 0 and 16777215 (it must fit in 3 bytes)."); + throw new ArgumentOutOfRangeException(nameof(increment), "The increment value must be between 0 and 16777215 (it must fit in 3 bytes)."); } byte[] bytes = new byte[12]; @@ -311,7 +324,7 @@ public static ObjectId Parse(string s) { if (s == null) { - throw new ArgumentNullException("s"); + throw new ArgumentNullException(nameof(s)); } ObjectId objectId; @@ -335,14 +348,11 @@ public static ObjectId Parse(string s) public static bool TryParse(string s, out ObjectId objectId) { // don't throw ArgumentNullException if s is null - if (s != null && s.Length == 24) + int a, b, c; + if (TryParse(s, out a, out b, out c)) { - byte[] bytes; - if (BsonUtils.TryParseHexString(s, out bytes)) - { - objectId = new ObjectId(bytes); - return true; - } + objectId = new ObjectId(a, b, c); + return true; } objectId = default(ObjectId); @@ -361,11 +371,11 @@ public static void Unpack(byte[] bytes, out int timestamp, out int machine, out { if (bytes == null) { - throw new ArgumentNullException("bytes"); + throw new ArgumentNullException(nameof(bytes)); } if (bytes.Length != 12) { - throw new ArgumentOutOfRangeException("bytes", "Byte array must be 12 bytes long."); + throw new ArgumentOutOfRangeException(nameof(bytes), "Byte array must be 12 bytes long."); } timestamp = (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3]; @@ -429,6 +439,38 @@ private static int GetTimestampFromDateTime(DateTime timestamp) return (int)secondsSinceEpoch; } + private static bool TryParse(string value, out int a, out int b, out int c) + { + a = 0; b = 0; c = 0; + + if (value == null || value.Length != 24) + { + return false; + } + + var err = 0; + + for (int i = 0; i < 8 && err == 0; i++) + { + var ch = value[i] | 0x20; + var r = (ch >= '0' && ch <= '9') ? (ch - '0') : (ch >= 'a' && ch <= 'f') ? (ch - 'a' + 10) : 16; + a = (a << 4) | (r & 0x0F); + err |= (r & 0xF0); + + ch = value[i + 8] | 0x20; + r = (ch >= '0' && ch <= '9') ? (ch - '0') : (ch >= 'a' && ch <= 'f') ? (ch - 'a' + 10) : 16; + b = (b << 4) | (r & 0x0F); + err |= (r & 0xF0); + + ch = value[i + 16] | 0x20; + r = (ch >= '0' && ch <= '9') ? (ch - '0') : (ch >= 'a' && ch <= 'f') ? (ch - 'a' + 10) : 16; + c = (c << 4) | (r & 0x0F); + err |= (r & 0xF0); + } + + return err == 0; + } + private static void FromByteArray(byte[] bytes, int offset, out int a, out int b, out int c) { a = (bytes[offset] << 24) | (bytes[offset + 1] << 16) | (bytes[offset + 2] << 8) | bytes[offset + 3]; @@ -471,14 +513,7 @@ public bool Equals(ObjectId rhs) /// <returns>True if the other object is an ObjectId and equal to this one.</returns> public override bool Equals(object obj) { - if (obj is ObjectId) - { - return Equals((ObjectId)obj); - } - else - { - return false; - } + return obj is ObjectId && Equals((ObjectId)obj); } /// <summary> @@ -514,11 +549,11 @@ public void ToByteArray(byte[] destination, int offset) { if (destination == null) { - throw new ArgumentNullException("destination"); + throw new ArgumentNullException(nameof(destination)); } if (offset + 12 > destination.Length) { - throw new ArgumentException("Not enough room in destination buffer.", "offset"); + throw new ArgumentException("Not enough room in destination buffer.", nameof(offset)); } destination[offset + 0] = (byte)(_a >> 24); @@ -541,7 +576,24 @@ public void ToByteArray(byte[] destination, int offset) /// <returns>A string representation of the value.</returns> public override string ToString() { - return BsonUtils.ToHexString(ToByteArray()); + var buffer = new char[24]; + + for (int i = 7, a = _a, b = _b, c = _c; i >= 0; i--) + { + var r = (a & 0x0F); + buffer[i] = (char)(r < 10 ? (r + '0') : (r - 10 + 'a')); + a >>= 4; + + r = (b & 0x0F); + buffer[i + 8] = (char)(r < 10 ? (r + '0') : (r - 10 + 'a')); + b >>= 4; + + r = (c & 0x0F); + buffer[i + 16] = (char)(r < 10 ? (r + '0') : (r - 10 + 'a')); + c >>= 4; + } + + return new string(buffer); } // explicit IConvertible implementation diff --git a/tests/MongoDB.Bson.Tests/ObjectModel/Decimal128Tests.cs b/tests/MongoDB.Bson.Tests/ObjectModel/Decimal128Tests.cs index 541e461b111..554f499c727 100644 --- a/tests/MongoDB.Bson.Tests/ObjectModel/Decimal128Tests.cs +++ b/tests/MongoDB.Bson.Tests/ObjectModel/Decimal128Tests.cs @@ -46,7 +46,7 @@ public void Default_value() [InlineData("-79228162514264337593543950335", "-79228162514264337593543950335")] public void Decimal(string valueString, string s) { - var value = decimal.Parse(valueString); + var value = decimal.Parse(valueString, CultureInfo.InvariantCulture); var subject = new Decimal128(value); subject.ToString().Should().Be(s);