]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/jaegertracing/thrift/lib/d/src/thrift/protocol/json.d
import quincy beta 17.1.0
[ceph.git] / ceph / src / jaegertracing / thrift / lib / d / src / thrift / protocol / json.d
diff --git a/ceph/src/jaegertracing/thrift/lib/d/src/thrift/protocol/json.d b/ceph/src/jaegertracing/thrift/lib/d/src/thrift/protocol/json.d
deleted file mode 100644 (file)
index 56a71da..0000000
+++ /dev/null
@@ -1,1037 +0,0 @@
-/*
- * 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;
-    }
-  }
-}