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

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: Address comments. Created 7 years, 4 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 return new _StringAdapterSink(sink);
18 }
19
20 /**
21 * Creates a [StringConversionSink] with a callback.
22 *
23 * The [callback] is invoked with the accumulated data when the sink is
24 * closed.
25 */
26 StringConversionSink createSink(void callback(String accumulated)) {
27 return new _StringCallbackSink(callback);
28 }
29 }
30
31 /**
32 * This class provides an interface for converters to
33 * efficiently transmit String data.
34 *
35 * Instead of limiting the interface to one non-chunked String it accepts
36 * partial strings and UTF-8 code units.
Søren Gjesse 2013/07/25 08:07:00 The comment here is a bit misleading. It says "acc
floitsch 2013/07/25 12:57:24 Rewritten.
37 */
38 abstract class StringConversionSink
39 extends ChunkedConversionSink<String> {
40
41 static const ChunkedConversionInterface INTERFACE = const _StringInterface();
42
43 StringConversionSink();
44
45 /**
46 * Creates a new instance wrapping the given [sink].
47 *
48 * Every string that is added to the returned instance is forwarded to
49 * the [sink]. The instance is allowed to buffer and is not required to
50 * forward immediately.
51 */
52 factory StringConversionSink.fromStringSink(StringSink sink) =
53 _StringSinkConversionSink;
54
55 /**
56 * Adds the next [chunk] to `this`.
57 *
58 * Adds the substring defined by [start] and [end]-exclusive to `this`.
59 *
60 * If [isLast] is `true` closes `this`.
61 */
62 void addSlice(String chunk, int start, int end, bool isLast);
63
64 /**
65 * Returns `this` as a sink that accepts UTF-8 input.
66 *
67 * This method must be the first and only call to `this`. It invalidates
Søren Gjesse 2013/07/25 08:07:00 Prefix "This meth..." with "If used".
floitsch 2013/07/25 12:57:24 Done.
68 * `this`. All further operations must be performed on the result.
69 */
70 ByteConversionSink asUtf8Sink(bool allowMalformed);
71 // - asRuneSink
72 // - asCodeUnitsSink
73
74 /**
75 * Returns `this` as a [ClosableStringSink].
76 *
77 * This method must be the first and only call to `this`. It invalidates
Søren Gjesse 2013/07/25 08:07:00 Ditto.
floitsch 2013/07/25 12:57:24 Done.
78 * `this`. All further operations must be performed on the result.
79 */
80 ClosableStringSink asStringSink();
81
82 ChunkedConversionInterface get interface => INTERFACE;
83 }
84
85 /**
86 * A [ClosableStringSink] extends the [StringSink] interface by adding a
87 * `close` method.
88 */
89 abstract class ClosableStringSink extends StringSink {
90 /**
91 * Creates a new instance combining a [StringSink] [sink] and a callback
92 * [onClose] which is invoked when the returned instance is closed.
93 */
94 factory ClosableStringSink.fromStringSink(StringSink sink, void onClose())
95 = _ClosableStringSink;
96
97 /**
98 * Closes `this` and flushes any outstanding data.
99 */
100 void close();
101 }
102
103 typedef void _StringSinkCloseCallback();
104
105 /**
106 * This class wraps an existing [StringSink] and invokes a
107 * closure when [close] is invoked.
108 */
109 class _ClosableStringSink implements ClosableStringSink {
110 final _StringSinkCloseCallback _callback;
111 final StringSink _sink;
112
113 _ClosableStringSink(this._sink, this._callback);
114
115 void close() => _callback();
116
117 void writeCharCode(int charCode) => _sink.writeCharCode(charCode);
118 void write(Object o) => _sink.write(o);
119 void writeln([Object o]) => _sink.writeln(o);
120 void writeAll(Iterable objects, [String separator])
121 => _sink.writeAll(objects, separator);
122 }
123
124 /**
125 * This class wraps an existing [StringConversionSink] and exposes a
126 * [ClosableStringSink] interface. The wrapped sink only needs to implement
127 * `add` and `close`.
128 */
129 // TODO(floitsch): make this class public?
130 class _StringConversionSinkAsStringSinkAdapter implements ClosableStringSink {
131 static const _MIN_STRING_SIZE = 16;
132
133 StringBuffer _buffer;
134 StringConversionSink _chunkedSink;
135
136 _StringConversionSinkAsStringSinkAdapter(this._chunkedSink)
137 : _buffer = new StringBuffer();
138
139 void close() {
140 if (_buffer.isNotEmpty) _flush();
141 _chunkedSink.close();
142 }
143
144 void writeCharCode(int charCode) {
145 _buffer.writeCharCode(charCode);
146 if (_buffer.length > _MIN_STRING_SIZE) _flush();
147 }
148
149 void write(Object o) {
150 if (_buffer.isNotEmpty) _flush();
151 String str = o.toString();
152 _chunkedSink.add(o.toString());
153 }
154
155 void writeln([Object o]) {
156 _buffer.writeln(o);
157 if (_buffer.length > _MIN_STRING_SIZE) _flush();
158 }
159
160 void writeAll(Iterable objects, [String separator]) {
161 if (_buffer.isNotEmpty) _flush();
162 Iterator iterator = objects.iterator;
163 if (!iterator.moveNext()) return;
164 if (separator.isEmpty) {
165 do {
166 _chunkedSink.add(iterator.current.toString());
167 } while (iterator.moveNext());
168 } else {
169 _chunkedSink.add(iterator.current.toString());
170 while (iterator.moveNext()) {
171 write(separator);
172 _chunkedSink.add(iterator.current.toString());
173 }
174 }
175 }
176
177 void _flush() {
178 String accumulated = _buffer.toString();
179 _buffer.clear();
180 _chunkedSink.add(accumulated);
181 }
182 }
183
184 /**
185 * This class provides a base-class for converters that need to accept String
186 * inputs.
187 */
188 abstract class StringConversionSinkBase extends StringConversionSinkMixin {
189 }
190
191 /**
192 * This class provides a mixin for converters that need to accept String
193 * inputs.
194 */
195 abstract class StringConversionSinkMixin implements StringConversionSink {
196
197 void addSlice(String str, int start, int end, bool isLast);
198 void close();
199
200 void add(String str) => addSlice(str, 0, str.length, false);
201
202 ByteConversionSink asUtf8Sink(bool allowMalformed) {
203 return new _Utf8ConversionSink(this, allowMalformed);
204 }
205
206 ChunkedConversionInterface get interface => StringConversionSink.INTERFACE;
207
208 ClosableStringSink asStringSink() {
209 return new _StringConversionSinkAsStringSinkAdapter(this);
210 }
211
212 ChunkedConversionSink adaptTo(ChunkedConversionInterface interface) {
213 if (this.interface == interface) return this;
214 return interface.adapt(this);
215 }
216 }
217
218 /**
219 * This class is a [StringConversionSink] that wraps a [StringSink].
220 */
221 class _StringSinkConversionSink extends StringConversionSinkBase {
222 StringSink _stringSink;
223 _StringSinkConversionSink(StringSink this._stringSink);
224
225 void close() {}
226 void addSlice(String str, int start, int end, bool isLast) {
227 if (start != 0 || end != str.length) {
228 for (int i = start; i < end; i++) {
229 _stringSink.writeCharCode(str.codeUnitAt(i));
230 }
231 } else {
232 _stringSink.write(str);
233 }
234 if (isLast) close();
235 }
236
237 void add(String str) => _stringSink.write(str);
238
239 ByteConversionSink asUtf8Sink(bool allowMalformed) {
240 return new _Utf8StringSinkAdapter(this, _stringSink, allowMalformed);
241 }
242
243 ClosableStringSink asStringSink() {
244 return new ClosableStringSink.fromStringSink(_stringSink, this.close);
245 }
246 }
247
248 /**
249 * This class accumulates all chunks into one string
250 * and invokes a callback when the sink is closed.
251 *
252 * This class can be used to terminate a chunked conversion.
253 */
254 class _StringCallbackSink extends _StringSinkConversionSink {
255 final _ChunkedConversionCallback<String> _callback;
256 _StringCallbackSink(this._callback) : super(new StringBuffer());
257
258 void close() {
259 StringBuffer buffer = _stringSink;
260 String accumulated = buffer.toString();
261 buffer.clear();
262 _callback(accumulated);
263 }
264
265 ByteConversionSink asUtf8Sink(bool allowMalformed) {
266 return new _Utf8StringSinkAdapter(
267 this, _stringSink, allowMalformed);
268 }
269 }
270
271 /**
272 * This class adapts a simple [ChunkedConversionSink] to a
273 * [StringConversionSink].
274 *
275 * All additional methods of the [StringConversionSink] (compared to the
276 * ChunkedConversionSink) are redirected to the `add` method.
277 */
278 class _StringAdapterSink extends StringConversionSinkBase {
279 final ChunkedConversionSink<String> _sink;
280
281 _StringAdapterSink(this._sink);
282
283 void add(String str) => _sink.add(str);
284
285 void addSlice(String str, int start, int end, bool isLast) {
286 if (start == 0 && end == str.length) {
287 add(str);
288 } else {
289 add(str.substring(start, end));
290 }
291 if (isLast) close();
292 }
293
294 void close() => _sink.close();
295 }
296
297
298 /**
299 * Decodes UTF-8 code units and stores them in a [StringSink].
300 */
301 class _Utf8StringSinkAdapter extends ByteConversionSink {
302 final _Utf8Decoder _decoder;
303 final ChunkedConversionSink _chunkedSink;
304
305 _Utf8StringSinkAdapter(ChunkedConversionSink chunkedSink,
306 StringSink sink, bool allowMalformed)
307 : _chunkedSink = chunkedSink,
308 _decoder = new _Utf8Decoder(sink, allowMalformed);
309
310 void close() {
311 _decoder.close();
312 if(_chunkedSink != null) _chunkedSink.close();
313 }
314
315 void add(List<int> chunk) {
316 addSlice(chunk, 0, chunk.length, false);
317 }
318
319 void addSlice(List<int> codeUnits, int startIndex, int endIndex,
320 bool isLast) {
321 _decoder.convert(codeUnits, startIndex, endIndex);
322 if (isLast) close();
323 }
324 }
325
326 /**
327 * Decodes UTF-8 code units.
328 *
329 * Forwards the decoded strings to the given [StringConversionSink].
330 */
331 // TODO(floitsch): make this class public?
332 class _Utf8ConversionSink extends ByteConversionSink {
333 static const _MIN_STRING_SIZE = 16;
334
335 final _Utf8Decoder _decoder;
336 final StringConversionSink _chunkedSink;
337 final StringBuffer _buffer;
338 _Utf8ConversionSink(StringConversionSink sink, bool allowMalformed)
339 : this._(sink, new StringBuffer(), allowMalformed);
340
341 _Utf8ConversionSink._(this._chunkedSink, StringBuffer stringBuffer,
342 bool allowMalformed)
343 : _decoder = new _Utf8Decoder(stringBuffer, allowMalformed),
344 _buffer = stringBuffer;
345
346 void close() {
347 _decoder.close();
348 if (_buffer.isNotEmpty) {
349 String accumulated = _buffer.toString();
350 _buffer.clear();
351 _chunkedSink.addSlice(accumulated, 0, accumulated.length, true);
352 } else {
353 _chunkedSink.close();
354 }
355 }
356
357 void add(List<int> chunk) {
358 addSlice(chunk, 0, chunk.length, false);
359 }
360
361 void addSlice(List<int> chunk, int startIndex, int endIndex,
362 bool isLast) {
363 _decoder.convert(chunk, startIndex, endIndex);
364 if (_buffer.length > _MIN_STRING_SIZE) {
365 String accumulated = _buffer.toString();
366 _chunkedSink.addSlice(accumulated, 0, accumulated.length, isLast);
367 _buffer.clear();
368 return;
369 }
370 if (isLast) close();
371 }
372 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698