|
13 | 13 | * limitations under the License.
|
14 | 14 | */
|
15 | 15 |
|
| 16 | +using System; |
| 17 | +using System.IO; |
| 18 | +using System.Text; |
| 19 | + |
16 | 20 | namespace MongoDB.Bson.IO
|
17 | 21 | {
|
18 | 22 | /// <summary>
|
19 |
| - /// This class represents a JSON string buffer. |
| 23 | + /// Represents a wrapper around a TextReader to provide some buffering functionality. |
20 | 24 | /// </summary>
|
21 |
| - public class JsonBuffer |
| 25 | + internal class JsonBuffer |
22 | 26 | {
|
23 | 27 | // private fields
|
24 |
| - private string _buffer; |
| 28 | + private readonly StringBuilder _buffer; |
25 | 29 | private int _position;
|
| 30 | + private readonly TextReader _reader; |
26 | 31 |
|
27 | 32 | // constructors
|
28 | 33 | /// <summary>
|
29 |
| - /// Initializes a new instance of the JsonBuffer class. |
| 34 | + /// Initializes a new instance of the <see cref="JsonBuffer"/> class. |
30 | 35 | /// </summary>
|
31 |
| - /// <param name="buffer">The string.</param> |
32 |
| - public JsonBuffer(string buffer) |
| 36 | + /// <param name="json">The json.</param> |
| 37 | + public JsonBuffer(string json) |
33 | 38 | {
|
34 |
| - _buffer = buffer; |
35 |
| - _position = 0; |
| 39 | + if (json == null) |
| 40 | + { |
| 41 | + throw new ArgumentNullException("json"); |
| 42 | + } |
| 43 | + _buffer = new StringBuilder(json); |
36 | 44 | }
|
37 | 45 |
|
38 |
| - // internal properties |
39 | 46 | /// <summary>
|
40 |
| - /// Gets the length of the JSON string. |
| 47 | + /// Initializes a new instance of the <see cref="JsonBuffer" /> class. |
41 | 48 | /// </summary>
|
42 |
| - public int Length |
| 49 | + /// <param name="reader">The reader.</param> |
| 50 | + public JsonBuffer(TextReader reader) |
43 | 51 | {
|
44 |
| - get { return _buffer.Length; } |
| 52 | + if (reader == null) |
| 53 | + { |
| 54 | + throw new ArgumentNullException("reader"); |
| 55 | + } |
| 56 | + _buffer = new StringBuilder(256); // start out with a reasonable initial capacity |
| 57 | + _reader = reader; |
45 | 58 | }
|
46 | 59 |
|
| 60 | + // public properties |
47 | 61 | /// <summary>
|
48 | 62 | /// Gets or sets the current position.
|
49 | 63 | /// </summary>
|
50 | 64 | public int Position
|
51 | 65 | {
|
52 | 66 | get { return _position; }
|
53 |
| - set { _position = value; } |
| 67 | + set |
| 68 | + { |
| 69 | + if (value < 0 || value > _buffer.Length) |
| 70 | + { |
| 71 | + var message = string.Format("Invalid position: {0}.", value); |
| 72 | + throw new ArgumentOutOfRangeException("value", message); |
| 73 | + } |
| 74 | + _position = value; |
| 75 | + } |
54 | 76 | }
|
55 | 77 |
|
56 | 78 | // public methods
|
57 | 79 | /// <summary>
|
58 |
| - /// Reads a character from the buffer. |
| 80 | + /// Gets a snippet of a maximum length from the buffer (usually to include in an error message). |
59 | 81 | /// </summary>
|
60 |
| - /// <returns>The next character (or -1 if at the end of the buffer).</returns> |
61 |
| - public int Read() |
| 82 | + /// <param name="start">The start.</param> |
| 83 | + /// <param name="maxLength">The maximum length.</param> |
| 84 | + /// <returns>The snippet.</returns> |
| 85 | + public string GetSnippet(int start, int maxLength) |
62 | 86 | {
|
63 |
| - return (_position >= _buffer.Length) ? -1 : _buffer[_position++]; |
| 87 | + if (start < 0) |
| 88 | + { |
| 89 | + throw new ArgumentOutOfRangeException("start", "Start cannot be negative."); |
| 90 | + } |
| 91 | + if (maxLength < 0) |
| 92 | + { |
| 93 | + throw new ArgumentOutOfRangeException("maxLength", "MaxLength cannot be negative."); |
| 94 | + } |
| 95 | + if (start > _position) |
| 96 | + { |
| 97 | + throw new ArgumentOutOfRangeException("start", "Start is beyond current position."); |
| 98 | + } |
| 99 | + var availableCount = _position - start; |
| 100 | + var count = Math.Min(availableCount, maxLength); |
| 101 | + return _buffer.ToString(start, count); |
64 | 102 | }
|
65 | 103 |
|
66 | 104 | /// <summary>
|
67 |
| - /// Reads a substring from the buffer. |
| 105 | + /// Gets a substring from the buffer. |
68 | 106 | /// </summary>
|
69 |
| - /// <param name="start">The zero based index of the start of the substring.</param> |
| 107 | + /// <param name="start">The start.</param> |
| 108 | + /// <param name="count">The count.</param> |
70 | 109 | /// <returns>The substring.</returns>
|
71 |
| - public string Substring(int start) |
| 110 | + public string GetSubstring(int start, int count) |
| 111 | + { |
| 112 | + if (start < 0) |
| 113 | + { |
| 114 | + throw new ArgumentOutOfRangeException("start", "Start cannot be negative."); |
| 115 | + } |
| 116 | + if (count < 0) |
| 117 | + { |
| 118 | + throw new ArgumentOutOfRangeException("count", "Count cannot be negative."); |
| 119 | + } |
| 120 | + if (start > _position) |
| 121 | + { |
| 122 | + throw new ArgumentOutOfRangeException("start", "Start is beyond current position."); |
| 123 | + } |
| 124 | + if (start + count > _position) |
| 125 | + { |
| 126 | + throw new ArgumentOutOfRangeException("start", "End of substring is beyond current position."); |
| 127 | + } |
| 128 | + return _buffer.ToString(start, count); |
| 129 | + } |
| 130 | + |
| 131 | + /// <summary> |
| 132 | + /// Reads the next character from the text reader and advances the character position by one character. |
| 133 | + /// </summary> |
| 134 | + /// <returns> |
| 135 | + /// The next character from the text reader, or -1 if no more characters are available. The default implementation returns -1. |
| 136 | + /// </returns> |
| 137 | + public int Read() |
72 | 138 | {
|
73 |
| - return _buffer.Substring(start); |
| 139 | + ReadMoreIfAtEndOfBuffer(); |
| 140 | + return _position >= _buffer.Length ? -1 : _buffer[_position++]; |
74 | 141 | }
|
75 | 142 |
|
76 | 143 | /// <summary>
|
77 |
| - /// Reads a substring from the buffer. |
| 144 | + /// Resets the buffer (clears everything up to the current position). |
78 | 145 | /// </summary>
|
79 |
| - /// <param name="start">The zero based index of the start of the substring.</param> |
80 |
| - /// <param name="count">The number of characters in the substring.</param> |
81 |
| - /// <returns>The substring.</returns> |
82 |
| - public string Substring(int start, int count) |
| 146 | + public void ResetBuffer() |
83 | 147 | {
|
84 |
| - return _buffer.Substring(start, count); |
| 148 | + // only trim the buffer if enough space will be reclaimed to make it worthwhile |
| 149 | + var minimumTrimCount = 256; // TODO: make configurable? |
| 150 | + if (_position >= minimumTrimCount) |
| 151 | + { |
| 152 | + _buffer.Remove(0, _position); |
| 153 | + _position = 0; |
| 154 | + } |
85 | 155 | }
|
86 | 156 |
|
87 | 157 | /// <summary>
|
88 |
| - /// Returns one character to the buffer (if the character matches the one at the current position the current position is moved back by one). |
| 158 | + /// Unreads one character (moving the current Position back one position). |
89 | 159 | /// </summary>
|
90 |
| - /// <param name="c">The character to return.</param> |
| 160 | + /// <param name="c">The character.</param> |
91 | 161 | public void UnRead(int c)
|
92 | 162 | {
|
93 |
| - if (c != -1 && _buffer[_position - 1] == c) |
| 163 | + if (_position == 0) |
94 | 164 | {
|
| 165 | + throw new InvalidOperationException("Unread called when nothing has been read."); |
| 166 | + } |
| 167 | + |
| 168 | + if (c == -1) |
| 169 | + { |
| 170 | + if (_position != _buffer.Length) |
| 171 | + { |
| 172 | + throw new InvalidOperationException("Unread called with -1 when position is not at the end of the buffer."); |
| 173 | + } |
| 174 | + } |
| 175 | + else |
| 176 | + { |
| 177 | + if (_buffer[_position - 1] != c) |
| 178 | + { |
| 179 | + throw new InvalidOperationException("Unread called with a character that does not match what is in the buffer."); |
| 180 | + } |
95 | 181 | _position -= 1;
|
96 | 182 | }
|
97 | 183 | }
|
| 184 | + |
| 185 | + // private methods |
| 186 | + private void ReadMoreIfAtEndOfBuffer() |
| 187 | + { |
| 188 | + if (_position >= _buffer.Length) |
| 189 | + { |
| 190 | + if (_reader != null) |
| 191 | + { |
| 192 | + var blockSize = 1024; // TODO: make configurable? |
| 193 | + var block = new char[blockSize]; |
| 194 | + var actualCount = _reader.ReadBlock(block, 0, blockSize); |
| 195 | + |
| 196 | + if (actualCount > 0) |
| 197 | + { |
| 198 | + _buffer.Append(block, 0, actualCount); |
| 199 | + } |
| 200 | + } |
| 201 | + } |
| 202 | + } |
98 | 203 | }
|
99 | 204 | }
|
0 commit comments