Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1071)

Side by Side Diff: sdk/lib/convert/string_conversion.dart

Issue 19883003: Add chunked conversion to converters. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Improve some typse. Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 part of dart.convert;
6
7 /**
8 * This class represents the [StringConversionSink] interface. It is
9 * accessed through [StringConversionSink.INTERFACE] or as instance field
10 * `interface` from [StringConversionSink]s.
11 */
12 class _StringInterface extends ChunkedConversionInterface {
13 const _StringInterface();
14
15 StringConversionSink adapt(ChunkedConversionSink sink) {
16 if (sink.interface == StringConversionSink.INTERFACE) return sink;
17 // Common interface is ChunkedConversionSink.
18 return new _StringAdapterSink(sink);
19 }
20 }
21
22 /**
23 * This class provides an interface for converters to
24 * efficiently transmit String data.
25 *
26 * Instead of limiting the interface to one non-chunked String it accepts
27 * partial strings and UTF-8 code units.
28 */
29 abstract class StringConversionSink
30 extends ChunkedConversionSink<String, String> {
31
32 static const ChunkedConversionInterface INTERFACE = const _StringInterface();
33
34 StringConversionSink();
35
36 /**
37 * Creates a new instance wrapping the given [sink].
38 *
39 * Every string that is added to the returned instance is forwarded to
40 * the [sink]. The instance is allowed to buffer and is not required to
41 * forward immediately.
42 */
43 factory StringConversionSink.fromStringSink(StringSink sink) =
44 _StringSinkConversionSink;
45
46 /**
47 * Adds the next [chunk] to `this`.
48 *
49 * Adds the substring defined by [start] and [end]-exclusive to `this`.
50 *
51 * If [isLast] is `true` closes `this`.
52 */
53 void addSlice(String chunk, int start, int end, bool isLast);
54
55 /**
56 * Returns `this` as a sink that accepts UTF-8 input.
57 *
58 * This method must be the first and only call to `this`. It invalidates
59 * `this`. All further operations must be performed on the result.
60 */
61 ByteConversionSink asUtf8Sink(bool allowMalformed);
62 // - asRuneSink
63 // - asCodeUnitsSink
64
65 /**
66 * Returns `this` as a [ClosableStringSink].
67 *
68 * This method must be the first and only call to `this`. It invalidates
69 * `this`. All further operations must be performed on the result.
70 */
71 ClosableStringSink asStringSink();
72
73 ChunkedConversionInterface get interface => INTERFACE;
74 }
75
76 /**
77 * A [ClosableStringSink] extends the [StringSink] interface by adding a
78 * `close` method.
79 */
80 abstract class ClosableStringSink extends StringSink {
81 /**
82 * Creates a new instance combining a [StringSink] [sink] and a callback
83 * [onClose] which is invoked when the returned instance is closed.
84 */
85 factory ClosableStringSink.fromStringSink(StringSink sink, void onClose())
86 = _ClosableStringSink;
87
88 /**
89 * Closes `this` and flushes any outstanding data.
90 */
91 void close();
92 }
93
94 typedef void _StringSinkCloseCallback();
95
96 /**
97 * This class wraps an existing [StringSink] and invokes a
98 * closure when [close] is invoked.
99 */
100 class _ClosableStringSink implements ClosableStringSink {
101 final _StringSinkCloseCallback _callback;
102 final StringSink _sink;
103
104 _ClosableStringSink(this._sink, this._callback);
105
106 void close() => _callback();
107
108 void writeCharCode(int charCode) => _sink.writeCharCode(charCode);
109 void write(Object o) => _sink.write(o);
110 void writeln([Object o]) => _sink.writeln(o);
111 void writeAll(Iterable objects, [String separator])
112 => _sink.writeAll(objects, separator);
113 }
114
115 // TODO(floitsch): make this class public?
116 /**
117 * This class wraps an existing [StringConversionSink] and exposes a
118 * [ClosableStringSink] interface. The wrapped sink only needs to implement
119 * `add` and `close`.
120 */
121 class _StringConversionSinkAsStringSinkAdapter implements ClosableStringSink {
122 static const _MIN_STRING_SIZE = 16;
123
124 StringBuffer _buffer;
125 StringConversionSink _chunkedSink;
126
127 _StringConversionSinkAsStringSinkAdapter(this._chunkedSink)
128 : _buffer = new StringBuffer();
129
130 void close() {
131 if (_buffer.isNotEmpty) _flush();
132 _chunkedSink.close();
133 }
134
135 void writeCharCode(int charCode) {
136 _buffer.writeCharCode(charCode);
137 if (_buffer.length > _MIN_STRING_SIZE) _flush();
138 }
139
140 void write(Object o) {
141 if (_buffer.isNotEmpty) _flush();
142 String str = o.toString();
143 _chunkedSink.add(o.toString());
144 }
145
146 void writeln([Object o]) {
147 _buffer.writeln(o);
148 if (_buffer.length > _MIN_STRING_SIZE) _flush();
149 }
150
151 void writeAll(Iterable objects, [String separator]) {
152 if (_buffer.isNotEmpty) _flush();
153 Iterator iterator = objects.iterator;
154 if (!iterator.moveNext()) return;
155 if (separator.isEmpty) {
156 do {
157 _chunkedSink.add(iterator.current.toString());
158 } while (iterator.moveNext());
159 } else {
160 _chunkedSink.add(iterator.current.toString());
161 while (iterator.moveNext()) {
162 write(separator);
163 _chunkedSink.add(iterator.current.toString());
164 }
165 }
166 }
167
168 void _flush() {
169 String accumulated = _buffer.toString();
170 _buffer.clear();
171 _chunkedSink.add(accumulated);
172 }
173 }
174
175 /**
176 * This class provides a base-class for converters that need to accept String
177 * inputs.
178 */
179 abstract class StringConversionSinkBase extends StringConversionSinkMixin {
180 }
181
182 /**
183 * This class provides a mixin for converters that need to accept String
184 * inputs.
185 */
186 abstract class StringConversionSinkMixin implements StringConversionSink {
187
188 void addSlice(String str, int start, int end, bool isLast);
189 void close();
190
191 void addNonChunked(String str) => addSlice(str, 0, str.length, true);
192 void add(String str) => addSlice(str, 0, str.length, false);
193
194 ByteConversionSink asUtf8Sink(bool allowMalformed) {
195 return new _Utf8ConversionSink(this, allowMalformed);
196 }
197
198 ChunkedConversionInterface get interface => StringConversionSink.INTERFACE;
199
200 ClosableStringSink asStringSink() {
201 return new _StringConversionSinkAsStringSinkAdapter(this);
202 }
203
204 ChunkedConversionSink adaptTo(ChunkedConversionInterface interface) {
205 if (this.interface == interface) return this;
206 return interface.adapt(this);
207 }
208 }
209
210 /**
211 * This class is a [StringConversionSink] that wraps a [StringSink].
212 */
213 class _StringSinkConversionSink extends StringConversionSinkBase {
214 StringSink _stringSink;
215 _StringSinkConversionSink(StringSink this._stringSink);
216
217 void close() {}
218 void addSlice(String str, int start, int end, bool isLast) {
219 if (start != 0 || end != str.length) {
220 for (int i = start; i < end; i++) {
221 _stringSink.writeCharCode(str.codeUnitAt(i));
222 }
223 } else {
224 _stringSink.write(str);
225 }
226 if (isLast) close();
227 }
228
229 void add(String str) => _stringSink.write(str);
230
231 ByteConversionSink asUtf8Sink(bool allowMalformed) {
232 return new _Utf8StringSinkAdapter(this, _stringSink, allowMalformed);
233 }
234
235 ClosableStringSink asStringSink() {
236 return new ClosableStringSink.fromStringSink(_stringSink, this.close);
237 }
238 }
239
240 /**
241 * This class accumulates all chunks and gives it to
242 * the wrapped sink in one go (using `addNonChunked`).
243 *
244 * This class can be used to terminate a chunked conversion.
245 */
246 class _StringAdapterSink extends _StringSinkConversionSink {
247 ChunkedConversionSink _chunkedSink;
248 _StringAdapterSink(this._chunkedSink) : super(new StringBuffer());
249
250 void close() {
251 StringBuffer buffer = _stringSink;
252 _chunkedSink.addNonChunked(buffer.toString());
253 buffer.clear();
254 }
255
256 ByteConversionSink asUtf8Sink(bool allowMalformed) {
257 return new _Utf8StringSinkAdapter(
258 this, _stringSink, allowMalformed);
259 }
260 }
261
262 /**
263 * Decodes UTF-8 code units and stores them in a [StringSink].
264 */
265 class _Utf8StringSinkAdapter extends ByteConversionSink {
266 final _Utf8Decoder _decoder;
267 final ChunkedConversionSink _chunkedSink;
268
269 _Utf8StringSinkAdapter(ChunkedConversionSink chunkedSink,
270 StringSink sink, bool allowMalformed)
271 : _chunkedSink = chunkedSink,
272 _decoder = new _Utf8Decoder(sink, allowMalformed);
273
274 void close() {
275 _decoder.close();
276 if(_chunkedSink != null) _chunkedSink.close();
277 }
278
279 void add(List<int> chunk) {
280 addSlice(chunk, 0, chunk.length, false);
281 }
282
283 void addNonChunked(List<int> list) {
284 addSlice(list, 0, list.length, true);
285 }
286
287 void addSlice(List<int> codeUnits, int startIndex, int endIndex,
288 bool isLast) {
289 _decoder.convert(codeUnits, startIndex, endIndex);
290 if (isLast) close();
291 }
292 }
293
294 /**
295 * Decodes UTF-8 code units.
296 *
297 * Forwards the decoded strings to the given [StringConversionSink].
298 */
299 // TODO(floitsch): make this class public?
300 class _Utf8ConversionSink extends ByteConversionSink {
301 static const _MIN_STRING_SIZE = 16;
302
303 final _Utf8Decoder _decoder;
304 final StringConversionSink _chunkedSink;
305 final StringBuffer _buffer;
306 _Utf8ConversionSink(StringConversionSink sink, bool allowMalformed)
307 : this._(sink, new StringBuffer(), allowMalformed);
308
309 _Utf8ConversionSink._(this._chunkedSink, StringBuffer stringBuffer,
310 bool allowMalformed)
311 : _decoder = new _Utf8Decoder(stringBuffer, allowMalformed),
312 _buffer = stringBuffer;
313
314 void close() {
315 _decoder.close();
316 if (_buffer.isNotEmpty) {
317 String accumulated = _buffer.toString();
318 _buffer.clear();
319 _chunkedSink.addSlice(accumulated, 0, accumulated.length, true);
320 } else {
321 _chunkedSink.close();
322 }
323 }
324
325 void add(List<int> chunk) {
326 addSlice(chunk, 0, chunk.length, false);
327 }
328
329 void addNonChunked(List<int> list) {
330 addSlice(list, 0, list.length, true);
331 }
332
333 void addSlice(List<int> chunk, int startIndex, int endIndex,
334 bool isLast) {
335 _decoder.convert(chunk, startIndex, endIndex);
336 if (_buffer.length > _MIN_STRING_SIZE) {
337 String accumulated = _buffer.toString();
338 _chunkedSink.addSlice(accumulated, 0, accumulated.length, isLast);
339 _buffer.clear();
340 return;
341 }
342 if (isLast) close();
343 }
344 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698