You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@thrift.apache.org by jf...@apache.org on 2012/03/22 22:49:13 UTC

svn commit: r1304085 [5/10] - in /thrift/trunk: ./ aclocal/ compiler/cpp/ compiler/cpp/src/generate/ lib/ lib/d/ lib/d/src/ lib/d/src/thrift/ lib/d/src/thrift/async/ lib/d/src/thrift/codegen/ lib/d/src/thrift/internal/ lib/d/src/thrift/internal/test/ l...

Added: thrift/trunk/lib/d/src/thrift/protocol/base.d
URL: http://svn.apache.org/viewvc/thrift/trunk/lib/d/src/thrift/protocol/base.d?rev=1304085&view=auto
==============================================================================
--- thrift/trunk/lib/d/src/thrift/protocol/base.d (added)
+++ thrift/trunk/lib/d/src/thrift/protocol/base.d Thu Mar 22 21:49:10 2012
@@ -0,0 +1,436 @@
+/*
+ * 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.
+ */
+
+/**
+ * Defines the basic interface for a Thrift protocol and associated exception
+ * types.
+ *
+ * Most parts of the protocol API are typically not used in client code, as
+ * the actual serialization code is generated by thrift.codegen.* – the only
+ * interesting thing usually is that there are protocols which can be created
+ * from transports and passed around.
+ */
+module thrift.protocol.base;
+
+import thrift.base;
+import thrift.transport.base;
+
+/**
+ * The field types Thrift protocols support.
+ */
+enum TType : byte {
+  STOP   = 0, /// Used to mark the end of a sequence of fields.
+  VOID   = 1, ///
+  BOOL   = 2, ///
+  BYTE   = 3, ///
+  DOUBLE = 4, ///
+  I16    = 6, ///
+  I32    = 8, ///
+  I64    = 10, ///
+  STRING = 11, ///
+  STRUCT = 12, ///
+  MAP    = 13, ///
+  SET    = 14, ///
+  LIST   = 15 ///
+}
+
+/**
+ * Types of Thrift RPC messages.
+ */
+enum TMessageType : byte {
+  CALL = 1, /// Call of a normal, two-way RPC method.
+  REPLY = 2, /// Reply to a normal method call.
+  EXCEPTION = 3, /// Reply to a method call if target raised a TApplicationException.
+  ONEWAY = 4 /// Call of a one-way RPC method which is not followed by a reply.
+}
+
+/**
+ * Descriptions of Thrift entities.
+ */
+struct TField {
+  string name;
+  TType type;
+  short id;
+}
+
+/// ditto
+struct TList {
+  TType elemType;
+  size_t size;
+}
+
+/// ditto
+struct TMap {
+  TType keyType;
+  TType valueType;
+  size_t size;
+}
+
+/// ditto
+struct TMessage {
+  string name;
+  TMessageType type;
+  int seqid;
+}
+
+/// ditto
+struct TSet {
+  TType elemType;
+  size_t size;
+}
+
+/// ditto
+struct TStruct {
+  string name;
+}
+
+/**
+ * Interface for a Thrift protocol implementation. Essentially, it defines
+ * a way of reading and writing all the base types, plus a mechanism for
+ * writing out structs with indexed fields.
+ *
+ * TProtocol objects should not be shared across multiple encoding contexts,
+ * as they may need to maintain internal state in some protocols (e.g. JSON).
+ * Note that is is acceptable for the TProtocol module to do its own internal
+ * buffered reads/writes to the underlying TTransport where appropriate (i.e.
+ * when parsing an input XML stream, reading could be batched rather than
+ * looking ahead character by character for a close tag).
+ */
+interface TProtocol {
+  /// The underlying transport used by the protocol.
+  TTransport transport() @property;
+
+  /*
+   * Writing methods.
+   */
+
+  void writeBool(bool b); ///
+  void writeByte(byte b); ///
+  void writeI16(short i16); ///
+  void writeI32(int i32); ///
+  void writeI64(long i64); ///
+  void writeDouble(double dub); ///
+  void writeString(string str); ///
+  void writeBinary(ubyte[] buf); ///
+
+  void writeMessageBegin(TMessage message); ///
+  void writeMessageEnd(); ///
+  void writeStructBegin(TStruct tstruct); ///
+  void writeStructEnd(); ///
+  void writeFieldBegin(TField field); ///
+  void writeFieldEnd(); ///
+  void writeFieldStop(); ///
+  void writeListBegin(TList list); ///
+  void writeListEnd(); ///
+  void writeMapBegin(TMap map); ///
+  void writeMapEnd(); ///
+  void writeSetBegin(TSet set); ///
+  void writeSetEnd(); ///
+
+  /*
+   * Reading methods.
+   */
+
+  bool readBool(); ///
+  byte readByte(); ///
+  short readI16(); ///
+  int readI32(); ///
+  long readI64(); ///
+  double readDouble(); ///
+  string readString(); ///
+  ubyte[] readBinary(); ///
+
+  TMessage readMessageBegin(); ///
+  void readMessageEnd(); ///
+  TStruct readStructBegin(); ///
+  void readStructEnd(); ///
+  TField readFieldBegin(); ///
+  void readFieldEnd(); ///
+  TList readListBegin(); ///
+  void readListEnd(); ///
+  TMap readMapBegin(); ///
+  void readMapEnd(); ///
+  TSet readSetBegin(); ///
+  void readSetEnd(); ///
+
+  /**
+   * Reset any internal state back to a blank slate, if the protocol is
+   * stateful.
+   */
+  void reset();
+}
+
+/**
+ * true if T is a TProtocol.
+ */
+template isTProtocol(T) {
+  enum isTProtocol = is(T : TProtocol);
+}
+
+unittest {
+  static assert(isTProtocol!TProtocol);
+  static assert(!isTProtocol!void);
+}
+
+/**
+ * Creates a protocol operating on a given transport.
+ */
+interface TProtocolFactory {
+  ///
+  TProtocol getProtocol(TTransport trans);
+}
+
+/**
+ * A protocol-level exception.
+ */
+class TProtocolException : TException {
+  /// The possible exception types.
+  enum Type {
+    UNKNOWN, ///
+    INVALID_DATA, ///
+    NEGATIVE_SIZE, ///
+    SIZE_LIMIT, ///
+    BAD_VERSION, ///
+    NOT_IMPLEMENTED ///
+  }
+
+  ///
+  this(Type type, string file = __FILE__, size_t line = __LINE__, Throwable next = null) {
+    static string msgForType(Type type) {
+      switch (type) {
+        case Type.UNKNOWN: return "Unknown protocol exception";
+        case Type.INVALID_DATA: return "Invalid data";
+        case Type.NEGATIVE_SIZE: return "Negative size";
+        case Type.SIZE_LIMIT: return "Exceeded size limit";
+        case Type.BAD_VERSION: return "Invalid version";
+        case Type.NOT_IMPLEMENTED: return "Not implemented";
+        default: return "(Invalid exception type)";
+      }
+    }
+    this(msgForType(type), type, file, line, next);
+  }
+
+  ///
+  this(string msg, string file = __FILE__, size_t line = __LINE__,
+    Throwable next = null)
+  {
+    this(msg, Type.UNKNOWN, file, line, next);
+  }
+
+  ///
+  this(string msg, Type type, string file = __FILE__, size_t line = __LINE__,
+    Throwable next = null)
+  {
+    super(msg, file, line, next);
+    type_ = type;
+  }
+
+  ///
+  Type type() const @property {
+    return type_;
+  }
+
+protected:
+  Type type_;
+}
+
+/**
+ * Skips a field of the given type on the protocol.
+ *
+ * The main purpose of skip() is to allow treating struct and cotainer types,
+ * (where multiple primitive types have to be skipped) the same as scalar types
+ * in generated code.
+ */
+void skip(Protocol)(Protocol prot, TType type) if (is(Protocol : TProtocol)) {
+  final switch (type) {
+    case TType.BOOL:
+      prot.readBool();
+      break;
+
+    case TType.BYTE:
+      prot.readByte();
+      break;
+
+    case TType.I16:
+      prot.readI16();
+      break;
+
+    case TType.I32:
+      prot.readI32();
+      break;
+
+    case TType.I64:
+      prot.readI64();
+      break;
+
+    case TType.DOUBLE:
+      prot.readDouble();
+      break;
+
+    case TType.STRING:
+      prot.readBinary();
+      break;
+
+    case TType.STRUCT:
+      prot.readStructBegin();
+      while (true) {
+        auto f = prot.readFieldBegin();
+        if (f.type == TType.STOP) break;
+        skip(prot, f.type);
+        prot.readFieldEnd();
+      }
+      prot.readStructEnd();
+      break;
+
+    case TType.LIST:
+      auto l = prot.readListBegin();
+      foreach (i; 0 .. l.size) {
+        skip(prot, l.elemType);
+      }
+      prot.readListEnd();
+      break;
+
+    case TType.MAP:
+      auto m = prot.readMapBegin();
+      foreach (i; 0 .. m.size) {
+        skip(prot, m.keyType);
+        skip(prot, m.valueType);
+      }
+      prot.readMapEnd();
+      break;
+
+    case TType.SET:
+      auto s = prot.readSetBegin();
+      foreach (i; 0 .. s.size) {
+        skip(prot, s.elemType);
+      }
+      prot.readSetEnd();
+      break;
+  }
+}
+
+/**
+ * Application-level exception.
+ *
+ * It is thrown if an RPC call went wrong on the application layer, e.g. if
+ * the receiver does not know the method name requested or a method invoked by
+ * the service processor throws an exception not part of the Thrift API.
+ */
+class TApplicationException : TException {
+  /// The possible exception types.
+  enum Type {
+    UNKNOWN = 0, ///
+    UNKNOWN_METHOD = 1, ///
+    INVALID_MESSAGE_TYPE = 2, ///
+    WRONG_METHOD_NAME = 3, ///
+    BAD_SEQUENCE_ID = 4, ///
+    MISSING_RESULT = 5, ///
+    INTERNAL_ERROR = 6, ///
+    PROTOCOL_ERROR = 7 ///
+  }
+
+  ///
+  this(Type type, string file = __FILE__, size_t line = __LINE__, Throwable next = null) {
+    static string msgForType(Type type) {
+      switch (type) {
+        case Type.UNKNOWN: return "Unknown application exception";
+        case Type.UNKNOWN_METHOD: return "Unknown method";
+        case Type.INVALID_MESSAGE_TYPE: return "Invalid message type";
+        case Type.WRONG_METHOD_NAME: return "Wrong method name";
+        case Type.BAD_SEQUENCE_ID: return "Bad sequence identifier";
+        case Type.MISSING_RESULT: return "Missing result";
+        default: return "(Invalid exception type)";
+      }
+    }
+    this(msgForType(type), type, file, line, next);
+  }
+
+  ///
+  this(string msg, string file = __FILE__, size_t line = __LINE__,
+    Throwable next = null)
+  {
+    this(msg, Type.UNKNOWN, file, line, next);
+  }
+
+  ///
+  this(string msg, Type type, string file = __FILE__, size_t line = __LINE__,
+    Throwable next = null)
+  {
+    super(msg, file, line, next);
+    type_ = type;
+  }
+
+  ///
+  Type type() @property const {
+    return type_;
+  }
+
+  // TODO: Replace hand-written read()/write() with thrift.codegen templates.
+
+  ///
+  void read(TProtocol iprot) {
+    iprot.readStructBegin();
+    while (true) {
+      auto f = iprot.readFieldBegin();
+      if (f.type == TType.STOP) break;
+
+      switch (f.id) {
+        case 1:
+          if (f.type == TType.STRING) {
+            msg = iprot.readString();
+          } else {
+            skip(iprot, f.type);
+          }
+          break;
+        case 2:
+          if (f.type == TType.I32) {
+            type_ = cast(Type)iprot.readI32();
+          } else {
+            skip(iprot, f.type);
+          }
+          break;
+        default:
+          skip(iprot, f.type);
+          break;
+      }
+    }
+    iprot.readStructEnd();
+  }
+
+  ///
+  void write(TProtocol oprot) const {
+    oprot.writeStructBegin(TStruct("TApplicationException"));
+
+    if (msg != null) {
+      oprot.writeFieldBegin(TField("message", TType.STRING, 1));
+      oprot.writeString(msg);
+      oprot.writeFieldEnd();
+    }
+
+    oprot.writeFieldBegin(TField("type", TType.I32, 2));
+    oprot.writeI32(type_);
+    oprot.writeFieldEnd();
+
+    oprot.writeFieldStop();
+    oprot.writeStructEnd();
+  }
+
+private:
+  Type type_;
+}

