private static Int32 WriteJSONString( StreamWriterWithResizableBuffer stream, IEncodingInfo encoding, String str ) { // Write starting quote var asciiSize = encoding.BytesPerASCIICharacter; var eencoding = encoding.Encoding; // Allocate enough bytes for whole string and 2 quotes, we will allocate more as we encounter escapable characters var range = stream.ReserveBufferSegment(eencoding.GetByteCount(str) + 2 * asciiSize); var array = stream.Buffer; var start = range.Offset; var idx = start; encoding.WriteASCIIByte(array, ref idx, (Byte)STR_START); // Write contents in chunks var prevStrIdx = 0; for (var i = 0; i < str.Length; ++i) { var c = str[i]; if (NeedsEscaping(c)) { // Append previous chunk idx += eencoding.GetBytes(str, prevStrIdx, i - prevStrIdx, array, idx); // Make sure we have room for escape character stream.ReserveBufferSegment(asciiSize); array = stream.Buffer; // Append escape character encoding.WriteASCIIByte(array, ref idx, (Byte)STR_ESCAPE_PREFIX); // Append escape sequence latter character TransformToEscape(encoding, array, ref idx, c); // Update index prevStrIdx = i + 1; } } // Append final chunk var finalChunkSize = str.Length - prevStrIdx; if (finalChunkSize > 0) { idx += eencoding.GetBytes(str, prevStrIdx, finalChunkSize, array, idx); } // Append closing quote encoding.WriteASCIIByte(array, ref idx, (Byte)STR_END); return(idx - start); }
/// <inheritdoc /> public async ValueTask <Int32> TryWriteAsync(IEnumerable <Char> value, StreamWriterWithResizableBuffer sink) { if (Interlocked.CompareExchange(ref this._state, BUSY, IDLE) == IDLE) { try { var total = 0; var auxArray = this._auxArray; var encoding = this._encoding.Encoding; using (var enumerator = value.GetEnumerator()) { while (enumerator.MoveNext()) { var c = enumerator.Current; auxArray[0] = c; var charCount = 1; if (Char.IsHighSurrogate(c) && enumerator.MoveNext()) { // Must read next char auxArray[1] = enumerator.Current; ++charCount; } var count = this._maxSingleCharSize * charCount; if (sink.ReservedBufferCount + count > this._maxBufferSize) { await sink.FlushAsync(); } Int32 offset; (offset, count) = sink.ReserveBufferSegment(count); if (count > 0) { var actualCount = encoding.GetBytes(auxArray, 0, charCount, sink.Buffer, offset); total += actualCount; sink.UnreserveBufferSegment(count - actualCount); } } } if (total > 0) { await sink.FlushAsync(); } return(total); } finally { Interlocked.Exchange(ref this._state, IDLE); } } else { throw BusyException(); } }
private static async ValueTask <Int32> PerformWriteJSONTTokenAsync( StreamWriterWithResizableBuffer stream, IEncodingInfo encoding, JToken jsonValue ) { ArgumentValidator.ValidateNotNull(nameof(jsonValue), jsonValue); Int32 bytesWritten; var asciiSize = encoding.BytesPerASCIICharacter; Int32 max; (Int32 Offset, Int32 Count)range; switch (jsonValue) { case JArray array: range = stream.ReserveBufferSegment(asciiSize); encoding.WriteASCIIByte(stream.Buffer, ref range.Offset, (Byte)ARRAY_START); bytesWritten = range.Count; max = array.Count; if (max > 0) { for (var i = 0; i < max; ++i) { bytesWritten += await PerformWriteJSONTTokenAsync(stream, encoding, array[i]); if (i < max - 1) { range = stream.ReserveBufferSegment(asciiSize); encoding.WriteASCIIByte(stream.Buffer, ref range.Offset, (Byte)ARRAY_VALUE_DELIM); bytesWritten += range.Count; } } } range = stream.ReserveBufferSegment(asciiSize); encoding.WriteASCIIByte(stream.Buffer, ref range.Offset, (Byte)ARRAY_END); bytesWritten += await stream.FlushAsync(); break; case JObject obj: range = stream.ReserveBufferSegment(asciiSize); encoding.WriteASCIIByte(stream.Buffer, ref range.Offset, (Byte)OBJ_START); bytesWritten = range.Count; max = obj.Count; if (max > 0) { var j = 0; foreach (var kvp in obj) { bytesWritten += WriteJSONString(stream, encoding, kvp.Key); range = stream.ReserveBufferSegment(asciiSize); encoding.WriteASCIIByte(stream.Buffer, ref range.Offset, (Byte)OBJ_KEY_VALUE_DELIM); bytesWritten += range.Count; await stream.FlushAsync(); bytesWritten += await PerformWriteJSONTTokenAsync(stream, encoding, kvp.Value); if (++j < max) { range = stream.ReserveBufferSegment(asciiSize); encoding.WriteASCIIByte(stream.Buffer, ref range.Offset, (Byte)OBJ_VALUE_DELIM); bytesWritten += range.Count; } } } range = stream.ReserveBufferSegment(asciiSize); encoding.WriteASCIIByte(stream.Buffer, ref range.Offset, (Byte)OBJ_END); bytesWritten += await stream.FlushAsync(); break; case JValue value: var val = value.Value; switch (val) { case String str: bytesWritten = WriteJSONString(stream, encoding, str); break; case Boolean boolean: // Write 'true' or 'false' range = stream.ReserveBufferSegment(boolean ? 4 : 5); encoding.WriteString(stream.Buffer, ref range.Offset, boolean ? "true" : "false"); bytesWritten = range.Count; break; case Int64 i64: range = stream.ReserveBufferSegment(encoding.GetTextualIntegerRepresentationSize(i64)); encoding.WriteIntegerTextual(stream.Buffer, ref range.Offset, i64); bytesWritten = range.Count; break; case Double dbl: // Have to allocate string :/ var dblStr = dbl.ToString(System.Globalization.CultureInfo.InvariantCulture); range = stream.ReserveBufferSegment(encoding.Encoding.GetByteCount(dblStr)); encoding.WriteString(stream.Buffer, ref range.Offset, dblStr); bytesWritten = range.Count; break; case null: // Write 'null' range = stream.ReserveBufferSegment(asciiSize * 4); encoding.WriteString(stream.Buffer, ref range.Offset, "null"); bytesWritten = range.Count; break; default: throw new NotSupportedException($"Unsupported primitive value {val}."); } // Remember to flush stream await stream.FlushAsync(); break; default: throw new NotSupportedException($"Unrecognized JToken type: {jsonValue?.GetType()}."); } return(bytesWritten); }