Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify trimming non significant digits in JsonUtf8Writer #51367

Merged
merged 3 commits into from
Apr 19, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,103 +42,68 @@ public static void WriteDateTimeOffsetTrimmed(Span<byte> buffer, DateTimeOffset
// 2017-06-12T05:30:45 (interpreted as local time wrt to current time zone)
public static void TrimDateTimeOffset(Span<byte> buffer, out int bytesWritten)
{
const int maxDateTimeLength = JsonConstants.MaximumFormatDateTimeLength;

// Assert buffer is the right length for:
// YYYY-MM-DDThh:mm:ss.fffffff (JsonConstants.MaximumFormatDateTimeLength)
// YYYY-MM-DDThh:mm:ss.fffffffZ (JsonConstants.MaximumFormatDateTimeLength + 1)
// YYYY-MM-DDThh:mm:ss.fffffff(+|-)hh:mm (JsonConstants.MaximumFormatDateTimeOffsetLength)
Debug.Assert(buffer.Length == JsonConstants.MaximumFormatDateTimeLength ||
buffer.Length == (JsonConstants.MaximumFormatDateTimeLength + 1) ||
Debug.Assert(buffer.Length == maxDateTimeLength ||
buffer.Length == maxDateTimeLength + 1 ||
buffer.Length == JsonConstants.MaximumFormatDateTimeOffsetLength);

uint digit7 = buffer[26] - (uint)'0';
uint digit6 = buffer[25] - (uint)'0';
uint digit5 = buffer[24] - (uint)'0';
uint digit4 = buffer[23] - (uint)'0';
uint digit3 = buffer[22] - (uint)'0';
uint digit2 = buffer[21] - (uint)'0';
uint digit1 = buffer[20] - (uint)'0';
uint fraction = (digit1 * 1_000_000) + (digit2 * 100_000) + (digit3 * 10_000) + (digit4 * 1_000) + (digit5 * 100) + (digit6 * 10) + digit7;

// The period's index
int curIndex = 19;

if (fraction > 0)
// Find the last significant digit.
int curIndex;
if (buffer[maxDateTimeLength - 1] == '0')
if (buffer[maxDateTimeLength - 2] == '0')
if (buffer[maxDateTimeLength - 3] == '0')
if (buffer[maxDateTimeLength - 4] == '0')
if (buffer[maxDateTimeLength - 5] == '0')
if (buffer[maxDateTimeLength - 6] == '0')
if (buffer[maxDateTimeLength - 7] == '0')
{
// All decimal places are 0 so we can delete the decimal point too.
curIndex = maxDateTimeLength - 7 - 1;
}
else { curIndex = maxDateTimeLength - 6; }
else { curIndex = maxDateTimeLength - 5; }
else { curIndex = maxDateTimeLength - 4; }
else { curIndex = maxDateTimeLength - 3; }
else { curIndex = maxDateTimeLength - 2; }
else { curIndex = maxDateTimeLength - 1; }
else
{
int numFractionDigits = 7;

// Remove trailing zeros
while (true)
{
(uint quotient, uint remainder) = DivRem(fraction, 10);
if (remainder != 0)
{
break;
}
fraction = quotient;
numFractionDigits--;
}

// The last fraction digit's index will be (the period's index plus one) + (the number of fraction digits minus one)
int fractionEnd = 19 + numFractionDigits;

// Write fraction
// Leading zeros are written because the fraction becomes zero when it's their turn
for (int i = fractionEnd; i > curIndex; i--)
{
buffer[i] = (byte)((fraction % 10) + (uint)'0');
fraction /= 10;
}

curIndex = fractionEnd + 1;
// There is nothing to trim.
bytesWritten = buffer.Length;
return;
}

bytesWritten = curIndex;

// We are either trimming a DateTimeOffset, or a DateTime with
// DateTimeKind.Local or DateTimeKind.Utc
if (buffer.Length > JsonConstants.MaximumFormatDateTimeLength)
if (buffer.Length == maxDateTimeLength)
{
// Write offset

buffer[curIndex] = buffer[27];
// There is no offset to copy.
bytesWritten = curIndex;
}
else if (buffer.Length == JsonConstants.MaximumFormatDateTimeOffsetLength)
{
// We have a non-UTC offset (+|-)hh:mm that are 6 characters to copy.
buffer[curIndex] = buffer[maxDateTimeLength];
buffer[curIndex + 1] = buffer[maxDateTimeLength + 1];
buffer[curIndex + 2] = buffer[maxDateTimeLength + 2];
buffer[curIndex + 3] = buffer[maxDateTimeLength + 3];
buffer[curIndex + 4] = buffer[maxDateTimeLength + 4];
buffer[curIndex + 5] = buffer[maxDateTimeLength + 5];
bytesWritten = curIndex + 6;
}
else
devsko marked this conversation as resolved.
Show resolved Hide resolved
{
// There is a single 'Z'. Just write it at the current index.
Debug.Assert(buffer[maxDateTimeLength] == 'Z');

// curIndex is at one of 'Z', '+', or '-'
buffer[curIndex] = (byte)'Z';
bytesWritten = curIndex + 1;

// We have a Non-UTC offset i.e. (+|-)hh:mm
if (buffer.Length == JsonConstants.MaximumFormatDateTimeOffsetLength)
{
// Last index of the offset
int bufferEnd = curIndex + 5;

// Cache offset characters to prevent them from being overwritten
// The second minute digit is never at risk
byte offsetMinDigit1 = buffer[31];
byte offsetHourDigit2 = buffer[29];
byte offsetHourDigit1 = buffer[28];

Debug.Assert(buffer[30] == JsonConstants.Colon);

// Write offset characters
buffer[bufferEnd] = buffer[32];
buffer[bufferEnd - 1] = offsetMinDigit1;
buffer[bufferEnd - 2] = JsonConstants.Colon;
buffer[bufferEnd - 3] = offsetHourDigit2;
buffer[bufferEnd - 4] = offsetHourDigit1;

// bytes written is the last index of the offset + 1
bytesWritten = bufferEnd + 1;
}
}
}

// We don't always have access to System.Math.DivRem,
// so this is a copy of the implementation.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static (uint Quotient, uint Remainder) DivRem(uint left, uint right)
{
uint quotient = left / right;
return (quotient, left - (quotient * right));
}
}
}