Added: thrift/trunk/lib/d/src/thrift/protocol/binary.d
URL: http://svn.apache.org/viewvc/thrift/trunk/lib/d/src/thrift/protocol/binary.d?rev=1304085&view=auto
==============================================================================
--- thrift/trunk/lib/d/src/thrift/protocol/binary.d (added)
+++ thrift/trunk/lib/d/src/thrift/protocol/binary.d Thu Mar 22 21:49:10 2012
@@ -0,0 +1,414 @@
+/*
+ * 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.binary;
+
+import std.array : uninitializedArray;
+import std.typetuple : allSatisfy, TypeTuple;
+import thrift.protocol.base;
+import thrift.transport.base;
+import thrift.internal.endian;
+
+/**
+ * TProtocol implementation of the Binary Thrift protocol.
+ */
+final class TBinaryProtocol(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.
+   *   strictRead = If false, old peers which do not include the protocol
+   *     version are tolerated.
+   *   strictWrite = Whether to include the protocol version in the header.
+   */
+  this(Transport trans, int containerSizeLimit = 0, int stringSizeLimit = 0,
+    bool strictRead = false, bool strictWrite = true
+  ) {
+    trans_ = trans;
+    this.containerSizeLimit = containerSizeLimit;
+    this.stringSizeLimit = stringSizeLimit;
+    this.strictRead = strictRead;
+    this.strictWrite = strictWrite;
+  }
+
+  Transport transport() @property {
+    return trans_;
+  }
+
+  void reset() {}
+
+  /**
+   * If false, old peers which do not include the protocol version in the
+   * message header are tolerated.
+   *
+   * Defaults to false.
+   */
+  bool strictRead;
+
+  /**
+   * Whether to include the protocol version in the message header (older
+   * versions didn't).
+   *
+   * Defaults to true.
+   */
+  bool strictWrite;
+
+  /**
+   * 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.
+   *
+   * Defaults to zero (no limit).
+   */
+  int stringSizeLimit;
+
+  /*
+   * Writing methods.
+   */
+
+  void writeBool(bool b) {
+    writeByte(b ? 1 : 0);
+  }
+
+  void writeByte(byte b) {
+    trans_.write((cast(ubyte*)&b)[0 .. 1]);
+  }
+
+  void writeI16(short i16) {
+    short net = hostToNet(i16);
+    trans_.write((cast(ubyte*)&net)[0 .. 2]);
+  }
+
+  void writeI32(int i32) {
+    int net = hostToNet(i32);
+    trans_.write((cast(ubyte*)&net)[0 .. 4]);
+  }
+
+  void writeI64(long i64) {
+    long net = hostToNet(i64);
+    trans_.write((cast(ubyte*)&net)[0 .. 8]);
+  }
+
+  void writeDouble(double dub) {
+    static assert(double.sizeof == ulong.sizeof);
+    auto bits = hostToNet(*cast(ulong*)(&dub));
+    trans_.write((cast(ubyte*)&bits)[0 .. 8]);
+  }
+
+  void writeString(string str) {
+    writeBinary(cast(ubyte[])str);
+  }
+
+  void writeBinary(ubyte[] buf) {
+    assert(buf.length <= int.max);
+    writeI32(cast(int)buf.length);
+    trans_.write(buf);
+  }
+
+  void writeMessageBegin(TMessage message) {
+    if (strictWrite) {
+      int versn = VERSION_1 | message.type;
+      writeI32(versn);
+      writeString(message.name);
+      writeI32(message.seqid);
+    } else {
+      writeString(message.name);
+      writeByte(message.type);
+      writeI32(message.seqid);
+    }
+  }
+  void writeMessageEnd() {}
+
+  void writeStructBegin(TStruct tstruct) {}
+  void writeStructEnd() {}
+
+  void writeFieldBegin(TField field) {
+    writeByte(field.type);
+    writeI16(field.id);
+  }
+  void writeFieldEnd() {}
+
+  void writeFieldStop() {
+    writeByte(TType.STOP);
+  }
+
+  void writeListBegin(TList list) {
+    assert(list.size <= int.max);
+    writeByte(list.elemType);
+    writeI32(cast(int)list.size);
+  }
+  void writeListEnd() {}
+
+  void writeMapBegin(TMap map) {
+    assert(map.size <= int.max);
+    writeByte(map.keyType);
+    writeByte(map.valueType);
+    writeI32(cast(int)map.size);
+  }
+  void writeMapEnd() {}
+
+  void writeSetBegin(TSet set) {
+    assert(set.size <= int.max);
+    writeByte(set.elemType);
+    writeI32(cast(int)set.size);
+  }
+  void writeSetEnd() {}
+
+
+  /*
+   * Reading methods.
+   */
+
+  bool readBool() {
+    return readByte() != 0;
+  }
+
+  byte readByte() {
+    ubyte[1] b = void;
+    trans_.readAll(b);
+    return cast(byte)b[0];
+  }
+
+  short readI16() {
+    IntBuf!short b = void;
+    trans_.readAll(b.bytes);
+    return netToHost(b.value);
+  }
+
+  int readI32() {
+    IntBuf!int b = void;
+    trans_.readAll(b.bytes);
+    return netToHost(b.value);
+  }
+
+  long readI64() {
+    IntBuf!long b = void;
+    trans_.readAll(b.bytes);
+    return netToHost(b.value);
+  }
+
+  double readDouble() {
+    IntBuf!long b = void;
+    trans_.readAll(b.bytes);
+    b.value = netToHost(b.value);
+    return *cast(double*)(&b.value);
+  }
+
+  string readString() {
+    return cast(string)readBinary();
+  }
+
+  ubyte[] readBinary() {
+    return readBinaryBody(readSize(stringSizeLimit));
+  }
+
+  TMessage readMessageBegin() {
+    TMessage msg = void;
+
+    int size = readI32();
+    if (size < 0) {
+      int versn = size & VERSION_MASK;
+      if (versn != VERSION_1) {
+        throw new TProtocolException("Bad protocol version.",
+          TProtocolException.Type.BAD_VERSION);
+      }
+
+      msg.type = cast(TMessageType)(size & MESSAGE_TYPE_MASK);
+      msg.name = readString();
+      msg.seqid = readI32();
+    } else {
+      if (strictRead) {
+        throw new TProtocolException(
+          "Protocol version missing, old client?",
+          TProtocolException.Type.BAD_VERSION);
+      } else {
+        if (size < 0) {
+          throw new TProtocolException(TProtocolException.Type.NEGATIVE_SIZE);
+        }
+        msg.name = cast(string)readBinaryBody(size);
+        msg.type = cast(TMessageType)(readByte());
+        msg.seqid = readI32();
+      }
+    }
+
+    return msg;
+  }
+  void readMessageEnd() {}
+
+  TStruct readStructBegin() {
+    return TStruct();
+  }
+  void readStructEnd() {}
+
+  TField readFieldBegin() {
+    TField f = void;
+    f.name = null;
+    f.type = cast(TType)readByte();
+    if (f.type == TType.STOP) return f;
+    f.id = readI16();
+    return f;
+  }
+  void readFieldEnd() {}
+
+  TList readListBegin() {
+    return TList(cast(TType)readByte(), readSize(containerSizeLimit));
+  }
+  void readListEnd() {}
+
+  TMap readMapBegin() {
+    return TMap(cast(TType)readByte(), cast(TType)readByte(),
+      readSize(containerSizeLimit));
+  }
+  void readMapEnd() {}
+
+  TSet readSetBegin() {
+    return TSet(cast(TType)readByte(), readSize(containerSizeLimit));
+  }
+  void readSetEnd() {}
+
+private:
+  ubyte[] readBinaryBody(int size) {
+    if (size == 0) {
+      return null;
+    }
+
+    auto buf = uninitializedArray!(ubyte[])(size);
+    trans_.readAll(buf);
+    return buf;
+  }
+
+  int readSize(int limit) {
+    auto size = readI32();
+    if (size < 0) {
+      throw new TProtocolException(TProtocolException.Type.NEGATIVE_SIZE);
+    } else if (limit > 0 && size > limit) {
+      throw new TProtocolException(TProtocolException.Type.SIZE_LIMIT);
+    }
+    return size;
+  }
+
+  enum MESSAGE_TYPE_MASK = 0x000000ff;
+  enum VERSION_MASK = 0xffff0000;
+  enum VERSION_1 = 0x80010000;
+
+  Transport trans_;
+}
+
+/**
+ * TBinaryProtocol 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)).
+ */
+TBinaryProtocol!Transport tBinaryProtocol(Transport)(Transport trans,
+  int containerSizeLimit = 0, int stringSizeLimit = 0,
+  bool strictRead = false, bool strictWrite = true
+) if (isTTransport!Transport) {
+  return new TBinaryProtocol!Transport(trans, containerSizeLimit,
+    stringSizeLimit, strictRead, strictWrite);
+}
+
+unittest {
+  import std.exception;
+  import thrift.transport.memory;
+
+  // Check the message header format.
+  auto buf = new TMemoryBuffer;
+  auto binary = tBinaryProtocol(buf);
+  binary.writeMessageBegin(TMessage("foo", TMessageType.CALL, 0));
+
+  auto header = new ubyte[15];
+  buf.readAll(header);
+  enforce(header == [
+    128, 1, 0, 1, // Version 1, TMessageType.CALL
+    0, 0, 0, 3, // Method name length
+    102, 111, 111, // Method name ("foo")
+    0, 0, 0, 0, // Sequence id
+  ]);
+}
+
+unittest {
+  import thrift.internal.test.protocol;
+  testContainerSizeLimit!(TBinaryProtocol!())();
+  testStringSizeLimit!(TBinaryProtocol!())();
+}
+
+/**
+ * TProtocolFactory creating a TBinaryProtocol instance for passed in
+ * transports.
+ *
+ * The optional Transports template tuple parameter can be used to specify
+ * one or more TTransport implementations to specifically instantiate
+ * TBinaryProtocol 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 TBinaryProtocolFactory(Transports...) if (
+  allSatisfy!(isTTransport, Transports)
+) : TProtocolFactory {
+  ///
+  this (int containerSizeLimit = 0, int stringSizeLimit = 0,
+    bool strictRead = false, bool strictWrite = true
+  ) {
+    strictRead_ = strictRead;
+    strictWrite_ = strictWrite;
+    containerSizeLimit_ = containerSizeLimit;
+    stringSizeLimit_ = stringSizeLimit;
+  }
+
+  TProtocol getProtocol(TTransport trans) const {
+    foreach (Transport; TypeTuple!(Transports, TTransport)) {
+      auto concreteTrans = cast(Transport)trans;
+      if (concreteTrans) {
+        return new TBinaryProtocol!Transport(concreteTrans,
+          containerSizeLimit_, stringSizeLimit_, strictRead_, strictWrite_);
+      }
+    }
+    throw new TProtocolException(
+      "Passed null transport to TBinaryProtocolFactoy.");
+  }
+
+protected:
+  bool strictRead_;
+  bool strictWrite_;
+  int containerSizeLimit_;
+  int stringSizeLimit_;
+}

Added: thrift/trunk/lib/d/src/thrift/protocol/compact.d
URL: http://svn.apache.org/viewvc/thrift/trunk/lib/d/src/thrift/protocol/compact.d?rev=1304085&view=auto
==============================================================================
--- thrift/trunk/lib/d/src/thrift/protocol/compact.d (added)
+++ thrift/trunk/lib/d/src/thrift/protocol/compact.d Thu Mar 22 21:49:10 2012
@@ -0,0 +1,695 @@
+/*
+ * 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.compact;
+
+import std.array : uninitializedArray;
+import std.typetuple : allSatisfy, TypeTuple;
+import thrift.protocol.base;
+import thrift.transport.base;
+import thrift.internal.endian;
+
+/**
+ * D implementation of the Compact protocol.
+ *
+ * See THRIFT-110 for a protocol description. This implementation is based on
+ * the C++ one.
+ */
+final class TCompactProtocol(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;
+  }
+
+  Transport transport() @property {
+    return trans_;
+  }
+
+  void reset() {
+    lastFieldId_ = 0;
+    fieldIdStack_ = null;
+    booleanField_ = TField.init;
+    hasBoolValue_ = false;
+  }
+
+  /**
+   * 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.
+   *
+   * Defaults to zero (no limit).
+   */
+  int stringSizeLimit;
+
+  /*
+   * Writing methods.
+   */
+
+  void writeBool(bool b) {
+    if (booleanField_.name !is null) {
+      // we haven't written the field header yet
+      writeFieldBeginInternal(booleanField_,
+        b ? CType.BOOLEAN_TRUE : CType.BOOLEAN_FALSE);
+      booleanField_.name = null;
+    } else {
+      // we're not part of a field, so just write the value
+      writeByte(b ? CType.BOOLEAN_TRUE : CType.BOOLEAN_FALSE);
+    }
+  }
+
+  void writeByte(byte b) {
+    trans_.write((cast(ubyte*)&b)[0..1]);
+  }
+
+  void writeI16(short i16) {
+    writeVarint32(i32ToZigzag(i16));
+  }
+
+  void writeI32(int i32) {
+    writeVarint32(i32ToZigzag(i32));
+  }
+
+  void writeI64(long i64) {
+    writeVarint64(i64ToZigzag(i64));
+  }
+
+  void writeDouble(double dub) {
+    ulong bits = hostToLe(*cast(ulong*)(&dub));
+    trans_.write((cast(ubyte*)&bits)[0 .. 8]);
+  }
+
+  void writeString(string str) {
+    writeBinary(cast(ubyte[])str);
+  }
+
+  void writeBinary(ubyte[] buf) {
+    assert(buf.length <= int.max);
+    writeVarint32(cast(int)buf.length);
+    trans_.write(buf);
+  }
+
+  void writeMessageBegin(TMessage msg) {
+    writeByte(cast(byte)PROTOCOL_ID);
+    writeByte((VERSION_N & VERSION_MASK) |
+      ((cast(int)msg.type << TYPE_SHIFT_AMOUNT) & TYPE_MASK));
+    writeVarint32(msg.seqid);
+    writeString(msg.name);
+  }
+  void writeMessageEnd() {}
+
+  void writeStructBegin(TStruct tstruct) {
+    fieldIdStack_ ~= lastFieldId_;
+    lastFieldId_ = 0;
+  }
+
+  void writeStructEnd() {
+    lastFieldId_ = fieldIdStack_[$ - 1];
+    fieldIdStack_ = fieldIdStack_[0 .. $ - 1];
+    fieldIdStack_.assumeSafeAppend();
+  }
+
+  void writeFieldBegin(TField field) {
+    if (field.type == TType.BOOL) {
+      booleanField_.name = field.name;
+      booleanField_.type = field.type;
+      booleanField_.id = field.id;
+    } else {
+      return writeFieldBeginInternal(field);
+    }
+  }
+  void writeFieldEnd() {}
+
+  void writeFieldStop() {
+    writeByte(TType.STOP);
+  }
+
+  void writeListBegin(TList list) {
+    writeCollectionBegin(list.elemType, list.size);
+  }
+  void writeListEnd() {}
+
+  void writeMapBegin(TMap map) {
+    if (map.size == 0) {
+      writeByte(0);
+    } else {
+      assert(map.size <= int.max);
+      writeVarint32(cast(int)map.size);
+      writeByte(cast(byte)(toCType(map.keyType) << 4 | toCType(map.valueType)));
+    }
+  }
+  void writeMapEnd() {}
+
+  void writeSetBegin(TSet set) {
+    writeCollectionBegin(set.elemType, set.size);
+  }
+  void writeSetEnd() {}
+
+
+  /*
+   * Reading methods.
+   */
+
+  bool readBool() {
+    if (hasBoolValue_ == true) {
+      hasBoolValue_ = false;
+      return boolValue_;
+    }
+
+    return readByte() == CType.BOOLEAN_TRUE;
+  }
+
+  byte readByte() {
+    ubyte[1] b = void;
+    trans_.readAll(b);
+    return cast(byte)b[0];
+  }
+
+  short readI16() {
+    return cast(short)zigzagToI32(readVarint32());
+  }
+
+  int readI32() {
+    return zigzagToI32(readVarint32());
+  }
+
+  long readI64() {
+    return zigzagToI64(readVarint64());
+  }
+
+  double readDouble() {
+    IntBuf!long b = void;
+    trans_.readAll(b.bytes);
+    b.value = leToHost(b.value);
+    return *cast(double*)(&b.value);
+  }
+
+  string readString() {
+    return cast(string)readBinary();
+  }
+
+  ubyte[] readBinary() {
+    auto size = readVarint32();
+    checkSize(size, stringSizeLimit);
+
+    if (size == 0) {
+      return null;
+    }
+
+    auto buf = uninitializedArray!(ubyte[])(size);
+    trans_.readAll(buf);
+    return buf;
+  }
+
+  TMessage readMessageBegin() {
+    TMessage msg = void;
+
+    auto protocolId = readByte();
+    if (protocolId != cast(byte)PROTOCOL_ID) {
+      throw new TProtocolException("Bad protocol identifier",
+        TProtocolException.Type.BAD_VERSION);
+    }
+
+    auto versionAndType = readByte();
+    auto ver = versionAndType & VERSION_MASK;
+    if (ver != VERSION_N) {
+      throw new TProtocolException("Bad protocol version",
+        TProtocolException.Type.BAD_VERSION);
+    }
+
+    msg.type = cast(TMessageType)((versionAndType >> TYPE_SHIFT_AMOUNT) & 0x03);
+    msg.seqid = readVarint32();
+    msg.name = readString();
+
+    return msg;
+  }
+  void readMessageEnd() {}
+
+  TStruct readStructBegin() {
+    fieldIdStack_ ~= lastFieldId_;
+    lastFieldId_ = 0;
+    return TStruct();
+  }
+
+  void readStructEnd() {
+    lastFieldId_ = fieldIdStack_[$ - 1];
+    fieldIdStack_ = fieldIdStack_[0 .. $ - 1];
+  }
+
+  TField readFieldBegin() {
+    TField f = void;
+    f.name = null;
+
+    auto bite = readByte();
+    auto type = cast(CType)(bite & 0x0f);
+
+    if (type == CType.STOP) {
+      // Struct stop byte, nothing more to do.
+      f.id = 0;
+      f.type = TType.STOP;
+      return f;
+    }
+
+    // Mask off the 4 MSB of the type header, which could contain a field id
+    // delta.
+    auto modifier = cast(short)((bite & 0xf0) >> 4);
+    if (modifier > 0) {
+      f.id = cast(short)(lastFieldId_ + modifier);
+    } else {
+      // Delta encoding not used, just read the id as usual.
+      f.id = readI16();
+    }
+    f.type = getTType(type);
+
+    if (type == CType.BOOLEAN_TRUE || type == CType.BOOLEAN_FALSE) {
+      // For boolean fields, the value is encoded in the type – keep it around
+      // for the readBool() call.
+      hasBoolValue_ = true;
+      boolValue_ = (type == CType.BOOLEAN_TRUE ? true : false);
+    }
+
+    lastFieldId_ = f.id;
+    return f;
+  }
+  void readFieldEnd() {}
+
+  TList readListBegin() {
+    auto sizeAndType = readByte();
+
+    auto lsize = (sizeAndType >> 4) & 0xf;
+    if (lsize == 0xf) {
+      lsize = readVarint32();
+    }
+    checkSize(lsize, containerSizeLimit);
+
+    TList l = void;
+    l.elemType = getTType(cast(CType)(sizeAndType & 0x0f));
+    l.size = cast(size_t)lsize;
+
+    return l;
+  }
+  void readListEnd() {}
+
+  TMap readMapBegin() {
+    TMap m = void;
+
+    auto size = readVarint32();
+    ubyte kvType;
+    if (size != 0) {
+      kvType = readByte();
+    }
+    checkSize(size, containerSizeLimit);
+
+    m.size = size;
+    m.keyType = getTType(cast(CType)(kvType >> 4));
+    m.valueType = getTType(cast(CType)(kvType & 0xf));
+
+    return m;
+  }
+  void readMapEnd() {}
+
+  TSet readSetBegin() {
+    auto sizeAndType = readByte();
+
+    auto lsize = (sizeAndType >> 4) & 0xf;
+    if (lsize == 0xf) {
+      lsize = readVarint32();
+    }
+    checkSize(lsize, containerSizeLimit);
+
+    TSet s = void;
+    s.elemType = getTType(cast(CType)(sizeAndType & 0xf));
+    s.size = cast(size_t)lsize;
+
+    return s;
+  }
+  void readSetEnd() {}
+
+private:
+  void writeFieldBeginInternal(TField field, byte typeOverride = -1) {
+    // If there's a type override, use that.
+    auto typeToWrite = (typeOverride == -1 ? toCType(field.type) : typeOverride);
+
+    // check if we can use delta encoding for the field id
+    if (field.id > lastFieldId_ && (field.id - lastFieldId_) <= 15) {
+      // write them together
+      writeByte(cast(byte)((field.id - lastFieldId_) << 4 | typeToWrite));
+    } else {
+      // write them separate
+      writeByte(cast(byte)typeToWrite);
+      writeI16(field.id);
+    }
+
+    lastFieldId_ = field.id;
+  }
+
+
+  void writeCollectionBegin(TType elemType, size_t size) {
+    if (size <= 14) {
+      writeByte(cast(byte)(size << 4 | toCType(elemType)));
+    } else {
+      assert(size <= int.max);
+      writeByte(0xf0 | toCType(elemType));
+      writeVarint32(cast(int)size);
+    }
+  }
+
+  void writeVarint32(uint n) {
+    ubyte[5] buf = void;
+    ubyte wsize;
+
+    while (true) {
+      if ((n & ~0x7F) == 0) {
+        buf[wsize++] = cast(ubyte)n;
+        break;
+      } else {
+        buf[wsize++] = cast(ubyte)((n & 0x7F) | 0x80);
+        n >>= 7;
+      }
+    }
+
+    trans_.write(buf[0 .. wsize]);
+  }
+
+  /*
+   * Write an i64 as a varint. Results in 1-10 bytes on the wire.
+   */
+  void writeVarint64(ulong n) {
+    ubyte[10] buf = void;
+    ubyte wsize;
+
+    while (true) {
+      if ((n & ~0x7FL) == 0) {
+        buf[wsize++] = cast(ubyte)n;
+        break;
+      } else {
+        buf[wsize++] = cast(ubyte)((n & 0x7F) | 0x80);
+        n >>= 7;
+      }
+    }
+
+    trans_.write(buf[0 .. wsize]);
+  }
+
+  /*
+   * Convert l into a zigzag long. This allows negative numbers to be
+   * represented compactly as a varint.
+   */
+  ulong i64ToZigzag(long l) {
+    return (l << 1) ^ (l >> 63);
+  }
+
+  /*
+   * Convert n into a zigzag int. This allows negative numbers to be
+   * represented compactly as a varint.
+   */
+  uint i32ToZigzag(int n) {
+    return (n << 1) ^ (n >> 31);
+  }
+
+  CType toCType(TType type) {
+    final switch (type) {
+      case TType.STOP:
+        return CType.STOP;
+      case TType.BOOL:
+        return CType.BOOLEAN_TRUE;
+      case TType.BYTE:
+        return CType.BYTE;
+      case TType.DOUBLE:
+        return CType.DOUBLE;
+      case TType.I16:
+        return CType.I16;
+      case TType.I32:
+        return CType.I32;
+      case TType.I64:
+        return CType.I64;
+      case TType.STRING:
+        return CType.BINARY;
+      case TType.STRUCT:
+        return CType.STRUCT;
+      case TType.MAP:
+        return CType.MAP;
+      case TType.SET:
+        return CType.SET;
+      case TType.LIST:
+        return CType.LIST;
+    }
+  }
+
+  int readVarint32() {
+    return cast(int)readVarint64();
+  }
+
+  long readVarint64() {
+    ulong val;
+    ubyte shift;
+    ubyte[10] buf = void;  // 64 bits / (7 bits/byte) = 10 bytes.
+    auto bufSize = buf.sizeof;
+    auto borrowed = trans_.borrow(buf.ptr, bufSize);
+
+    ubyte rsize;
+
+    if (borrowed) {
+      // Fast path.
+      while (true) {
+        auto bite = borrowed[rsize];
+        rsize++;
+        val |= cast(ulong)(bite & 0x7f) << shift;
+        shift += 7;
+        if (!(bite & 0x80)) {
+          trans_.consume(rsize);
+          return val;
+        }
+        // Have to check for invalid data so we don't crash.
+        if (rsize == buf.sizeof) {
+          throw new TProtocolException(TProtocolException.Type.INVALID_DATA,
+            "Variable-length int over 10 bytes.");
+        }
+      }
+    } else {
+      // Slow path.
+      while (true) {
+        ubyte[1] bite;
+        trans_.readAll(bite);
+        ++rsize;
+
+        val |= cast(ulong)(bite[0] & 0x7f) << shift;
+        shift += 7;
+        if (!(bite[0] & 0x80)) {
+          return val;
+        }
+
+        // Might as well check for invalid data on the slow path too.
+        if (rsize >= buf.sizeof) {
+          throw new TProtocolException(TProtocolException.Type.INVALID_DATA,
+            "Variable-length int over 10 bytes.");
+        }
+      }
+    }
+  }
+
+  /*
+   * Convert from zigzag int to int.
+   */
+  int zigzagToI32(uint n) {
+    return (n >> 1) ^ -(n & 1);
+  }
+
+  /*
+   * Convert from zigzag long to long.
+   */
+  long zigzagToI64(ulong n) {
+    return (n >> 1) ^ -(n & 1);
+  }
+
+  TType getTType(CType type) {
+    final switch (type) {
+      case CType.STOP:
+        return TType.STOP;
+      case CType.BOOLEAN_FALSE:
+        return TType.BOOL;
+      case CType.BOOLEAN_TRUE:
+        return TType.BOOL;
+      case CType.BYTE:
+        return TType.BYTE;
+      case CType.I16:
+        return TType.I16;
+      case CType.I32:
+        return TType.I32;
+      case CType.I64:
+        return TType.I64;
+      case CType.DOUBLE:
+        return TType.DOUBLE;
+      case CType.BINARY:
+        return TType.STRING;
+      case CType.LIST:
+        return TType.LIST;
+      case CType.SET:
+        return TType.SET;
+      case CType.MAP:
+        return TType.MAP;
+      case CType.STRUCT:
+        return TType.STRUCT;
+    }
+  }
+
+  void checkSize(int size, int limit) {
+    if (size < 0) {
+      throw new TProtocolException(TProtocolException.Type.NEGATIVE_SIZE);
+    } else if (limit > 0 && size > limit) {
+      throw new TProtocolException(TProtocolException.Type.SIZE_LIMIT);
+    }
+  }
+
+  enum PROTOCOL_ID = 0x82;
+  enum VERSION_N = 1;
+  enum VERSION_MASK = 0b0001_1111;
+  enum TYPE_MASK = 0b1110_0000;
+  enum TYPE_SHIFT_AMOUNT = 5;
+
+  // Probably need to implement a better stack at some point.
+  short[] fieldIdStack_;
+  short lastFieldId_;
+
+  TField booleanField_;
+
+  bool hasBoolValue_;
+  bool boolValue_;
+
+  Transport trans_;
+}
+
+/**
+ * TCompactProtocol 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)).
+ */
+TCompactProtocol!Transport tCompactProtocol(Transport)(Transport trans,
+  int containerSizeLimit = 0, int stringSizeLimit = 0
+) if (isTTransport!Transport)
+{
+  return new TCompactProtocol!Transport(trans,
+    containerSizeLimit, stringSizeLimit);
+}
+
+private {
+  enum CType : ubyte {
+    STOP = 0x0,
+    BOOLEAN_TRUE = 0x1,
+    BOOLEAN_FALSE = 0x2,
+    BYTE = 0x3,
+    I16 = 0x4,
+    I32 = 0x5,
+    I64 = 0x6,
+    DOUBLE = 0x7,
+    BINARY = 0x8,
+    LIST = 0x9,
+    SET = 0xa,
+    MAP = 0xb,
+    STRUCT = 0xc
+  }
+  static assert(CType.max <= 0xf,
+    "Compact protocol wire type representation must fit into 4 bits.");
+}
+
+unittest {
+  import std.exception;
+  import thrift.transport.memory;
+
+  // Check the message header format.
+  auto buf = new TMemoryBuffer;
+  auto compact = tCompactProtocol(buf);
+  compact.writeMessageBegin(TMessage("foo", TMessageType.CALL, 0));
+
+  auto header = new ubyte[7];
+  buf.readAll(header);
+  enforce(header == [
+    130, // Protocol id.
+    33, // Version/type byte.
+    0, // Sequence id.
+    3, 102, 111, 111 // Method name.
+  ]);
+}
+
+unittest {
+  import thrift.internal.test.protocol;
+  testContainerSizeLimit!(TCompactProtocol!())();
+  testStringSizeLimit!(TCompactProtocol!())();
+}
+
+/**
+ * TProtocolFactory creating a TCompactProtocol instance for passed in
+ * transports.
+ *
+ * The optional Transports template tuple parameter can be used to specify
+ * one or more TTransport implementations to specifically instantiate
+ * TCompactProtocol 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 TCompactProtocolFactory(Transports...) if (
+  allSatisfy!(isTTransport, Transports)
+) : TProtocolFactory {
+  ///
+  this(int containerSizeLimit = 0, int stringSizeLimit = 0) {
+    containerSizeLimit_ = 0;
+    stringSizeLimit_ = 0;
+  }
+
+  TProtocol getProtocol(TTransport trans) const {
+    foreach (Transport; TypeTuple!(Transports, TTransport)) {
+      auto concreteTrans = cast(Transport)trans;
+      if (concreteTrans) {
+        return new TCompactProtocol!Transport(concreteTrans);
+      }
+    }
+    throw new TProtocolException(
+      "Passed null transport to TCompactProtocolFactory.");
+  }
+
+  int containerSizeLimit_;
+  int stringSizeLimit_;
+}

Added: thrift/trunk/lib/d/src/thrift/protocol/json.d
URL: http://svn.apache.org/viewvc/thrift/trunk/lib/d/src/thrift/protocol/json.d?rev=1304085&view=auto
==============================================================================
--- thrift/trunk/lib/d/src/thrift/protocol/json.d (added)
+++ thrift/trunk/lib/d/src/thrift/protocol/json.d Thu Mar 22 21:49:10 2012
@@ -0,0 +1,979 @@
+/*
+ * 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 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() {
+    contextStack_.clear();
+    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) {
+      value = format("%.16g", 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);
+  }
+
+  ubyte readJsonEscapeChar() {
+    readJsonSyntaxChar(ZERO_CHAR);
+    readJsonSyntaxChar(ZERO_CHAR);
+    auto a = reader_.read();
+    auto b = reader_.read();
+    return cast(ubyte)((hexVal(a[0]) << 4) + hexVal(b[0]));
+  }
+
+  string readJsonString(bool skipContext = false) {
+    if (!skipContext) context_.read(reader_);
+
+    readJsonSyntaxChar(STRING_DELIMITER);
+    auto buffer = appender!string();
+
+    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) {
+          ch = readJsonEscapeChar();
+        } 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];
+        }
+      }
+      buffer.put(ch[0]);
+    }
+
+    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 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";
+    }
+  }
+
+  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;
+    }
+  }
+}

Added: thrift/trunk/lib/d/src/thrift/protocol/processor.d
URL: http://svn.apache.org/viewvc/thrift/trunk/lib/d/src/thrift/protocol/processor.d?rev=1304085&view=auto
==============================================================================
--- thrift/trunk/lib/d/src/thrift/protocol/processor.d (added)
+++ thrift/trunk/lib/d/src/thrift/protocol/processor.d Thu Mar 22 21:49:10 2012
@@ -0,0 +1,145 @@
+/*
+ * 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.processor;
+
+// Use selective import once DMD @@BUG314@@ is fixed.
+import std.variant /+ : Variant +/;
+import thrift.protocol.base;
+import thrift.transport.base;
+
+/**
+ * A processor is a generic object which operates upon an input stream and
+ * writes to some output stream.
+ *
+ * The definition of this object is loose, though the typical case is for some
+ * sort of server that either generates responses to an input stream or
+ * forwards data from one pipe onto another.
+ *
+ * An implementation can optionally allow one or more TProcessorEventHandlers
+ * to be attached, providing an interface to hook custom code into the
+ * handling process, which can be used e.g. for gathering statistics.
+ */
+interface TProcessor {
+  ///
+  bool process(TProtocol iprot, TProtocol oprot,
+    Variant connectionContext = Variant()
+  ) in {
+    assert(iprot);
+    assert(oprot);
+  }
+
+  ///
+  final bool process(TProtocol prot, Variant connectionContext = Variant()) {
+    return process(prot, prot, connectionContext);
+  }
+}
+
+/**
+ * Handles events from a processor.
+ */
+interface TProcessorEventHandler {
+  /**
+   * Called before calling other callback methods.
+   *
+   * Expected to return some sort of »call context«, which is passed to all
+   * other callbacks for that function invocation.
+   */
+  Variant createContext(string methodName, Variant connectionContext);
+
+  /**
+   * Called when handling the method associated with a context has been
+   * finished – can be used to perform clean up work.
+   */
+  void deleteContext(Variant callContext, string methodName);
+
+  /**
+   * Called before reading arguments.
+   */
+  void preRead(Variant callContext, string methodName);
+
+  /**
+   * Called between reading arguments and calling the handler.
+   */
+  void postRead(Variant callContext, string methodName);
+
+  /**
+   * Called between calling the handler and writing the response.
+   */
+  void preWrite(Variant callContext, string methodName);
+
+  /**
+   * Called after writing the response.
+   */
+  void postWrite(Variant callContext, string methodName);
+
+  /**
+   * Called when handling a one-way function call is completed successfully.
+   */
+  void onewayComplete(Variant callContext, string methodName);
+
+  /**
+   * Called if the handler throws an undeclared exception.
+   */
+  void handlerError(Variant callContext, string methodName, Exception e);
+}
+
+struct TConnectionInfo {
+  /// The input and output protocols.
+  TProtocol input;
+  TProtocol output; /// Ditto.
+
+  /// The underlying transport used for the connection
+  /// This is the transport that was returned by TServerTransport.accept(),
+  /// and it may be different than the transport pointed to by the input and
+  /// output protocols.
+  TTransport transport;
+}
+
+interface TProcessorFactory {
+  /**
+   * Get the TProcessor to use for a particular connection.
+   *
+   * This method is always invoked in the same thread that the connection was
+   * accepted on, which is always the same thread for all current server
+   * implementations.
+   */
+  TProcessor getProcessor(ref const(TConnectionInfo) connInfo);
+}
+
+/**
+ * The default processor factory which always returns the same instance.
+ */
+class TSingletonProcessorFactory : TProcessorFactory {
+  /**
+   * Creates a new instance.
+   *
+   * Params:
+   *   processor = The processor object to return from getProcessor().
+   */
+  this(TProcessor processor) {
+    processor_ = processor;
+  }
+
+  override TProcessor getProcessor(ref const(TConnectionInfo) connInfo) {
+    return processor_;
+  }
+
+private:
+  TProcessor processor_;
+}

