Skip to content

Commit e1441da

Browse files
committed
CSHARP-5611: Eliminate the temporary byte array allocation in ObjectId.ToString
1 parent e62da2b commit e1441da

File tree

2 files changed

+85
-0
lines changed

2 files changed

+85
-0
lines changed

src/MongoDB.Bson/ObjectModel/ObjectId.cs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,12 +449,86 @@ public void ToByteArray(byte[] destination, int offset)
449449
destination[offset + 11] = (byte)(_c);
450450
}
451451

452+
#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
453+
/// <summary>
454+
/// Converts the ObjectId to a byte buffer provided by the input span.
455+
/// </summary>
456+
/// <param name="destination">The destination byte span.</param>
457+
/// <exception cref="ArgumentException">Not enough room in destination span.</exception>
458+
public void ToByteSpan(Span<byte> destination)
459+
{
460+
if (destination.Length < 12)
461+
{
462+
throw new ArgumentException("Not enough room in destination span.", "offset");
463+
}
464+
465+
destination[0] = (byte)(_a >> 24);
466+
destination[1] = (byte)(_a >> 16);
467+
destination[2] = (byte)(_a >> 8);
468+
destination[3] = (byte)(_a);
469+
destination[4] = (byte)(_b >> 24);
470+
destination[5] = (byte)(_b >> 16);
471+
destination[6] = (byte)(_b >> 8);
472+
destination[7] = (byte)(_b);
473+
destination[8] = (byte)(_c >> 24);
474+
destination[9] = (byte)(_c >> 16);
475+
destination[10] = (byte)(_c >> 8);
476+
destination[11] = (byte)(_c);
477+
}
478+
479+
/// <summary>
480+
/// Fills a character span with the characters corresponding to the string representation of the value.
481+
/// </summary>
482+
/// <param name="destination">The span to fill the characters in.</param>
483+
/// <exception cref="ArgumentException">Not enough room in destination span.</exception>
484+
public void ToCharSpan(Span<char> destination)
485+
{
486+
if (destination.Length < 24)
487+
{
488+
throw new ArgumentException("Not enough room in destination span.", "offset");
489+
}
490+
491+
Span<byte> span = stackalloc byte[12];
492+
ToByteSpan(span);
493+
destination[0] = BsonUtils.ToHexChar(span[3] >> 4);
494+
destination[1] = BsonUtils.ToHexChar(span[3] & 0xF);
495+
destination[2] = BsonUtils.ToHexChar(span[2] >> 4);
496+
destination[3] = BsonUtils.ToHexChar(span[2] & 0xF);
497+
destination[4] = BsonUtils.ToHexChar(span[1] >> 4);
498+
destination[5] = BsonUtils.ToHexChar(span[1] & 0xF);
499+
destination[6] = BsonUtils.ToHexChar(span[0] >> 4);
500+
destination[7] = BsonUtils.ToHexChar(span[0] & 0xF);
501+
destination[8] = BsonUtils.ToHexChar(span[7] >> 4);
502+
destination[9] = BsonUtils.ToHexChar(span[7] & 0xF);
503+
destination[10] = BsonUtils.ToHexChar(span[6] >> 4);
504+
destination[11] = BsonUtils.ToHexChar(span[6] & 0xF);
505+
destination[12] = BsonUtils.ToHexChar(span[5] >> 4);
506+
destination[13] = BsonUtils.ToHexChar(span[5] & 0xF);
507+
destination[14] = BsonUtils.ToHexChar(span[4] >> 4);
508+
destination[15] = BsonUtils.ToHexChar(span[4] & 0xF);
509+
destination[16] = BsonUtils.ToHexChar(span[11] >> 4);
510+
destination[17] = BsonUtils.ToHexChar(span[11] & 0xF);
511+
destination[18] = BsonUtils.ToHexChar(span[10] >> 4);
512+
destination[19] = BsonUtils.ToHexChar(span[10] & 0xF);
513+
destination[20] = BsonUtils.ToHexChar(span[9] >> 4);
514+
destination[21] = BsonUtils.ToHexChar(span[9] & 0xF);
515+
destination[22] = BsonUtils.ToHexChar(span[8] >> 4);
516+
destination[23] = BsonUtils.ToHexChar(span[8] & 0xF);
517+
}
518+
#endif
519+
452520
/// <summary>
453521
/// Returns a string representation of the value.
454522
/// </summary>
455523
/// <returns>A string representation of the value.</returns>
456524
public override string ToString()
457525
{
526+
#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
527+
return string.Create(24, this, static (span, input) =>
528+
{
529+
input.ToCharSpan(span);
530+
});
531+
#else
458532
var c = new char[24];
459533
c[0] = BsonUtils.ToHexChar((_a >> 28) & 0x0f);
460534
c[1] = BsonUtils.ToHexChar((_a >> 24) & 0x0f);
@@ -481,6 +555,7 @@ public override string ToString()
481555
c[22] = BsonUtils.ToHexChar((_c >> 4) & 0x0f);
482556
c[23] = BsonUtils.ToHexChar(_c & 0x0f);
483557
return new string(c);
558+
#endif
484559
}
485560

486561
// explicit IConvertible implementation

tests/MongoDB.Bson.Tests/ObjectModel/ObjectIdTests.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@ public void TestByteArrayConstructor()
8787
Assert.Equal(BsonConstants.UnixEpoch.AddSeconds(0x01020304), objectId.CreationTime);
8888
Assert.Equal("0102030405060708090a0b0c", objectId.ToString());
8989
Assert.True(bytes.SequenceEqual(objectId.ToByteArray()));
90+
#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
91+
Span<byte> span = new byte[12];
92+
objectId.ToByteSpan(span);
93+
Assert.True(span.SequenceEqual(bytes));
94+
#endif
9095
}
9196

9297
[Fact]
@@ -101,6 +106,11 @@ public void TestStringConstructor()
101106
Assert.Equal(BsonConstants.UnixEpoch.AddSeconds(0x01020304), objectId.CreationTime);
102107
Assert.Equal("0102030405060708090a0b0c", objectId.ToString());
103108
Assert.True(bytes.SequenceEqual(objectId.ToByteArray()));
109+
#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
110+
Span<char> span = new char[24];
111+
objectId.ToCharSpan(span);
112+
Assert.True(span.SequenceEqual("0102030405060708090a0b0c"));
113+
#endif
104114
}
105115

106116
[Fact]

0 commit comments

Comments
 (0)