WritableTextSource
using AngleSharp.Common;
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace AngleSharp.Text
{
internal sealed class WritableTextSource : ITextSource, IReadOnlyTextSource, IDisposable
{
private enum EncodingConfidence : byte
{
Tentative,
Certain,
Irrelevant
}
private const int BufferSize = 4096;
private readonly Stream _baseStream;
private readonly MemoryStream _raw;
private readonly byte[] _buffer;
private readonly char[] _chars;
private StringBuilder _content;
private EncodingConfidence _confidence;
private bool _finished;
private Encoding _encoding;
private Decoder _decoder;
private int _index;
[System.Diagnostics.CodeAnalysis.MemberNotNull("_content")]
public string Text {
[System.Diagnostics.CodeAnalysis.MemberNotNull("_content")]
get {
return _content.ToString();
}
}
public char this[int index] {
get {
return Replace(_content[index]);
}
}
public int Length => _content.Length;
public Encoding CurrentEncoding {
get {
return _encoding;
}
set {
if (_confidence == EncodingConfidence.Tentative) {
if (_encoding.IsUnicode())
_confidence = EncodingConfidence.Certain;
else {
if (value.IsUnicode())
value = TextEncoding.Utf8;
if (value == _encoding)
_confidence = EncodingConfidence.Certain;
else {
_encoding = value;
_decoder = value.GetDecoder();
byte[] array = _raw.ToArray();
char[] array2 = new char[_encoding.GetMaxCharCount(array.Length)];
int chars = _decoder.GetChars(array, 0, array.Length, array2, 0);
string text = new string(array2, 0, chars);
int num = Math.Min(_index, text.Length);
if (!text.Substring(0, num).Is(_content.ToString(0, num))) {
_index = 0;
_content.Clear().Append(text);
throw new NotSupportedException();
}
_confidence = EncodingConfidence.Certain;
_content.Remove(num, _content.Length - num);
_content.Append(text.Substring(num));
}
}
}
}
}
public int Index {
get {
return _index;
}
set {
_index = value;
}
}
private WritableTextSource(Encoding encoding, bool allocateBuffers)
{
if (allocateBuffers) {
_buffer = new byte[4096];
_chars = new char[4097];
}
_raw = new MemoryStream();
_index = 0;
_encoding = (encoding ?? TextEncoding.Utf8);
_decoder = _encoding.GetDecoder();
}
public WritableTextSource(string source)
: this(null, TextEncoding.Utf8)
{
_finished = true;
_content.Append(source);
_confidence = EncodingConfidence.Irrelevant;
}
public WritableTextSource(Stream baseStream, Encoding encoding = null)
: this(encoding, baseStream != null)
{
_baseStream = baseStream;
_content = StringBuilderPool.Obtain();
_confidence = EncodingConfidence.Tentative;
}
public void Dispose()
{
if (_content != null) {
_raw.Dispose();
_content.Clear().ReturnToPool();
_content = null;
}
}
public char ReadCharacter()
{
if (_index < _content.Length)
return Replace(_content[_index++]);
ExpandBuffer(4096);
int num = _index++;
if (num >= _content.Length)
return '';
return Replace(_content[num]);
}
public string ReadCharacters(int characters)
{
int index = _index;
if (index + characters <= _content.Length) {
_index += characters;
return _content.ToString(index, characters);
}
ExpandBuffer(Math.Max(4096, characters));
_index += characters;
characters = Math.Min(characters, _content.Length - index);
return _content.ToString(index, characters);
}
public StringOrMemory ReadMemory(int characters)
{
return new StringOrMemory(ReadCharacters(characters));
}
public Task PrefetchAsync(int length, CancellationToken cancellationToken)
{
return ExpandBufferAsync(length, cancellationToken);
}
[AsyncStateMachine(typeof(<PrefetchAllAsync>d__31))]
public Task PrefetchAllAsync(CancellationToken cancellationToken)
{
<PrefetchAllAsync>d__31 stateMachine = default(<PrefetchAllAsync>d__31);
stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create();
stateMachine.<>4__this = this;
stateMachine.cancellationToken = cancellationToken;
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
public bool TryGetContentLength(out int length)
{
length = 0;
return false;
}
public void InsertText(string content)
{
if (_index >= 0 && _index < _content.Length)
_content.Insert(_index, content);
else
_content.Append(content);
_index += content.Length;
}
private static char Replace(char c)
{
if (c != '')
return c;
return '�';
}
[AsyncStateMachine(typeof(<DetectByteOrderMarkAsync>d__35))]
private Task DetectByteOrderMarkAsync(CancellationToken cancellationToken)
{
<DetectByteOrderMarkAsync>d__35 stateMachine = default(<DetectByteOrderMarkAsync>d__35);
stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create();
stateMachine.<>4__this = this;
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
[AsyncStateMachine(typeof(<ExpandBufferAsync>d__36))]
private Task ExpandBufferAsync(long size, CancellationToken cancellationToken)
{
<ExpandBufferAsync>d__36 stateMachine = default(<ExpandBufferAsync>d__36);
stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create();
stateMachine.<>4__this = this;
stateMachine.size = size;
stateMachine.cancellationToken = cancellationToken;
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
[AsyncStateMachine(typeof(<ReadIntoBufferAsync>d__37))]
private Task ReadIntoBufferAsync(CancellationToken cancellationToken)
{
<ReadIntoBufferAsync>d__37 stateMachine = default(<ReadIntoBufferAsync>d__37);
stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create();
stateMachine.<>4__this = this;
stateMachine.cancellationToken = cancellationToken;
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
private void ExpandBuffer(long size)
{
if (!_finished && _content.Length == 0)
DetectByteOrderMarkAsync(CancellationToken.None).Wait();
while (!_finished && size + _index > _content.Length) {
ReadIntoBuffer();
}
}
private void ReadIntoBuffer()
{
int size = _baseStream.Read(_buffer, 0, 4096);
AppendContentFromBuffer(size);
}
private void AppendContentFromBuffer(int size)
{
_finished = (size == 0);
int chars = _decoder.GetChars(_buffer, 0, size, _chars, 0);
if (_confidence != EncodingConfidence.Certain)
_raw.Write(_buffer, 0, size);
_content.Append(_chars, 0, chars);
}
}
}