/* Copyright 2010-2014 MongoDB Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.IO; using System.Text; namespace MongoDB.Bson.IO { /// /// Represents a wrapper around a TextReader to provide some buffering functionality. /// internal class JsonBuffer { // private fields private readonly StringBuilder _buffer; private int _position; private readonly TextReader _reader; // constructors /// /// Initializes a new instance of the class. /// /// The json. public JsonBuffer(string json) { if (json == null) { throw new ArgumentNullException("json"); } _buffer = new StringBuilder(json); } /// /// Initializes a new instance of the class. /// /// The reader. public JsonBuffer(TextReader reader) { if (reader == null) { throw new ArgumentNullException("reader"); } _buffer = new StringBuilder(256); // start out with a reasonable initial capacity _reader = reader; } // public properties /// /// Gets or sets the current position. /// public int Position { get { return _position; } set { if (value < 0 || value > _buffer.Length) { var message = string.Format("Invalid position: {0}.", value); throw new ArgumentOutOfRangeException("value", message); } _position = value; } } // public methods /// /// Gets a snippet of a maximum length from the buffer (usually to include in an error message). /// /// The start. /// The maximum length. /// The snippet. public string GetSnippet(int start, int maxLength) { if (start < 0) { throw new ArgumentOutOfRangeException("start", "Start cannot be negative."); } if (maxLength < 0) { throw new ArgumentOutOfRangeException("maxLength", "MaxLength cannot be negative."); } if (start > _position) { throw new ArgumentOutOfRangeException("start", "Start is beyond current position."); } var availableCount = _position - start; var count = Math.Min(availableCount, maxLength); return _buffer.ToString(start, count); } /// /// Gets a substring from the buffer. /// /// The start. /// The count. /// The substring. public string GetSubstring(int start, int count) { if (start < 0) { throw new ArgumentOutOfRangeException("start", "Start cannot be negative."); } if (count < 0) { throw new ArgumentOutOfRangeException("count", "Count cannot be negative."); } if (start > _position) { throw new ArgumentOutOfRangeException("start", "Start is beyond current position."); } if (start + count > _position) { throw new ArgumentOutOfRangeException("start", "End of substring is beyond current position."); } return _buffer.ToString(start, count); } /// /// Reads the next character from the text reader and advances the character position by one character. /// /// /// The next character from the text reader, or -1 if no more characters are available. The default implementation returns -1. /// public int Read() { ReadMoreIfAtEndOfBuffer(); return _position >= _buffer.Length ? -1 : _buffer[_position++]; } /// /// Resets the buffer (clears everything up to the current position). /// public void ResetBuffer() { // only trim the buffer if enough space will be reclaimed to make it worthwhile var minimumTrimCount = 256; // TODO: make configurable? if (_position >= minimumTrimCount) { _buffer.Remove(0, _position); _position = 0; } } /// /// Unreads one character (moving the current Position back one position). /// /// The character. public void UnRead(int c) { if (_position == 0) { throw new InvalidOperationException("Unread called when nothing has been read."); } if (c == -1) { if (_position != _buffer.Length) { throw new InvalidOperationException("Unread called with -1 when position is not at the end of the buffer."); } } else { if (_buffer[_position - 1] != c) { throw new InvalidOperationException("Unread called with a character that does not match what is in the buffer."); } _position -= 1; } } // private methods private void ReadMoreIfAtEndOfBuffer() { if (_position >= _buffer.Length) { if (_reader != null) { var blockSize = 1024; // TODO: make configurable? var block = new char[blockSize]; var actualCount = _reader.ReadBlock(block, 0, blockSize); if (actualCount > 0) { _buffer.Append(block, 0, actualCount); } } } } } }