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

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

Powered by Google App Engine
This is Rietveld 408576698