]> git.proxmox.com Git - ceph.git/blob - ceph/src/jaegertracing/thrift/lib/d/src/thrift/protocol/json.d
56a71daccd417722e04ce7a6b09e1a41de52be31
[ceph.git] / ceph / src / jaegertracing / thrift / lib / d / src / thrift / protocol / json.d
1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 module thrift.protocol.json;
20
21 import std.algorithm;
22 import std.array;
23 import std.base64;
24 import std.conv;
25 import std.range;
26 import std.string : format;
27 import std.traits : isIntegral;
28 import std.typetuple : allSatisfy, TypeTuple;
29 import std.utf : toUTF8;
30 import thrift.protocol.base;
31 import thrift.transport.base;
32
33 alias Base64Impl!('+', '/', Base64.NoPadding) Base64NoPad;
34
35 /**
36 * Implementation of the Thrift JSON protocol.
37 */
38 final class TJsonProtocol(Transport = TTransport) if (
39 isTTransport!Transport
40 ) : TProtocol {
41 /**
42 * Constructs a new instance.
43 *
44 * Params:
45 * trans = The transport to use.
46 * containerSizeLimit = If positive, the container size is limited to the
47 * given number of items.
48 * stringSizeLimit = If positive, the string length is limited to the
49 * given number of bytes.
50 */
51 this(Transport trans, int containerSizeLimit = 0, int stringSizeLimit = 0) {
52 trans_ = trans;
53 this.containerSizeLimit = containerSizeLimit;
54 this.stringSizeLimit = stringSizeLimit;
55
56 context_ = new Context();
57 reader_ = new LookaheadReader(trans);
58 }
59
60 Transport transport() @property {
61 return trans_;
62 }
63
64 void reset() {
65 destroy(contextStack_);
66 context_ = new Context();
67 reader_ = new LookaheadReader(trans_);
68 }
69
70 /**
71 * If positive, limits the number of items of deserialized containers to the
72 * given amount.
73 *
74 * This is useful to avoid allocating excessive amounts of memory when broken
75 * data is received. If the limit is exceeded, a SIZE_LIMIT-type
76 * TProtocolException is thrown.
77 *
78 * Defaults to zero (no limit).
79 */
80 int containerSizeLimit;
81
82 /**
83 * If positive, limits the length of deserialized strings/binary data to the
84 * given number of bytes.
85 *
86 * This is useful to avoid allocating excessive amounts of memory when broken
87 * data is received. If the limit is exceeded, a SIZE_LIMIT-type
88 * TProtocolException is thrown.
89 *
90 * Note: For binary data, the limit applies to the length of the
91 * Base64-encoded string data, not the resulting byte array.
92 *
93 * Defaults to zero (no limit).
94 */
95 int stringSizeLimit;
96
97 /*
98 * Writing methods.
99 */
100
101 void writeBool(bool b) {
102 writeJsonInteger(b ? 1 : 0);
103 }
104
105 void writeByte(byte b) {
106 writeJsonInteger(b);
107 }
108
109 void writeI16(short i16) {
110 writeJsonInteger(i16);
111 }
112
113 void writeI32(int i32) {
114 writeJsonInteger(i32);
115 }
116
117 void writeI64(long i64) {
118 writeJsonInteger(i64);
119 }
120
121 void writeDouble(double dub) {
122 context_.write(trans_);
123
124 string value;
125 if (dub is double.nan) {
126 value = NAN_STRING;
127 } else if (dub is double.infinity) {
128 value = INFINITY_STRING;
129 } else if (dub is -double.infinity) {
130 value = NEG_INFINITY_STRING;
131 }
132
133 bool escapeNum = value !is null || context_.escapeNum;
134
135 if (value is null) {
136 /* precision is 17 */
137 value = format("%.17g", dub);
138 }
139
140 if (escapeNum) trans_.write(STRING_DELIMITER);
141 trans_.write(cast(ubyte[])value);
142 if (escapeNum) trans_.write(STRING_DELIMITER);
143 }
144
145 void writeString(string str) {
146 context_.write(trans_);
147 trans_.write(STRING_DELIMITER);
148 foreach (c; str) {
149 writeJsonChar(c);
150 }
151 trans_.write(STRING_DELIMITER);
152 }
153
154 void writeBinary(ubyte[] buf) {
155 context_.write(trans_);
156
157 trans_.write(STRING_DELIMITER);
158 ubyte[4] b;
159 while (!buf.empty) {
160 auto toWrite = take(buf, 3);
161 Base64NoPad.encode(toWrite, b[]);
162 trans_.write(b[0 .. toWrite.length + 1]);
163 buf.popFrontN(toWrite.length);
164 }
165 trans_.write(STRING_DELIMITER);
166 }
167
168 void writeMessageBegin(TMessage msg) {
169 writeJsonArrayBegin();
170 writeJsonInteger(THRIFT_JSON_VERSION);
171 writeString(msg.name);
172 writeJsonInteger(cast(byte)msg.type);
173 writeJsonInteger(msg.seqid);
174 }
175
176 void writeMessageEnd() {
177 writeJsonArrayEnd();
178 }
179
180 void writeStructBegin(TStruct tstruct) {
181 writeJsonObjectBegin();
182 }
183
184 void writeStructEnd() {
185 writeJsonObjectEnd();
186 }
187
188 void writeFieldBegin(TField field) {
189 writeJsonInteger(field.id);
190 writeJsonObjectBegin();
191 writeString(getNameFromTType(field.type));
192 }
193
194 void writeFieldEnd() {
195 writeJsonObjectEnd();
196 }
197
198 void writeFieldStop() {}
199
200 void writeListBegin(TList list) {
201 writeJsonArrayBegin();
202 writeString(getNameFromTType(list.elemType));
203 writeJsonInteger(list.size);
204 }
205
206 void writeListEnd() {
207 writeJsonArrayEnd();
208 }
209
210 void writeMapBegin(TMap map) {
211 writeJsonArrayBegin();
212 writeString(getNameFromTType(map.keyType));
213 writeString(getNameFromTType(map.valueType));
214 writeJsonInteger(map.size);
215 writeJsonObjectBegin();
216 }
217
218 void writeMapEnd() {
219 writeJsonObjectEnd();
220 writeJsonArrayEnd();
221 }
222
223 void writeSetBegin(TSet set) {
224 writeJsonArrayBegin();
225 writeString(getNameFromTType(set.elemType));
226 writeJsonInteger(set.size);
227 }
228
229 void writeSetEnd() {
230 writeJsonArrayEnd();
231 }
232
233
234 /*
235 * Reading methods.
236 */
237
238 bool readBool() {
239 return readJsonInteger!byte() ? true : false;
240 }
241
242 byte readByte() {
243 return readJsonInteger!byte();
244 }
245
246 short readI16() {
247 return readJsonInteger!short();
248 }
249
250 int readI32() {
251 return readJsonInteger!int();
252 }
253
254 long readI64() {
255 return readJsonInteger!long();
256 }
257
258 double readDouble() {
259 context_.read(reader_);
260
261 if (reader_.peek() == STRING_DELIMITER) {
262 auto str = readJsonString(true);
263 if (str == NAN_STRING) {
264 return double.nan;
265 }
266 if (str == INFINITY_STRING) {
267 return double.infinity;
268 }
269 if (str == NEG_INFINITY_STRING) {
270 return -double.infinity;
271 }
272
273 if (!context_.escapeNum) {
274 // Throw exception -- we should not be in a string in this case
275 throw new TProtocolException("Numeric data unexpectedly quoted",
276 TProtocolException.Type.INVALID_DATA);
277 }
278 try {
279 return to!double(str);
280 } catch (ConvException e) {
281 throw new TProtocolException(`Expected numeric value; got "` ~ str ~
282 `".`, TProtocolException.Type.INVALID_DATA);
283 }
284 }
285 else {
286 if (context_.escapeNum) {
287 // This will throw - we should have had a quote if escapeNum == true
288 readJsonSyntaxChar(STRING_DELIMITER);
289 }
290
291 auto str = readJsonNumericChars();
292 try {
293 return to!double(str);
294 } catch (ConvException e) {
295 throw new TProtocolException(`Expected numeric value; got "` ~ str ~
296 `".`, TProtocolException.Type.INVALID_DATA);
297 }
298 }
299 }
300
301 string readString() {
302 return readJsonString(false);
303 }
304
305 ubyte[] readBinary() {
306 return Base64NoPad.decode(readString());
307 }
308
309 TMessage readMessageBegin() {
310 TMessage msg = void;
311
312 readJsonArrayBegin();
313
314 auto ver = readJsonInteger!short();
315 if (ver != THRIFT_JSON_VERSION) {
316 throw new TProtocolException("Message contained bad version.",
317 TProtocolException.Type.BAD_VERSION);
318 }
319
320 msg.name = readString();
321 msg.type = cast(TMessageType)readJsonInteger!byte();
322 msg.seqid = readJsonInteger!short();
323
324 return msg;
325 }
326
327 void readMessageEnd() {
328 readJsonArrayEnd();
329 }
330
331 TStruct readStructBegin() {
332 readJsonObjectBegin();
333 return TStruct();
334 }
335
336 void readStructEnd() {
337 readJsonObjectEnd();
338 }
339
340 TField readFieldBegin() {
341 TField f = void;
342 f.name = null;
343
344 auto ch = reader_.peek();
345 if (ch == OBJECT_END) {
346 f.type = TType.STOP;
347 } else {
348 f.id = readJsonInteger!short();
349 readJsonObjectBegin();
350 f.type = getTTypeFromName(readString());
351 }
352
353 return f;
354 }
355
356 void readFieldEnd() {
357 readJsonObjectEnd();
358 }
359
360 TList readListBegin() {
361 readJsonArrayBegin();
362 auto type = getTTypeFromName(readString());
363 auto size = readContainerSize();
364 return TList(type, size);
365 }
366
367 void readListEnd() {
368 readJsonArrayEnd();
369 }
370
371 TMap readMapBegin() {
372 readJsonArrayBegin();
373 auto keyType = getTTypeFromName(readString());
374 auto valueType = getTTypeFromName(readString());
375 auto size = readContainerSize();
376 readJsonObjectBegin();
377 return TMap(keyType, valueType, size);
378 }
379
380 void readMapEnd() {
381 readJsonObjectEnd();
382 readJsonArrayEnd();
383 }
384
385 TSet readSetBegin() {
386 readJsonArrayBegin();
387 auto type = getTTypeFromName(readString());
388 auto size = readContainerSize();
389 return TSet(type, size);
390 }
391
392 void readSetEnd() {
393 readJsonArrayEnd();
394 }
395
396 private:
397 void pushContext(Context c) {
398 contextStack_ ~= context_;
399 context_ = c;
400 }
401
402 void popContext() {
403 context_ = contextStack_.back;
404 contextStack_.popBack();
405 contextStack_.assumeSafeAppend();
406 }
407
408 /*
409 * Writing functions
410 */
411
412 // Write the character ch as a Json escape sequence ("\u00xx")
413 void writeJsonEscapeChar(ubyte ch) {
414 trans_.write(ESCAPE_PREFIX);
415 trans_.write(ESCAPE_PREFIX);
416 auto outCh = hexChar(cast(ubyte)(ch >> 4));
417 trans_.write((&outCh)[0 .. 1]);
418 outCh = hexChar(ch);
419 trans_.write((&outCh)[0 .. 1]);
420 }
421
422 // Write the character ch as part of a Json string, escaping as appropriate.
423 void writeJsonChar(ubyte ch) {
424 if (ch >= 0x30) {
425 if (ch == '\\') { // Only special character >= 0x30 is '\'
426 trans_.write(BACKSLASH);
427 trans_.write(BACKSLASH);
428 } else {
429 trans_.write((&ch)[0 .. 1]);
430 }
431 }
432 else {
433 auto outCh = kJsonCharTable[ch];
434 // Check if regular character, backslash escaped, or Json escaped
435 if (outCh == 1) {
436 trans_.write((&ch)[0 .. 1]);
437 } else if (outCh > 1) {
438 trans_.write(BACKSLASH);
439 trans_.write((&outCh)[0 .. 1]);
440 } else {
441 writeJsonEscapeChar(ch);
442 }
443 }
444 }
445
446 // Convert the given integer type to a Json number, or a string
447 // if the context requires it (eg: key in a map pair).
448 void writeJsonInteger(T)(T num) if (isIntegral!T) {
449 context_.write(trans_);
450
451 auto escapeNum = context_.escapeNum();
452 if (escapeNum) trans_.write(STRING_DELIMITER);
453 trans_.write(cast(ubyte[])to!string(num));
454 if (escapeNum) trans_.write(STRING_DELIMITER);
455 }
456
457 void writeJsonObjectBegin() {
458 context_.write(trans_);
459 trans_.write(OBJECT_BEGIN);
460 pushContext(new PairContext());
461 }
462
463 void writeJsonObjectEnd() {
464 popContext();
465 trans_.write(OBJECT_END);
466 }
467
468 void writeJsonArrayBegin() {
469 context_.write(trans_);
470 trans_.write(ARRAY_BEGIN);
471 pushContext(new ListContext());
472 }
473
474 void writeJsonArrayEnd() {
475 popContext();
476 trans_.write(ARRAY_END);
477 }
478
479 /*
480 * Reading functions
481 */
482
483 int readContainerSize() {
484 auto size = readJsonInteger!int();
485 if (size < 0) {
486 throw new TProtocolException(TProtocolException.Type.NEGATIVE_SIZE);
487 } else if (containerSizeLimit > 0 && size > containerSizeLimit) {
488 throw new TProtocolException(TProtocolException.Type.SIZE_LIMIT);
489 }
490 return size;
491 }
492
493 void readJsonSyntaxChar(ubyte[1] ch) {
494 return readSyntaxChar(reader_, ch);
495 }
496
497 wchar readJsonEscapeChar() {
498 auto a = reader_.read();
499 auto b = reader_.read();
500 auto c = reader_.read();
501 auto d = reader_.read();
502 return cast(ushort)(
503 (hexVal(a[0]) << 12) + (hexVal(b[0]) << 8) +
504 (hexVal(c[0]) << 4) + hexVal(d[0])
505 );
506 }
507
508 string readJsonString(bool skipContext = false) {
509 if (!skipContext) context_.read(reader_);
510
511 readJsonSyntaxChar(STRING_DELIMITER);
512 auto buffer = appender!string();
513
514 wchar[] wchs;
515 int bytesRead;
516 while (true) {
517 auto ch = reader_.read();
518 if (ch == STRING_DELIMITER) {
519 break;
520 }
521
522 ++bytesRead;
523 if (stringSizeLimit > 0 && bytesRead > stringSizeLimit) {
524 throw new TProtocolException(TProtocolException.Type.SIZE_LIMIT);
525 }
526
527 if (ch == BACKSLASH) {
528 ch = reader_.read();
529 if (ch == ESCAPE_CHAR) {
530 auto wch = readJsonEscapeChar();
531 if (wch >= 0xD800 && wch <= 0xDBFF) {
532 wchs ~= wch;
533 } else if (wch >= 0xDC00 && wch <= 0xDFFF && wchs.length == 0) {
534 throw new TProtocolException("Missing UTF-16 high surrogate.",
535 TProtocolException.Type.INVALID_DATA);
536 } else {
537 wchs ~= wch;
538 buffer.put(wchs.toUTF8);
539 wchs = [];
540 }
541 continue;
542 } else {
543 auto pos = countUntil(kEscapeChars[], ch[0]);
544 if (pos == -1) {
545 throw new TProtocolException("Expected control char, got '" ~
546 cast(char)ch[0] ~ "'.", TProtocolException.Type.INVALID_DATA);
547 }
548 ch = kEscapeCharVals[pos];
549 }
550 }
551 if (wchs.length != 0) {
552 throw new TProtocolException("Missing UTF-16 low surrogate.",
553 TProtocolException.Type.INVALID_DATA);
554 }
555 buffer.put(ch[0]);
556 }
557
558 if (wchs.length != 0) {
559 throw new TProtocolException("Missing UTF-16 low surrogate.",
560 TProtocolException.Type.INVALID_DATA);
561 }
562 return buffer.data;
563 }
564
565 // Reads a sequence of characters, stopping at the first one that is not
566 // a valid Json numeric character.
567 string readJsonNumericChars() {
568 string str;
569 while (true) {
570 auto ch = reader_.peek();
571 if (!isJsonNumeric(ch[0])) {
572 break;
573 }
574 reader_.read();
575 str ~= ch;
576 }
577 return str;
578 }
579
580 // Reads a sequence of characters and assembles them into a number,
581 // returning them via num
582 T readJsonInteger(T)() if (isIntegral!T) {
583 context_.read(reader_);
584 if (context_.escapeNum()) {
585 readJsonSyntaxChar(STRING_DELIMITER);
586 }
587 auto str = readJsonNumericChars();
588 T num;
589 try {
590 num = to!T(str);
591 } catch (ConvException e) {
592 throw new TProtocolException(`Expected numeric value, got "` ~ str ~ `".`,
593 TProtocolException.Type.INVALID_DATA);
594 }
595 if (context_.escapeNum()) {
596 readJsonSyntaxChar(STRING_DELIMITER);
597 }
598 return num;
599 }
600
601 void readJsonObjectBegin() {
602 context_.read(reader_);
603 readJsonSyntaxChar(OBJECT_BEGIN);
604 pushContext(new PairContext());
605 }
606
607 void readJsonObjectEnd() {
608 readJsonSyntaxChar(OBJECT_END);
609 popContext();
610 }
611
612 void readJsonArrayBegin() {
613 context_.read(reader_);
614 readJsonSyntaxChar(ARRAY_BEGIN);
615 pushContext(new ListContext());
616 }
617
618 void readJsonArrayEnd() {
619 readJsonSyntaxChar(ARRAY_END);
620 popContext();
621 }
622
623 static {
624 final class LookaheadReader {
625 this(Transport trans) {
626 trans_ = trans;
627 }
628
629 ubyte[1] read() {
630 if (hasData_) {
631 hasData_ = false;
632 } else {
633 trans_.readAll(data_);
634 }
635 return data_;
636 }
637
638 ubyte[1] peek() {
639 if (!hasData_) {
640 trans_.readAll(data_);
641 hasData_ = true;
642 }
643 return data_;
644 }
645
646 private:
647 Transport trans_;
648 bool hasData_;
649 ubyte[1] data_;
650 }
651
652 /*
653 * Class to serve as base Json context and as base class for other context
654 * implementations
655 */
656 class Context {
657 /**
658 * Write context data to the transport. Default is to do nothing.
659 */
660 void write(Transport trans) {}
661
662 /**
663 * Read context data from the transport. Default is to do nothing.
664 */
665 void read(LookaheadReader reader) {}
666
667 /**
668 * Return true if numbers need to be escaped as strings in this context.
669 * Default behavior is to return false.
670 */
671 bool escapeNum() @property {
672 return false;
673 }
674 }
675
676 // Context class for object member key-value pairs
677 class PairContext : Context {
678 this() {
679 first_ = true;
680 colon_ = true;
681 }
682
683 override void write(Transport trans) {
684 if (first_) {
685 first_ = false;
686 colon_ = true;
687 } else {
688 trans.write(colon_ ? PAIR_SEP : ELEM_SEP);
689 colon_ = !colon_;
690 }
691 }
692
693 override void read(LookaheadReader reader) {
694 if (first_) {
695 first_ = false;
696 colon_ = true;
697 } else {
698 auto ch = (colon_ ? PAIR_SEP : ELEM_SEP);
699 colon_ = !colon_;
700 return readSyntaxChar(reader, ch);
701 }
702 }
703
704 // Numbers must be turned into strings if they are the key part of a pair
705 override bool escapeNum() @property {
706 return colon_;
707 }
708
709 private:
710 bool first_;
711 bool colon_;
712 }
713
714 class ListContext : Context {
715 this() {
716 first_ = true;
717 }
718
719 override void write(Transport trans) {
720 if (first_) {
721 first_ = false;
722 } else {
723 trans.write(ELEM_SEP);
724 }
725 }
726
727 override void read(LookaheadReader reader) {
728 if (first_) {
729 first_ = false;
730 } else {
731 readSyntaxChar(reader, ELEM_SEP);
732 }
733 }
734
735 private:
736 bool first_;
737 }
738
739 // Read 1 character from the transport trans and verify that it is the
740 // expected character ch.
741 // Throw a protocol exception if it is not.
742 void readSyntaxChar(LookaheadReader reader, ubyte[1] ch) {
743 auto ch2 = reader.read();
744 if (ch2 != ch) {
745 throw new TProtocolException("Expected '" ~ cast(char)ch[0] ~ "', got '" ~
746 cast(char)ch2[0] ~ "'.", TProtocolException.Type.INVALID_DATA);
747 }
748 }
749 }
750
751 // Probably need to implement a better stack at some point.
752 Context[] contextStack_;
753 Context context_;
754
755 Transport trans_;
756 LookaheadReader reader_;
757 }
758
759 /**
760 * TJsonProtocol construction helper to avoid having to explicitly specify
761 * the transport type, i.e. to allow the constructor being called using IFTI
762 * (see $(LINK2 http://d.puremagic.com/issues/show_bug.cgi?id=6082, D Bugzilla
763 * enhancement requet 6082)).
764 */
765 TJsonProtocol!Transport tJsonProtocol(Transport)(Transport trans,
766 int containerSizeLimit = 0, int stringSizeLimit = 0
767 ) if (isTTransport!Transport) {
768 return new TJsonProtocol!Transport(trans, containerSizeLimit, stringSizeLimit);
769 }
770
771 unittest {
772 import std.exception;
773 import thrift.transport.memory;
774
775 // Check the message header format.
776 auto buf = new TMemoryBuffer;
777 auto json = tJsonProtocol(buf);
778 json.writeMessageBegin(TMessage("foo", TMessageType.CALL, 0));
779 json.writeMessageEnd();
780
781 auto header = new ubyte[13];
782 buf.readAll(header);
783 enforce(cast(char[])header == `[1,"foo",1,0]`);
784 }
785
786 unittest {
787 import std.exception;
788 import thrift.transport.memory;
789
790 // Check that short binary data is read correctly (the Thrift JSON format
791 // does not include padding chars in the Base64 encoded data).
792 auto buf = new TMemoryBuffer;
793 auto json = tJsonProtocol(buf);
794 json.writeBinary([1, 2]);
795 json.reset();
796 enforce(json.readBinary() == [1, 2]);
797 }
798
799 unittest {
800 import std.exception;
801 import thrift.transport.memory;
802
803 auto buf = new TMemoryBuffer(cast(ubyte[])"\"\\u0e01 \\ud835\\udd3e\"");
804 auto json = tJsonProtocol(buf);
805 auto str = json.readString();
806 enforce(str == "ก 𝔾");
807 }
808
809 unittest {
810 // Thrown if low surrogate is missing.
811 import std.exception;
812 import thrift.transport.memory;
813
814 auto buf = new TMemoryBuffer(cast(ubyte[])"\"\\u0e01 \\ud835\"");
815 auto json = tJsonProtocol(buf);
816 assertThrown!TProtocolException(json.readString());
817 }
818
819 unittest {
820 // Thrown if high surrogate is missing.
821 import std.exception;
822 import thrift.transport.memory;
823
824 auto buf = new TMemoryBuffer(cast(ubyte[])"\"\\u0e01 \\udd3e\"");
825 auto json = tJsonProtocol(buf);
826 assertThrown!TProtocolException(json.readString());
827 }
828
829 unittest {
830 import thrift.internal.test.protocol;
831 testContainerSizeLimit!(TJsonProtocol!())();
832 testStringSizeLimit!(TJsonProtocol!())();
833 }
834
835 /**
836 * TProtocolFactory creating a TJsonProtocol instance for passed in
837 * transports.
838 *
839 * The optional Transports template tuple parameter can be used to specify
840 * one or more TTransport implementations to specifically instantiate
841 * TJsonProtocol for. If the actual transport types encountered at
842 * runtime match one of the transports in the list, a specialized protocol
843 * instance is created. Otherwise, a generic TTransport version is used.
844 */
845 class TJsonProtocolFactory(Transports...) if (
846 allSatisfy!(isTTransport, Transports)
847 ) : TProtocolFactory {
848 TProtocol getProtocol(TTransport trans) const {
849 foreach (Transport; TypeTuple!(Transports, TTransport)) {
850 auto concreteTrans = cast(Transport)trans;
851 if (concreteTrans) {
852 auto p = new TJsonProtocol!Transport(concreteTrans);
853 return p;
854 }
855 }
856 throw new TProtocolException(
857 "Passed null transport to TJsonProtocolFactoy.");
858 }
859 }
860
861 private {
862 immutable ubyte[1] OBJECT_BEGIN = '{';
863 immutable ubyte[1] OBJECT_END = '}';
864 immutable ubyte[1] ARRAY_BEGIN = '[';
865 immutable ubyte[1] ARRAY_END = ']';
866 immutable ubyte[1] NEWLINE = '\n';
867 immutable ubyte[1] PAIR_SEP = ':';
868 immutable ubyte[1] ELEM_SEP = ',';
869 immutable ubyte[1] BACKSLASH = '\\';
870 immutable ubyte[1] STRING_DELIMITER = '"';
871 immutable ubyte[1] ZERO_CHAR = '0';
872 immutable ubyte[1] ESCAPE_CHAR = 'u';
873 immutable ubyte[4] ESCAPE_PREFIX = cast(ubyte[4])r"\u00";
874
875 enum THRIFT_JSON_VERSION = 1;
876
877 immutable NAN_STRING = "NaN";
878 immutable INFINITY_STRING = "Infinity";
879 immutable NEG_INFINITY_STRING = "-Infinity";
880
881 string getNameFromTType(TType typeID) {
882 final switch (typeID) {
883 case TType.BOOL:
884 return "tf";
885 case TType.BYTE:
886 return "i8";
887 case TType.I16:
888 return "i16";
889 case TType.I32:
890 return "i32";
891 case TType.I64:
892 return "i64";
893 case TType.DOUBLE:
894 return "dbl";
895 case TType.STRING:
896 return "str";
897 case TType.STRUCT:
898 return "rec";
899 case TType.MAP:
900 return "map";
901 case TType.LIST:
902 return "lst";
903 case TType.SET:
904 return "set";
905 case TType.STOP: goto case;
906 case TType.VOID:
907 assert(false, "Invalid type passed.");
908 }
909 }
910
911 TType getTTypeFromName(string name) {
912 TType result;
913 if (name.length > 1) {
914 switch (name[0]) {
915 case 'd':
916 result = TType.DOUBLE;
917 break;
918 case 'i':
919 switch (name[1]) {
920 case '8':
921 result = TType.BYTE;
922 break;
923 case '1':
924 result = TType.I16;
925 break;
926 case '3':
927 result = TType.I32;
928 break;
929 case '6':
930 result = TType.I64;
931 break;
932 default:
933 // Do nothing.
934 }
935 break;
936 case 'l':
937 result = TType.LIST;
938 break;
939 case 'm':
940 result = TType.MAP;
941 break;
942 case 'r':
943 result = TType.STRUCT;
944 break;
945 case 's':
946 if (name[1] == 't') {
947 result = TType.STRING;
948 }
949 else if (name[1] == 'e') {
950 result = TType.SET;
951 }
952 break;
953 case 't':
954 result = TType.BOOL;
955 break;
956 default:
957 // Do nothing.
958 }
959 }
960 if (result == TType.STOP) {
961 throw new TProtocolException("Unrecognized type",
962 TProtocolException.Type.NOT_IMPLEMENTED);
963 }
964 return result;
965 }
966
967 // This table describes the handling for the first 0x30 characters
968 // 0 : escape using "\u00xx" notation
969 // 1 : just output index
970 // <other> : escape using "\<other>" notation
971 immutable ubyte[0x30] kJsonCharTable = [
972 // 0 1 2 3 4 5 6 7 8 9 A B C D E F
973 0, 0, 0, 0, 0, 0, 0, 0,'b','t','n', 0,'f','r', 0, 0, // 0
974 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
975 1, 1,'"', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2
976 ];
977
978 // This string's characters must match up with the elements in kEscapeCharVals.
979 // I don't have '/' on this list even though it appears on www.json.org --
980 // it is not in the RFC
981 immutable kEscapeChars = cast(ubyte[7]) `"\\bfnrt`;
982
983 // The elements of this array must match up with the sequence of characters in
984 // kEscapeChars
985 immutable ubyte[7] kEscapeCharVals = [
986 '"', '\\', '\b', '\f', '\n', '\r', '\t',
987 ];
988
989 // Return the integer value of a hex character ch.
990 // Throw a protocol exception if the character is not [0-9a-f].
991 ubyte hexVal(ubyte ch) {
992 if ((ch >= '0') && (ch <= '9')) {
993 return cast(ubyte)(ch - '0');
994 } else if ((ch >= 'a') && (ch <= 'f')) {
995 return cast(ubyte)(ch - 'a' + 10);
996 }
997 else {
998 throw new TProtocolException("Expected hex val ([0-9a-f]), got '" ~
999 ch ~ "'.", TProtocolException.Type.INVALID_DATA);
1000 }
1001 }
1002
1003 // Return the hex character representing the integer val. The value is masked
1004 // to make sure it is in the correct range.
1005 ubyte hexChar(ubyte val) {
1006 val &= 0x0F;
1007 if (val < 10) {
1008 return cast(ubyte)(val + '0');
1009 } else {
1010 return cast(ubyte)(val - 10 + 'a');
1011 }
1012 }
1013
1014 // Return true if the character ch is in [-+0-9.Ee]; false otherwise
1015 bool isJsonNumeric(ubyte ch) {
1016 switch (ch) {
1017 case '+':
1018 case '-':
1019 case '.':
1020 case '0':
1021 case '1':
1022 case '2':
1023 case '3':
1024 case '4':
1025 case '5':
1026 case '6':
1027 case '7':
1028 case '8':
1029 case '9':
1030 case 'E':
1031 case 'e':
1032 return true;
1033 default:
1034 return false;
1035 }
1036 }
1037 }