Added: thrift/trunk/lib/d/src/thrift/server/base.d
URL: http://svn.apache.org/viewvc/thrift/trunk/lib/d/src/thrift/server/base.d?rev=1304085&view=auto
==============================================================================
--- thrift/trunk/lib/d/src/thrift/server/base.d (added)
+++ thrift/trunk/lib/d/src/thrift/server/base.d Thu Mar 22 21:49:10 2012
@@ -0,0 +1,147 @@
+/*
+ * 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.server.base;
+
+import std.variant : Variant;
+import thrift.protocol.base;
+import thrift.protocol.binary;
+import thrift.protocol.processor;
+import thrift.server.transport.base;
+import thrift.transport.base;
+import thrift.util.cancellation;
+
+/**
+ * Base class for all Thrift servers.
+ *
+ * By setting the eventHandler property to a TServerEventHandler
+ * implementation, custom code can be integrated into the processing pipeline,
+ * which can be used e.g. for gathering statistics.
+ */
+class TServer {
+  /**
+   * Starts serving.
+   *
+   * Blocks until the server finishes, i.e. a serious problem occured or the
+   * cancellation request has been triggered.
+   *
+   * Server implementations are expected to implement cancellation in a best-
+   * effort way – usually, it should be possible to immediately stop accepting
+   * connections and return after all currently active clients have been
+   * processed, but this might not be the case for every conceivable
+   * implementation.
+   */
+  abstract void serve(TCancellation cancellation = null);
+
+  /// The server event handler to notify. Null by default.
+  TServerEventHandler eventHandler;
+
+protected:
+  this(
+    TProcessor processor,
+    TServerTransport serverTransport,
+    TTransportFactory transportFactory,
+    TProtocolFactory protocolFactory
+  ) {
+    this(processor, serverTransport, transportFactory, transportFactory,
+      protocolFactory, protocolFactory);
+  }
+
+  this(
+    TProcessorFactory processorFactory,
+    TServerTransport serverTransport,
+    TTransportFactory transportFactory,
+    TProtocolFactory protocolFactory
+  ) {
+    this(processorFactory, serverTransport, transportFactory, transportFactory,
+      protocolFactory, protocolFactory);
+  }
+
+  this(
+    TProcessor processor,
+    TServerTransport serverTransport,
+    TTransportFactory inputTransportFactory,
+    TTransportFactory outputTransportFactory,
+    TProtocolFactory inputProtocolFactory,
+    TProtocolFactory outputProtocolFactory
+  ) {
+    this(new TSingletonProcessorFactory(processor), serverTransport,
+      inputTransportFactory, outputTransportFactory,
+      inputProtocolFactory, outputProtocolFactory);
+  }
+
+  this(
+    TProcessorFactory processorFactory,
+    TServerTransport serverTransport,
+    TTransportFactory inputTransportFactory,
+    TTransportFactory outputTransportFactory,
+    TProtocolFactory inputProtocolFactory,
+    TProtocolFactory outputProtocolFactory
+  ) {
+    import std.exception;
+    import thrift.base;
+    enforce(inputTransportFactory,
+      new TException("Input transport factory must not be null."));
+    enforce(outputTransportFactory,
+      new TException("Output transport factory must not be null."));
+    enforce(inputProtocolFactory,
+      new TException("Input protocol factory must not be null."));
+    enforce(outputProtocolFactory,
+      new TException("Output protocol factory must not be null."));
+
+    processorFactory_ = processorFactory;
+    serverTransport_ = serverTransport;
+    inputTransportFactory_ = inputTransportFactory;
+    outputTransportFactory_ = outputTransportFactory;
+    inputProtocolFactory_ = inputProtocolFactory;
+    outputProtocolFactory_ = outputProtocolFactory;
+  }
+
+  TProcessorFactory processorFactory_;
+  TServerTransport serverTransport_;
+  TTransportFactory inputTransportFactory_;
+  TTransportFactory outputTransportFactory_;
+  TProtocolFactory inputProtocolFactory_;
+  TProtocolFactory outputProtocolFactory_;
+}
+
+/**
+ * Handles events from a TServer core.
+ */
+interface TServerEventHandler {
+  /**
+   * Called before the server starts accepting connections.
+   */
+  void preServe();
+
+  /**
+   * Called when a new client has connected and processing is about to begin.
+   */
+  Variant createContext(TProtocol input, TProtocol output);
+
+  /**
+   * Called when request handling for a client has been finished – can be used
+   * to perform clean up work.
+   */
+  void deleteContext(Variant serverContext, TProtocol input, TProtocol output);
+
+  /**
+   * Called when the processor for a client call is about to be invoked.
+   */
+  void preProcess(Variant serverContext, TTransport transport);
+}