]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/jaegertracing/thrift/lib/d/src/thrift/protocol/compact.d
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / jaegertracing / thrift / lib / d / src / thrift / protocol / compact.d
diff --git a/ceph/src/jaegertracing/thrift/lib/d/src/thrift/protocol/compact.d b/ceph/src/jaegertracing/thrift/lib/d/src/thrift/protocol/compact.d
new file mode 100644 (file)
index 0000000..9155c81
--- /dev/null
@@ -0,0 +1,698 @@
+/*
+ * 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(cast(byte)((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) & TYPE_BITS);
+    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(cast(byte)(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;
+      case TType.VOID:
+        assert(false, "Invalid type passed.");
+    }
+  }
+
+  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_BITS = 0b0000_0111;
+  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_;
+}