##// END OF EJS Templates
JSONWriter improvements
cin -
r150:3258399cba83 v2
parent child
Show More
@@ -1,280 +1,319
1 using System;
1 using System;
2 using System.Collections.Generic;
2 using System.Collections.Generic;
3 using System.IO;
3 using System.IO;
4 using System.Globalization;
4 using System.Globalization;
5 using System.Diagnostics;
5
6
6 namespace Implab.JSON {
7 namespace Implab.JSON {
7 public class JSONWriter {
8 public class JSONWriter {
8 struct Context {
9 struct Context {
9 public bool needComma;
10 public bool needComma;
10 public JSONElementContext element;
11 public JSONElementContext element;
11 }
12 }
12 Stack<Context> m_contextStack = new Stack<Context>();
13 Stack<Context> m_contextStack = new Stack<Context>();
13 Context m_context;
14 Context m_context;
14
15
16 const int BUFFER_SIZE = 64;
17
15 TextWriter m_writer;
18 TextWriter m_writer;
16 readonly bool m_indent = true;
19 readonly bool m_indent = true;
17 readonly int m_indentSize = 4;
20 readonly int m_indentSize = 4;
21 readonly char[] m_buffer = new char[BUFFER_SIZE];
22 int m_bufferPos;
18
23
24 static readonly char [] _hex = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
19 static readonly char [] _escapeBKS,
25 static readonly char [] _escapeBKS,
20 _escapeFWD,
26 _escapeFWD,
21 _escapeCR,
27 _escapeCR,
22 _escapeNL,
28 _escapeNL,
23 _escapeTAB,
29 _escapeTAB,
24 _escapeBSLASH,
30 _escapeBSLASH,
25 _escapeQ;
31 _escapeQ;
26
32
27 static JSONWriter() {
33 static JSONWriter() {
28 _escapeBKS = "\\b".ToCharArray();
34 _escapeBKS = "\\b".ToCharArray();
29 _escapeFWD = "\\f".ToCharArray();
35 _escapeFWD = "\\f".ToCharArray();
30 _escapeCR = "\\r".ToCharArray();
36 _escapeCR = "\\r".ToCharArray();
31 _escapeNL = "\\n".ToCharArray();
37 _escapeNL = "\\n".ToCharArray();
32 _escapeTAB = "\\t".ToCharArray();
38 _escapeTAB = "\\t".ToCharArray();
33 _escapeBSLASH = "\\\\".ToCharArray();
39 _escapeBSLASH = "\\\\".ToCharArray();
34 _escapeQ = "\\\"".ToCharArray();
40 _escapeQ = "\\\"".ToCharArray();
35 }
41 }
36
42
37 public JSONWriter(TextWriter writer) {
43 public JSONWriter(TextWriter writer) {
38 Safe.ArgumentNotNull(writer, "writer");
44 Safe.ArgumentNotNull(writer, "writer");
39 m_writer = writer;
45 m_writer = writer;
40 }
46 }
41
47
42 public JSONWriter(TextWriter writer, bool indent) {
48 public JSONWriter(TextWriter writer, bool indent) {
43 Safe.ArgumentNotNull(writer, "writer");
49 Safe.ArgumentNotNull(writer, "writer");
44
50
45 m_writer = writer;
51 m_writer = writer;
46 m_indent = indent;
52 m_indent = indent;
47 }
53 }
48
54
49 void WriteIndent() {
55 void WriteIndent() {
50 if (m_indent) {
56 if (m_indent) {
51 var indent = new char[m_contextStack.Count * m_indentSize + 1];
57 var indent = new char[m_contextStack.Count * m_indentSize + 1];
52 indent[0] = '\n';
58 indent[0] = '\n';
53 for (int i = 1; i < indent.Length; i++)
59 for (int i = 1; i < indent.Length; i++)
54 indent[i] = ' ';
60 indent[i] = ' ';
55 m_writer.Write(new String(indent));
61 m_writer.Write(new String(indent));
56 } else {
62 } else {
57 m_writer.Write(' ');
63 m_writer.Write(' ');
58 }
64 }
59 }
65 }
60
66
61 void WriteMemberName(string name) {
67 void WriteMemberName(string name) {
62 Safe.ArgumentNotEmpty(name, "name");
68 Safe.ArgumentNotEmpty(name, "name");
63 if (m_context.element != JSONElementContext.Object)
69 if (m_context.element != JSONElementContext.Object)
64 OperationNotApplicable("WriteMember");
70 OperationNotApplicable("WriteMember");
65 if (m_context.needComma)
71 if (m_context.needComma)
66 m_writer.Write(",");
72 m_writer.Write(",");
67
73
68 WriteIndent();
74 WriteIndent();
69 m_context.needComma = true;
75 m_context.needComma = true;
70 Write(name);
76 Write(name);
71 m_writer.Write(" : ");
77 m_writer.Write(" : ");
72 }
78 }
73
79
74 public void WriteValue(string name, string value) {
80 public void WriteValue(string name, string value) {
75 WriteMemberName(name);
81 WriteMemberName(name);
76 Write(value);
82 Write(value);
77 }
83 }
78
84
79 public void WriteValue(string name, bool value) {
85 public void WriteValue(string name, bool value) {
80 WriteMemberName(name);
86 WriteMemberName(name);
81 Write(value);
87 Write(value);
82 }
88 }
83
89
84 public void WriteValue(string name, double value) {
90 public void WriteValue(string name, double value) {
85 WriteMemberName(name);
91 WriteMemberName(name);
86 Write(value);
92 Write(value);
87 }
93 }
88
94
89 public void WriteValue(string value) {
95 public void WriteValue(string value) {
90 if (m_context.element == JSONElementContext.Array) {
96 if (m_context.element == JSONElementContext.Array) {
91
97
92 if (m_context.needComma)
98 if (m_context.needComma)
93 m_writer.Write(",");
99 m_writer.Write(",");
94 WriteIndent();
100 WriteIndent();
95 m_context.needComma = true;
101 m_context.needComma = true;
96
102
97 Write(value);
103 Write(value);
98 } else if (m_context.element == JSONElementContext.None) {
104 } else if (m_context.element == JSONElementContext.None) {
99 Write(value);
105 Write(value);
100 m_context.element = JSONElementContext.Closed;
106 m_context.element = JSONElementContext.Closed;
101 } else {
107 } else {
102 OperationNotApplicable("WriteValue");
108 OperationNotApplicable("WriteValue");
103 }
109 }
104 }
110 }
105
111
106 public void WriteValue(bool value) {
112 public void WriteValue(bool value) {
107 if (m_context.element == JSONElementContext.Array) {
113 if (m_context.element == JSONElementContext.Array) {
108
114
109 if (m_context.needComma)
115 if (m_context.needComma)
110 m_writer.Write(",");
116 m_writer.Write(",");
111 WriteIndent();
117 WriteIndent();
112 m_context.needComma = true;
118 m_context.needComma = true;
113
119
114 Write(value);
120 Write(value);
115 } else if (m_context.element == JSONElementContext.None) {
121 } else if (m_context.element == JSONElementContext.None) {
116 Write(value);
122 Write(value);
117 m_context.element = JSONElementContext.Closed;
123 m_context.element = JSONElementContext.Closed;
118 } else {
124 } else {
119 OperationNotApplicable("WriteValue");
125 OperationNotApplicable("WriteValue");
120 }
126 }
121 }
127 }
122
128
123 public void WriteValue(double value) {
129 public void WriteValue(double value) {
124 if (m_context.element == JSONElementContext.Array) {
130 if (m_context.element == JSONElementContext.Array) {
125
131
126 if (m_context.needComma)
132 if (m_context.needComma)
127 m_writer.Write(",");
133 m_writer.Write(",");
128 WriteIndent();
134 WriteIndent();
129 m_context.needComma = true;
135 m_context.needComma = true;
130
136
131 Write(value);
137 Write(value);
132 } else if (m_context.element == JSONElementContext.None) {
138 } else if (m_context.element == JSONElementContext.None) {
133 Write(value);
139 Write(value);
134 m_context.element = JSONElementContext.Closed;
140 m_context.element = JSONElementContext.Closed;
135 } else {
141 } else {
136 OperationNotApplicable("WriteValue");
142 OperationNotApplicable("WriteValue");
137 }
143 }
138 }
144 }
139
145
140 public void BeginObject() {
146 public void BeginObject() {
141 if (m_context.element != JSONElementContext.None && m_context.element != JSONElementContext.Array)
147 if (m_context.element != JSONElementContext.None && m_context.element != JSONElementContext.Array)
142 OperationNotApplicable("BeginObject");
148 OperationNotApplicable("BeginObject");
143 if (m_context.needComma)
149 if (m_context.needComma)
144 m_writer.Write(",");
150 m_writer.Write(",");
145
151
146 WriteIndent();
152 WriteIndent();
147
153
148 m_context.needComma = true;
154 m_context.needComma = true;
149
155
150 m_contextStack.Push(m_context);
156 m_contextStack.Push(m_context);
151
157
152 m_context = new Context { element = JSONElementContext.Object, needComma = false };
158 m_context = new Context { element = JSONElementContext.Object, needComma = false };
153 m_writer.Write("{");
159 m_writer.Write("{");
154 }
160 }
155
161
156 public void BeginObject(string name) {
162 public void BeginObject(string name) {
157 WriteMemberName(name);
163 WriteMemberName(name);
158
164
159 m_contextStack.Push(m_context);
165 m_contextStack.Push(m_context);
160
166
161 m_context = new Context { element = JSONElementContext.Object, needComma = false };
167 m_context = new Context { element = JSONElementContext.Object, needComma = false };
162 m_writer.Write("{");
168 m_writer.Write("{");
163 }
169 }
164
170
165 public void EndObject() {
171 public void EndObject() {
166 if (m_context.element != JSONElementContext.Object)
172 if (m_context.element != JSONElementContext.Object)
167 OperationNotApplicable("EndObject");
173 OperationNotApplicable("EndObject");
168
174
169 m_context = m_contextStack.Pop();
175 m_context = m_contextStack.Pop();
170 if (m_contextStack.Count == 0)
176 if (m_contextStack.Count == 0)
171 m_context.element = JSONElementContext.Closed;
177 m_context.element = JSONElementContext.Closed;
172 WriteIndent();
178 WriteIndent();
173 m_writer.Write("}");
179 m_writer.Write("}");
174 }
180 }
175
181
176 public void BeginArray() {
182 public void BeginArray() {
177 if (m_context.element != JSONElementContext.None && m_context.element != JSONElementContext.Array)
183 if (m_context.element != JSONElementContext.None && m_context.element != JSONElementContext.Array)
178 throw new InvalidOperationException();
184 throw new InvalidOperationException();
179 if (m_context.needComma) {
185 if (m_context.needComma) {
180 m_writer.Write(",");
186 m_writer.Write(",");
181
187
182 }
188 }
183 m_context.needComma = true;
189 m_context.needComma = true;
184
190
185 WriteIndent();
191 WriteIndent();
186 m_contextStack.Push(m_context);
192 m_contextStack.Push(m_context);
187 m_context = new Context { element = JSONElementContext.Array, needComma = false };
193 m_context = new Context { element = JSONElementContext.Array, needComma = false };
188 m_writer.Write("[");
194 m_writer.Write("[");
189 }
195 }
190
196
191 public void BeginArray(string name) {
197 public void BeginArray(string name) {
192 WriteMemberName(name);
198 WriteMemberName(name);
193
199
194 m_contextStack.Push(m_context);
200 m_contextStack.Push(m_context);
195
201
196 m_context = new Context { element = JSONElementContext.Array, needComma = false };
202 m_context = new Context { element = JSONElementContext.Array, needComma = false };
197 m_writer.Write("[");
203 m_writer.Write("[");
198 }
204 }
199
205
200 public void EndArray() {
206 public void EndArray() {
201 if (m_context.element != JSONElementContext.Array)
207 if (m_context.element != JSONElementContext.Array)
202 OperationNotApplicable("EndArray");
208 OperationNotApplicable("EndArray");
203
209
204 m_context = m_contextStack.Pop();
210 m_context = m_contextStack.Pop();
205 if (m_contextStack.Count == 0)
211 if (m_contextStack.Count == 0)
206 m_context.element = JSONElementContext.Closed;
212 m_context.element = JSONElementContext.Closed;
207 WriteIndent();
213 WriteIndent();
208 m_writer.Write("]");
214 m_writer.Write("]");
209 }
215 }
210
216
211 void Write(bool value) {
217 void Write(bool value) {
212 m_writer.Write(value ? "true" : "false");
218 m_writer.Write(value ? "true" : "false");
213 }
219 }
214
220
221 void FlushBuffer() {
222 if (m_bufferPos > 0) {
223 m_writer.Write(m_buffer, 0, m_bufferPos);
224 m_bufferPos = 0;
225 }
226 }
215
227
216 void Write(string value) {
228 void Write(string value) {
217 if (value == null) {
229 if (value == null) {
218 m_writer.Write("null");
230 m_writer.Write("null");
219 return;
231 return;
220 }
232 }
221
233
234 Debug.Assert(m_bufferPos == 0);
235
222 var chars = value.ToCharArray();
236 var chars = value.ToCharArray();
223 m_writer.Write('"');
237 m_buffer[m_bufferPos++] = '"';
224
238
225 // Analysis disable once ForCanBeConvertedToForeach
239 // Analysis disable once ForCanBeConvertedToForeach
226 for (int i = 0; i < chars.Length; i++) {
240 for (int i = 0; i < chars.Length; i++) {
227 var ch = chars[i];
241 var ch = chars[i];
228
242
243 char[] escapeSeq;
244
229 switch (ch) {
245 switch (ch) {
230 case '\b':
246 case '\b':
231 m_writer.Write(_escapeBKS);
247 escapeSeq = _escapeBKS;
232 break;
248 break;
233 case '\f':
249 case '\f':
234 m_writer.Write(_escapeFWD);
250 escapeSeq = _escapeFWD;
235 break;
251 break;
236 case '\r':
252 case '\r':
237 m_writer.Write(_escapeCR);
253 escapeSeq = _escapeCR;
238 break;
254 break;
239 case '\n':
255 case '\n':
240 m_writer.Write(_escapeNL);
256 escapeSeq = _escapeNL;
241 break;
257 break;
242 case '\t':
258 case '\t':
243 m_writer.Write(_escapeTAB);
259 escapeSeq = _escapeTAB;
244 break;
260 break;
245 case '\\':
261 case '\\':
246 m_writer.Write(_escapeBSLASH);
262 escapeSeq = _escapeBSLASH;
247 break;
263 break;
248 case '"':
264 case '"':
249 m_writer.Write(_escapeQ);
265 escapeSeq = _escapeQ;
250 break;
266 break;
251 default:
267 default:
252 if (ch < 0x20) {
268 if (ch < 0x20) {
253 m_writer.Write("\\u00{0:x2}",(int)ch);
269 if (m_bufferPos + 6 > BUFFER_SIZE)
270 FlushBuffer();
271
272 m_buffer[m_bufferPos++] = '\\';
273 m_buffer[m_bufferPos++] = 'u';
274 m_buffer[m_bufferPos++] = '0';
275 m_buffer[m_bufferPos++] = '0';
276 m_buffer[m_bufferPos++] = _hex[ch >> 4 & 0xf];
277 m_buffer[m_bufferPos++] = _hex[ch & 0xf];
278
254 } else {
279 } else {
255 m_writer.Write(ch);
280 if (m_bufferPos >= BUFFER_SIZE)
281 FlushBuffer();
282 m_buffer[m_bufferPos++] = ch;
256 }
283 }
257 break;
284 continue;
258 }
285 }
286
287 if (m_bufferPos + escapeSeq.Length > BUFFER_SIZE)
288 FlushBuffer();
289
290 Array.Copy(escapeSeq, 0, m_buffer, m_bufferPos, escapeSeq.Length);
291 m_bufferPos += escapeSeq.Length;
292
259 }
293 }
260
294
261 m_writer.Write('"');
295 if (m_bufferPos >= BUFFER_SIZE)
296 FlushBuffer();
297
298 m_buffer[m_bufferPos++] = '"';
299
300 FlushBuffer();
262 }
301 }
263
302
264 void Write(double value) {
303 void Write(double value) {
265 if (double.IsNaN(value))
304 if (double.IsNaN(value))
266 Write("NaN");
305 Write("NaN");
267 else if (double.IsNegativeInfinity(value))
306 else if (double.IsNegativeInfinity(value))
268 Write("-Infinity");
307 Write("-Infinity");
269 else if (double.IsPositiveInfinity(value))
308 else if (double.IsPositiveInfinity(value))
270 Write("Infinity");
309 Write("Infinity");
271 else
310 else
272 m_writer.Write(value.ToString(CultureInfo.InvariantCulture));
311 m_writer.Write(value.ToString(CultureInfo.InvariantCulture));
273 }
312 }
274
313
275 void OperationNotApplicable(string opName) {
314 void OperationNotApplicable(string opName) {
276 throw new InvalidOperationException(String.Format("The operation '{0}' isn't applicable in the context of '{1}'", opName, m_context.element ));
315 throw new InvalidOperationException(String.Format("The operation '{0}' isn't applicable in the context of '{1}'", opName, m_context.element ));
277 }
316 }
278
317
279 }
318 }
280 }
319 }
@@ -1,40 +1,44
1 using System;
1 using System;
2 using Implab.Diagnostics;
2 using Implab.Diagnostics;
3 using Implab.Parallels;
3 using Implab.Parallels;
4 using Implab;
4 using Implab;
5 using System.Collections.Generic;
5 using System.Collections.Generic;
6 using System.Collections.Concurrent;
6 using System.Collections.Concurrent;
7 using System.Threading;
7 using System.Threading;
8 using Implab.JSON;
9 using System.IO;
8
10
9 namespace MonoPlay {
11 namespace MonoPlay {
10 class MainClass {
12 class MainClass {
11
13
12
14
13 public static void Main(string[] args) {
15 public static void Main(string[] args) {
14 if (args == null)
16 if (args == null)
15 throw new ArgumentNullException("args");
17 throw new ArgumentNullException("args");
16
18
17 var t1 = Environment.TickCount;
19 var t1 = Environment.TickCount;
18
20
19 for (int i = 0; i < 10000000; i++) {
21 for(int i =0; i < 1000000; i++)
20
22 using (var tw = new StringWriter()) {
21 var p = new Promise<int>();
23 var jw = new JSONWriter(tw);
22 p.On(HandleResult);
24
23 p.Resolve(i);
25 jw.WriteValue("\r\nhere\tvalue\u0002\u0003");
26
27 //Console.WriteLine(tw);
24 }
28 }
25
29
30
31
26 var t2 = Environment.TickCount;
32 var t2 = Environment.TickCount;
27 Console.WriteLine("done: {0} ms, {1:.00} Mb, {2} GC", t2 - t1, GC.GetTotalMemory(false) / (1024*1024), GC.CollectionCount(0) );
33 Console.WriteLine("done: {0} ms, {1:.00} Mb, {2} GC", t2 - t1, GC.GetTotalMemory(false) / (1024*1024), GC.CollectionCount(0) );
28
34
29 }
35 }
30
36
31 static void HandleAction ()
37 static void DoTest() {
32 {
38
33
39
40
34 }
41 }
35
42
36 static void HandleResult(int x) {
37
38 }
39 }
43 }
40 }
44 }
General Comments 0
You need to be logged in to leave comments. Login now