+++ /dev/null
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-module thrift.protocol.json;
-
-import std.algorithm;
-import std.array;
-import std.base64;
-import std.conv;
-import std.range;
-import std.string : format;
-import std.traits : isIntegral;
-import std.typetuple : allSatisfy, TypeTuple;
-import std.utf : toUTF8;
-import thrift.protocol.base;
-import thrift.transport.base;
-
-alias Base64Impl!('+', '/', Base64.NoPadding) Base64NoPad;
-
-/**
- * Implementation of the Thrift JSON protocol.
- */
-final class TJsonProtocol(Transport = TTransport) if (
- isTTransport!Transport
-) : TProtocol {
- /**
- * Constructs a new instance.
- *
- * Params:
- * trans = The transport to use.
- * containerSizeLimit = If positive, the container size is limited to the
- * given number of items.
- * stringSizeLimit = If positive, the string length is limited to the
- * given number of bytes.
- */
- this(Transport trans, int containerSizeLimit = 0, int stringSizeLimit = 0) {
- trans_ = trans;
- this.containerSizeLimit = containerSizeLimit;
- this.stringSizeLimit = stringSizeLimit;
-
- context_ = new Context();
- reader_ = new LookaheadReader(trans);
- }
-
- Transport transport() @property {
- return trans_;
- }
-
- void reset() {
- destroy(contextStack_);
- context_ = new Context();
- reader_ = new LookaheadReader(trans_);
- }
-
- /**
- * If positive, limits the number of items of deserialized containers to the
- * given amount.
- *
- * This is useful to avoid allocating excessive amounts of memory when broken
- * data is received. If the limit is exceeded, a SIZE_LIMIT-type
- * TProtocolException is thrown.
- *
- * Defaults to zero (no limit).
- */
- int containerSizeLimit;
-
- /**
- * If positive, limits the length of deserialized strings/binary data to the
- * given number of bytes.
- *
- * This is useful to avoid allocating excessive amounts of memory when broken
- * data is received. If the limit is exceeded, a SIZE_LIMIT-type
- * TProtocolException is thrown.
- *
- * Note: For binary data, the limit applies to the length of the
- * Base64-encoded string data, not the resulting byte array.
- *
- * Defaults to zero (no limit).
- */
- int stringSizeLimit;
-
- /*
- * Writing methods.
- */
-
- void writeBool(bool b) {
- writeJsonInteger(b ? 1 : 0);
- }
-
- void writeByte(byte b) {
- writeJsonInteger(b);
- }
-
- void writeI16(short i16) {
- writeJsonInteger(i16);
- }
-
- void writeI32(int i32) {
- writeJsonInteger(i32);
- }
-
- void writeI64(long i64) {
- writeJsonInteger(i64);
- }
-
- void writeDouble(double dub) {
- context_.write(trans_);
-
- string value;
- if (dub is double.nan) {
- value = NAN_STRING;
- } else if (dub is double.infinity) {
- value = INFINITY_STRING;
- } else if (dub is -double.infinity) {
- value = NEG_INFINITY_STRING;
- }
-
- bool escapeNum = value !is null || context_.escapeNum;
-
- if (value is null) {
- /* precision is 17 */
- value = format("%.17g", dub);
- }
-
- if (escapeNum) trans_.write(STRING_DELIMITER);
- trans_.write(cast(ubyte[])value);
- if (escapeNum) trans_.write(STRING_DELIMITER);
- }
-
- void writeString(string str) {
- context_.write(trans_);
- trans_.write(STRING_DELIMITER);
- foreach (c; str) {
- writeJsonChar(c);
- }
- trans_.write(STRING_DELIMITER);
- }
-
- void writeBinary(ubyte[] buf) {
- context_.write(trans_);
-
- trans_.write(STRING_DELIMITER);
- ubyte[4] b;
- while (!buf.empty) {
- auto toWrite = take(buf, 3);
- Base64NoPad.encode(toWrite, b[]);
- trans_.write(b[0 .. toWrite.length + 1]);
- buf.popFrontN(toWrite.length);
- }
- trans_.write(STRING_DELIMITER);
- }
-
- void writeMessageBegin(TMessage msg) {
- writeJsonArrayBegin();
- writeJsonInteger(THRIFT_JSON_VERSION);
- writeString(msg.name);
- writeJsonInteger(cast(byte)msg.type);
- writeJsonInteger(msg.seqid);
- }
-
- void writeMessageEnd() {
- writeJsonArrayEnd();
- }
-
- void writeStructBegin(TStruct tstruct) {
- writeJsonObjectBegin();
- }
-
- void writeStructEnd() {
- writeJsonObjectEnd();
- }
-
- void writeFieldBegin(TField field) {
- writeJsonInteger(field.id);
- writeJsonObjectBegin();
- writeString(getNameFromTType(field.type));
- }
-
- void writeFieldEnd() {
- writeJsonObjectEnd();
- }
-
- void writeFieldStop() {}
-
- void writeListBegin(TList list) {
- writeJsonArrayBegin();
- writeString(getNameFromTType(list.elemType));
- writeJsonInteger(list.size);
- }
-
- void writeListEnd() {
- writeJsonArrayEnd();
- }
-
- void writeMapBegin(TMap map) {
- writeJsonArrayBegin();
- writeString(getNameFromTType(map.keyType));
- writeString(getNameFromTType(map.valueType));
- writeJsonInteger(map.size);
- writeJsonObjectBegin();
- }
-
- void writeMapEnd() {
- writeJsonObjectEnd();
- writeJsonArrayEnd();
- }
-
- void writeSetBegin(TSet set) {
- writeJsonArrayBegin();
- writeString(getNameFromTType(set.elemType));
- writeJsonInteger(set.size);
- }
-
- void writeSetEnd() {
- writeJsonArrayEnd();
- }
-
-
- /*
- * Reading methods.
- */
-
- bool readBool() {
- return readJsonInteger!byte() ? true : false;
- }
-
- byte readByte() {
- return readJsonInteger!byte();
- }
-
- short readI16() {
- return readJsonInteger!short();
- }
-
- int readI32() {
- return readJsonInteger!int();
- }
-
- long readI64() {
- return readJsonInteger!long();
- }
-
- double readDouble() {
- context_.read(reader_);
-
- if (reader_.peek() == STRING_DELIMITER) {
- auto str = readJsonString(true);
- if (str == NAN_STRING) {
- return double.nan;
- }
- if (str == INFINITY_STRING) {
- return double.infinity;
- }
- if (str == NEG_INFINITY_STRING) {
- return -double.infinity;
- }
-
- if (!context_.escapeNum) {
- // Throw exception -- we should not be in a string in this case
- throw new TProtocolException("Numeric data unexpectedly quoted",
- TProtocolException.Type.INVALID_DATA);
- }
- try {
- return to!double(str);
- } catch (ConvException e) {
- throw new TProtocolException(`Expected numeric value; got "` ~ str ~
- `".`, TProtocolException.Type.INVALID_DATA);
- }
- }
- else {
- if (context_.escapeNum) {
- // This will throw - we should have had a quote if escapeNum == true
- readJsonSyntaxChar(STRING_DELIMITER);
- }
-
- auto str = readJsonNumericChars();
- try {
- return to!double(str);
- } catch (ConvException e) {
- throw new TProtocolException(`Expected numeric value; got "` ~ str ~
- `".`, TProtocolException.Type.INVALID_DATA);
- }
- }
- }
-
- string readString() {
- return readJsonString(false);
- }
-
- ubyte[] readBinary() {
- return Base64NoPad.decode(readString());
- }
-
- TMessage readMessageBegin() {
- TMessage msg = void;
-
- readJsonArrayBegin();
-
- auto ver = readJsonInteger!short();
- if (ver != THRIFT_JSON_VERSION) {
- throw new TProtocolException("Message contained bad version.",
- TProtocolException.Type.BAD_VERSION);
- }
-
- msg.name = readString();
- msg.type = cast(TMessageType)readJsonInteger!byte();
- msg.seqid = readJsonInteger!short();
-
- return msg;
- }
-
- void readMessageEnd() {
- readJsonArrayEnd();
- }
-
- TStruct readStructBegin() {
- readJsonObjectBegin();
- return TStruct();
- }
-
- void readStructEnd() {
- readJsonObjectEnd();
- }
-
- TField readFieldBegin() {
- TField f = void;
- f.name = null;
-
- auto ch = reader_.peek();
- if (ch == OBJECT_END) {
- f.type = TType.STOP;
- } else {
- f.id = readJsonInteger!short();
- readJsonObjectBegin();
- f.type = getTTypeFromName(readString());
- }
-
- return f;
- }
-
- void readFieldEnd() {
- readJsonObjectEnd();
- }
-
- TList readListBegin() {
- readJsonArrayBegin();
- auto type = getTTypeFromName(readString());
- auto size = readContainerSize();
- return TList(type, size);
- }
-
- void readListEnd() {
- readJsonArrayEnd();
- }
-
- TMap readMapBegin() {
- readJsonArrayBegin();
- auto keyType = getTTypeFromName(readString());
- auto valueType = getTTypeFromName(readString());
- auto size = readContainerSize();
- readJsonObjectBegin();
- return TMap(keyType, valueType, size);
- }
-
- void readMapEnd() {
- readJsonObjectEnd();
- readJsonArrayEnd();
- }
-
- TSet readSetBegin() {
- readJsonArrayBegin();
- auto type = getTTypeFromName(readString());
- auto size = readContainerSize();
- return TSet(type, size);
- }
-
- void readSetEnd() {
- readJsonArrayEnd();
- }
-
-private:
- void pushContext(Context c) {
- contextStack_ ~= context_;
- context_ = c;
- }
-
- void popContext() {
- context_ = contextStack_.back;
- contextStack_.popBack();
- contextStack_.assumeSafeAppend();
- }
-
- /*
- * Writing functions
- */
-
- // Write the character ch as a Json escape sequence ("\u00xx")
- void writeJsonEscapeChar(ubyte ch) {
- trans_.write(ESCAPE_PREFIX);
- trans_.write(ESCAPE_PREFIX);
- auto outCh = hexChar(cast(ubyte)(ch >> 4));
- trans_.write((&outCh)[0 .. 1]);
- outCh = hexChar(ch);
- trans_.write((&outCh)[0 .. 1]);
- }
-
- // Write the character ch as part of a Json string, escaping as appropriate.
- void writeJsonChar(ubyte ch) {
- if (ch >= 0x30) {
- if (ch == '\\') { // Only special character >= 0x30 is '\'
- trans_.write(BACKSLASH);
- trans_.write(BACKSLASH);
- } else {
- trans_.write((&ch)[0 .. 1]);
- }
- }
- else {
- auto outCh = kJsonCharTable[ch];
- // Check if regular character, backslash escaped, or Json escaped
- if (outCh == 1) {
- trans_.write((&ch)[0 .. 1]);
- } else if (outCh > 1) {
- trans_.write(BACKSLASH);
- trans_.write((&outCh)[0 .. 1]);
- } else {
- writeJsonEscapeChar(ch);
- }
- }
- }
-
- // Convert the given integer type to a Json number, or a string
- // if the context requires it (eg: key in a map pair).
- void writeJsonInteger(T)(T num) if (isIntegral!T) {
- context_.write(trans_);
-
- auto escapeNum = context_.escapeNum();
- if (escapeNum) trans_.write(STRING_DELIMITER);
- trans_.write(cast(ubyte[])to!string(num));
- if (escapeNum) trans_.write(STRING_DELIMITER);
- }
-
- void writeJsonObjectBegin() {
- context_.write(trans_);
- trans_.write(OBJECT_BEGIN);
- pushContext(new PairContext());
- }
-
- void writeJsonObjectEnd() {
- popContext();
- trans_.write(OBJECT_END);
- }
-
- void writeJsonArrayBegin() {
- context_.write(trans_);
- trans_.write(ARRAY_BEGIN);
- pushContext(new ListContext());
- }
-
- void writeJsonArrayEnd() {
- popContext();
- trans_.write(ARRAY_END);
- }
-
- /*
- * Reading functions
- */
-
- int readContainerSize() {
- auto size = readJsonInteger!int();
- if (size < 0) {
- throw new TProtocolException(TProtocolException.Type.NEGATIVE_SIZE);
- } else if (containerSizeLimit > 0 && size > containerSizeLimit) {
- throw new TProtocolException(TProtocolException.Type.SIZE_LIMIT);
- }
- return size;
- }
-
- void readJsonSyntaxChar(ubyte[1] ch) {
- return readSyntaxChar(reader_, ch);
- }
-
- wchar readJsonEscapeChar() {
- auto a = reader_.read();
- auto b = reader_.read();
- auto c = reader_.read();
- auto d = reader_.read();
- return cast(ushort)(
- (hexVal(a[0]) << 12) + (hexVal(b[0]) << 8) +
- (hexVal(c[0]) << 4) + hexVal(d[0])
- );
- }
-
- string readJsonString(bool skipContext = false) {
- if (!skipContext) context_.read(reader_);
-
- readJsonSyntaxChar(STRING_DELIMITER);
- auto buffer = appender!string();
-
- wchar[] wchs;
- int bytesRead;
- while (true) {
- auto ch = reader_.read();
- if (ch == STRING_DELIMITER) {
- break;
- }
-
- ++bytesRead;
- if (stringSizeLimit > 0 && bytesRead > stringSizeLimit) {
- throw new TProtocolException(TProtocolException.Type.SIZE_LIMIT);
- }
-
- if (ch == BACKSLASH) {
- ch = reader_.read();
- if (ch == ESCAPE_CHAR) {
- auto wch = readJsonEscapeChar();
- if (wch >= 0xD800 && wch <= 0xDBFF) {
- wchs ~= wch;
- } else if (wch >= 0xDC00 && wch <= 0xDFFF && wchs.length == 0) {
- throw new TProtocolException("Missing UTF-16 high surrogate.",
- TProtocolException.Type.INVALID_DATA);
- } else {
- wchs ~= wch;
- buffer.put(wchs.toUTF8);
- wchs = [];
- }
- continue;
- } else {
- auto pos = countUntil(kEscapeChars[], ch[0]);
- if (pos == -1) {
- throw new TProtocolException("Expected control char, got '" ~
- cast(char)ch[0] ~ "'.", TProtocolException.Type.INVALID_DATA);
- }
- ch = kEscapeCharVals[pos];
- }
- }
- if (wchs.length != 0) {
- throw new TProtocolException("Missing UTF-16 low surrogate.",
- TProtocolException.Type.INVALID_DATA);
- }
- buffer.put(ch[0]);
- }
-
- if (wchs.length != 0) {
- throw new TProtocolException("Missing UTF-16 low surrogate.",
- TProtocolException.Type.INVALID_DATA);
- }
- return buffer.data;
- }
-
- // Reads a sequence of characters, stopping at the first one that is not
- // a valid Json numeric character.
- string readJsonNumericChars() {
- string str;
- while (true) {
- auto ch = reader_.peek();
- if (!isJsonNumeric(ch[0])) {
- break;
- }
- reader_.read();
- str ~= ch;
- }
- return str;
- }
-
- // Reads a sequence of characters and assembles them into a number,
- // returning them via num
- T readJsonInteger(T)() if (isIntegral!T) {
- context_.read(reader_);
- if (context_.escapeNum()) {
- readJsonSyntaxChar(STRING_DELIMITER);
- }
- auto str = readJsonNumericChars();
- T num;
- try {
- num = to!T(str);
- } catch (ConvException e) {
- throw new TProtocolException(`Expected numeric value, got "` ~ str ~ `".`,
- TProtocolException.Type.INVALID_DATA);
- }
- if (context_.escapeNum()) {
- readJsonSyntaxChar(STRING_DELIMITER);
- }
- return num;
- }
-
- void readJsonObjectBegin() {
- context_.read(reader_);
- readJsonSyntaxChar(OBJECT_BEGIN);
- pushContext(new PairContext());
- }
-
- void readJsonObjectEnd() {
- readJsonSyntaxChar(OBJECT_END);
- popContext();
- }
-
- void readJsonArrayBegin() {
- context_.read(reader_);
- readJsonSyntaxChar(ARRAY_BEGIN);
- pushContext(new ListContext());
- }
-
- void readJsonArrayEnd() {
- readJsonSyntaxChar(ARRAY_END);
- popContext();
- }
-
- static {
- final class LookaheadReader {
- this(Transport trans) {
- trans_ = trans;
- }
-
- ubyte[1] read() {
- if (hasData_) {
- hasData_ = false;
- } else {
- trans_.readAll(data_);
- }
- return data_;
- }
-
- ubyte[1] peek() {
- if (!hasData_) {
- trans_.readAll(data_);
- hasData_ = true;
- }
- return data_;
- }
-
- private:
- Transport trans_;
- bool hasData_;
- ubyte[1] data_;
- }
-
- /*
- * Class to serve as base Json context and as base class for other context
- * implementations
- */
- class Context {
- /**
- * Write context data to the transport. Default is to do nothing.
- */
- void write(Transport trans) {}
-
- /**
- * Read context data from the transport. Default is to do nothing.
- */
- void read(LookaheadReader reader) {}
-
- /**
- * Return true if numbers need to be escaped as strings in this context.
- * Default behavior is to return false.
- */
- bool escapeNum() @property {
- return false;
- }
- }
-
- // Context class for object member key-value pairs
- class PairContext : Context {
- this() {
- first_ = true;
- colon_ = true;
- }
-
- override void write(Transport trans) {
- if (first_) {
- first_ = false;
- colon_ = true;
- } else {
- trans.write(colon_ ? PAIR_SEP : ELEM_SEP);
- colon_ = !colon_;
- }
- }
-
- override void read(LookaheadReader reader) {
- if (first_) {
- first_ = false;
- colon_ = true;
- } else {
- auto ch = (colon_ ? PAIR_SEP : ELEM_SEP);
- colon_ = !colon_;
- return readSyntaxChar(reader, ch);
- }
- }
-
- // Numbers must be turned into strings if they are the key part of a pair
- override bool escapeNum() @property {
- return colon_;
- }
-
- private:
- bool first_;
- bool colon_;
- }
-
- class ListContext : Context {
- this() {
- first_ = true;
- }
-
- override void write(Transport trans) {
- if (first_) {
- first_ = false;
- } else {
- trans.write(ELEM_SEP);
- }
- }
-
- override void read(LookaheadReader reader) {
- if (first_) {
- first_ = false;
- } else {
- readSyntaxChar(reader, ELEM_SEP);
- }
- }
-
- private:
- bool first_;
- }
-
- // Read 1 character from the transport trans and verify that it is the
- // expected character ch.
- // Throw a protocol exception if it is not.
- void readSyntaxChar(LookaheadReader reader, ubyte[1] ch) {
- auto ch2 = reader.read();
- if (ch2 != ch) {
- throw new TProtocolException("Expected '" ~ cast(char)ch[0] ~ "', got '" ~
- cast(char)ch2[0] ~ "'.", TProtocolException.Type.INVALID_DATA);
- }
- }
- }
-
- // Probably need to implement a better stack at some point.
- Context[] contextStack_;
- Context context_;
-
- Transport trans_;
- LookaheadReader reader_;
-}
-
-/**
- * TJsonProtocol construction helper to avoid having to explicitly specify
- * the transport type, i.e. to allow the constructor being called using IFTI
- * (see $(LINK2 http://d.puremagic.com/issues/show_bug.cgi?id=6082, D Bugzilla
- * enhancement requet 6082)).
- */
-TJsonProtocol!Transport tJsonProtocol(Transport)(Transport trans,
- int containerSizeLimit = 0, int stringSizeLimit = 0
-) if (isTTransport!Transport) {
- return new TJsonProtocol!Transport(trans, containerSizeLimit, stringSizeLimit);
-}
-
-unittest {
- import std.exception;
- import thrift.transport.memory;
-
- // Check the message header format.
- auto buf = new TMemoryBuffer;
- auto json = tJsonProtocol(buf);
- json.writeMessageBegin(TMessage("foo", TMessageType.CALL, 0));
- json.writeMessageEnd();
-
- auto header = new ubyte[13];
- buf.readAll(header);
- enforce(cast(char[])header == `[1,"foo",1,0]`);
-}
-
-unittest {
- import std.exception;
- import thrift.transport.memory;
-
- // Check that short binary data is read correctly (the Thrift JSON format
- // does not include padding chars in the Base64 encoded data).
- auto buf = new TMemoryBuffer;
- auto json = tJsonProtocol(buf);
- json.writeBinary([1, 2]);
- json.reset();
- enforce(json.readBinary() == [1, 2]);
-}
-
-unittest {
- import std.exception;
- import thrift.transport.memory;
-
- auto buf = new TMemoryBuffer(cast(ubyte[])"\"\\u0e01 \\ud835\\udd3e\"");
- auto json = tJsonProtocol(buf);
- auto str = json.readString();
- enforce(str == "ก 𝔾");
-}
-
-unittest {
- // Thrown if low surrogate is missing.
- import std.exception;
- import thrift.transport.memory;
-
- auto buf = new TMemoryBuffer(cast(ubyte[])"\"\\u0e01 \\ud835\"");
- auto json = tJsonProtocol(buf);
- assertThrown!TProtocolException(json.readString());
-}
-
-unittest {
- // Thrown if high surrogate is missing.
- import std.exception;
- import thrift.transport.memory;
-
- auto buf = new TMemoryBuffer(cast(ubyte[])"\"\\u0e01 \\udd3e\"");
- auto json = tJsonProtocol(buf);
- assertThrown!TProtocolException(json.readString());
-}
-
-unittest {
- import thrift.internal.test.protocol;
- testContainerSizeLimit!(TJsonProtocol!())();
- testStringSizeLimit!(TJsonProtocol!())();
-}
-
-/**
- * TProtocolFactory creating a TJsonProtocol instance for passed in
- * transports.
- *
- * The optional Transports template tuple parameter can be used to specify
- * one or more TTransport implementations to specifically instantiate
- * TJsonProtocol for. If the actual transport types encountered at
- * runtime match one of the transports in the list, a specialized protocol
- * instance is created. Otherwise, a generic TTransport version is used.
- */
-class TJsonProtocolFactory(Transports...) if (
- allSatisfy!(isTTransport, Transports)
-) : TProtocolFactory {
- TProtocol getProtocol(TTransport trans) const {
- foreach (Transport; TypeTuple!(Transports, TTransport)) {
- auto concreteTrans = cast(Transport)trans;
- if (concreteTrans) {
- auto p = new TJsonProtocol!Transport(concreteTrans);
- return p;
- }
- }
- throw new TProtocolException(
- "Passed null transport to TJsonProtocolFactoy.");
- }
-}
-
-private {
- immutable ubyte[1] OBJECT_BEGIN = '{';
- immutable ubyte[1] OBJECT_END = '}';
- immutable ubyte[1] ARRAY_BEGIN = '[';
- immutable ubyte[1] ARRAY_END = ']';
- immutable ubyte[1] NEWLINE = '\n';
- immutable ubyte[1] PAIR_SEP = ':';
- immutable ubyte[1] ELEM_SEP = ',';
- immutable ubyte[1] BACKSLASH = '\\';
- immutable ubyte[1] STRING_DELIMITER = '"';
- immutable ubyte[1] ZERO_CHAR = '0';
- immutable ubyte[1] ESCAPE_CHAR = 'u';
- immutable ubyte[4] ESCAPE_PREFIX = cast(ubyte[4])r"\u00";
-
- enum THRIFT_JSON_VERSION = 1;
-
- immutable NAN_STRING = "NaN";
- immutable INFINITY_STRING = "Infinity";
- immutable NEG_INFINITY_STRING = "-Infinity";
-
- string getNameFromTType(TType typeID) {
- final switch (typeID) {
- case TType.BOOL:
- return "tf";
- case TType.BYTE:
- return "i8";
- case TType.I16:
- return "i16";
- case TType.I32:
- return "i32";
- case TType.I64:
- return "i64";
- case TType.DOUBLE:
- return "dbl";
- case TType.STRING:
- return "str";
- case TType.STRUCT:
- return "rec";
- case TType.MAP:
- return "map";
- case TType.LIST:
- return "lst";
- case TType.SET:
- return "set";
- case TType.STOP: goto case;
- case TType.VOID:
- assert(false, "Invalid type passed.");
- }
- }
-
- TType getTTypeFromName(string name) {
- TType result;
- if (name.length > 1) {
- switch (name[0]) {
- case 'd':
- result = TType.DOUBLE;
- break;
- case 'i':
- switch (name[1]) {
- case '8':
- result = TType.BYTE;
- break;
- case '1':
- result = TType.I16;
- break;
- case '3':
- result = TType.I32;
- break;
- case '6':
- result = TType.I64;
- break;
- default:
- // Do nothing.
- }
- break;
- case 'l':
- result = TType.LIST;
- break;
- case 'm':
- result = TType.MAP;
- break;
- case 'r':
- result = TType.STRUCT;
- break;
- case 's':
- if (name[1] == 't') {
- result = TType.STRING;
- }
- else if (name[1] == 'e') {
- result = TType.SET;
- }
- break;
- case 't':
- result = TType.BOOL;
- break;
- default:
- // Do nothing.
- }
- }
- if (result == TType.STOP) {
- throw new TProtocolException("Unrecognized type",
- TProtocolException.Type.NOT_IMPLEMENTED);
- }
- return result;
- }
-
- // This table describes the handling for the first 0x30 characters
- // 0 : escape using "\u00xx" notation
- // 1 : just output index
- // <other> : escape using "\<other>" notation
- immutable ubyte[0x30] kJsonCharTable = [
- // 0 1 2 3 4 5 6 7 8 9 A B C D E F
- 0, 0, 0, 0, 0, 0, 0, 0,'b','t','n', 0,'f','r', 0, 0, // 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
- 1, 1,'"', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2
- ];
-
- // This string's characters must match up with the elements in kEscapeCharVals.
- // I don't have '/' on this list even though it appears on www.json.org --
- // it is not in the RFC
- immutable kEscapeChars = cast(ubyte[7]) `"\\bfnrt`;
-
- // The elements of this array must match up with the sequence of characters in
- // kEscapeChars
- immutable ubyte[7] kEscapeCharVals = [
- '"', '\\', '\b', '\f', '\n', '\r', '\t',
- ];
-
- // Return the integer value of a hex character ch.
- // Throw a protocol exception if the character is not [0-9a-f].
- ubyte hexVal(ubyte ch) {
- if ((ch >= '0') && (ch <= '9')) {
- return cast(ubyte)(ch - '0');
- } else if ((ch >= 'a') && (ch <= 'f')) {
- return cast(ubyte)(ch - 'a' + 10);
- }
- else {
- throw new TProtocolException("Expected hex val ([0-9a-f]), got '" ~
- ch ~ "'.", TProtocolException.Type.INVALID_DATA);
- }
- }
-
- // Return the hex character representing the integer val. The value is masked
- // to make sure it is in the correct range.
- ubyte hexChar(ubyte val) {
- val &= 0x0F;
- if (val < 10) {
- return cast(ubyte)(val + '0');
- } else {
- return cast(ubyte)(val - 10 + 'a');
- }
- }
-
- // Return true if the character ch is in [-+0-9.Ee]; false otherwise
- bool isJsonNumeric(ubyte ch) {
- switch (ch) {
- case '+':
- case '-':
- case '.':
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- case 'E':
- case 'e':
- return true;
- default:
- return false;
- }
- }
-}