diff options
| author | 2011-02-07 00:32:21 +0100 | |
|---|---|---|
| committer | 2011-02-07 00:32:21 +0100 | |
| commit | 69f2d3660e44bd138d24b6cf97b234bc2efc1c78 (patch) | |
| tree | af276b8ed0b5cac5a62e6949a00d607afcc94e5d /module/lib/thrift/protocol | |
| parent | closed #231 (diff) | |
| download | pyload-69f2d3660e44bd138d24b6cf97b234bc2efc1c78.tar.xz | |
added Thrift backend
Diffstat (limited to 'module/lib/thrift/protocol')
| -rw-r--r-- | module/lib/thrift/protocol/TBinaryProtocol.py | 259 | ||||
| -rw-r--r-- | module/lib/thrift/protocol/TCompactProtocol.py | 368 | ||||
| -rw-r--r-- | module/lib/thrift/protocol/TProtocol.py | 205 | ||||
| -rw-r--r-- | module/lib/thrift/protocol/__init__.py | 20 | ||||
| -rw-r--r-- | module/lib/thrift/protocol/fastbinary.c | 1203 | 
5 files changed, 2055 insertions, 0 deletions
| diff --git a/module/lib/thrift/protocol/TBinaryProtocol.py b/module/lib/thrift/protocol/TBinaryProtocol.py new file mode 100644 index 000000000..50c6aa896 --- /dev/null +++ b/module/lib/thrift/protocol/TBinaryProtocol.py @@ -0,0 +1,259 @@ +# +# 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. +# + +from TProtocol import * +from struct import pack, unpack + +class TBinaryProtocol(TProtocolBase): + +  """Binary implementation of the Thrift protocol driver.""" + +  # NastyHaxx. Python 2.4+ on 32-bit machines forces hex constants to be +  # positive, converting this into a long. If we hardcode the int value +  # instead it'll stay in 32 bit-land. + +  # VERSION_MASK = 0xffff0000 +  VERSION_MASK = -65536 + +  # VERSION_1 = 0x80010000 +  VERSION_1 = -2147418112 + +  TYPE_MASK = 0x000000ff + +  def __init__(self, trans, strictRead=False, strictWrite=True): +    TProtocolBase.__init__(self, trans) +    self.strictRead = strictRead +    self.strictWrite = strictWrite + +  def writeMessageBegin(self, name, type, seqid): +    if self.strictWrite: +      self.writeI32(TBinaryProtocol.VERSION_1 | type) +      self.writeString(name) +      self.writeI32(seqid) +    else: +      self.writeString(name) +      self.writeByte(type) +      self.writeI32(seqid) + +  def writeMessageEnd(self): +    pass + +  def writeStructBegin(self, name): +    pass + +  def writeStructEnd(self): +    pass + +  def writeFieldBegin(self, name, type, id): +    self.writeByte(type) +    self.writeI16(id) + +  def writeFieldEnd(self): +    pass + +  def writeFieldStop(self): +    self.writeByte(TType.STOP); + +  def writeMapBegin(self, ktype, vtype, size): +    self.writeByte(ktype) +    self.writeByte(vtype) +    self.writeI32(size) + +  def writeMapEnd(self): +    pass + +  def writeListBegin(self, etype, size): +    self.writeByte(etype) +    self.writeI32(size) + +  def writeListEnd(self): +    pass + +  def writeSetBegin(self, etype, size): +    self.writeByte(etype) +    self.writeI32(size) + +  def writeSetEnd(self): +    pass + +  def writeBool(self, bool): +    if bool: +      self.writeByte(1) +    else: +      self.writeByte(0) + +  def writeByte(self, byte): +    buff = pack("!b", byte) +    self.trans.write(buff) + +  def writeI16(self, i16): +    buff = pack("!h", i16) +    self.trans.write(buff) + +  def writeI32(self, i32): +    buff = pack("!i", i32) +    self.trans.write(buff) + +  def writeI64(self, i64): +    buff = pack("!q", i64) +    self.trans.write(buff) + +  def writeDouble(self, dub): +    buff = pack("!d", dub) +    self.trans.write(buff) + +  def writeString(self, str): +    self.writeI32(len(str)) +    self.trans.write(str) + +  def readMessageBegin(self): +    sz = self.readI32() +    if sz < 0: +      version = sz & TBinaryProtocol.VERSION_MASK +      if version != TBinaryProtocol.VERSION_1: +        raise TProtocolException(type=TProtocolException.BAD_VERSION, message='Bad version in readMessageBegin: %d' % (sz)) +      type = sz & TBinaryProtocol.TYPE_MASK +      name = self.readString() +      seqid = self.readI32() +    else: +      if self.strictRead: +        raise TProtocolException(type=TProtocolException.BAD_VERSION, message='No protocol version header') +      name = self.trans.readAll(sz) +      type = self.readByte() +      seqid = self.readI32() +    return (name, type, seqid) + +  def readMessageEnd(self): +    pass + +  def readStructBegin(self): +    pass + +  def readStructEnd(self): +    pass + +  def readFieldBegin(self): +    type = self.readByte() +    if type == TType.STOP: +      return (None, type, 0) +    id = self.readI16() +    return (None, type, id) + +  def readFieldEnd(self): +    pass + +  def readMapBegin(self): +    ktype = self.readByte() +    vtype = self.readByte() +    size = self.readI32() +    return (ktype, vtype, size) + +  def readMapEnd(self): +    pass + +  def readListBegin(self): +    etype = self.readByte() +    size = self.readI32() +    return (etype, size) + +  def readListEnd(self): +    pass + +  def readSetBegin(self): +    etype = self.readByte() +    size = self.readI32() +    return (etype, size) + +  def readSetEnd(self): +    pass + +  def readBool(self): +    byte = self.readByte() +    if byte == 0: +      return False +    return True + +  def readByte(self): +    buff = self.trans.readAll(1) +    val, = unpack('!b', buff) +    return val + +  def readI16(self): +    buff = self.trans.readAll(2) +    val, = unpack('!h', buff) +    return val + +  def readI32(self): +    buff = self.trans.readAll(4) +    val, = unpack('!i', buff) +    return val + +  def readI64(self): +    buff = self.trans.readAll(8) +    val, = unpack('!q', buff) +    return val + +  def readDouble(self): +    buff = self.trans.readAll(8) +    val, = unpack('!d', buff) +    return val + +  def readString(self): +    len = self.readI32() +    str = self.trans.readAll(len) +    return str + + +class TBinaryProtocolFactory: +  def __init__(self, strictRead=False, strictWrite=True): +    self.strictRead = strictRead +    self.strictWrite = strictWrite + +  def getProtocol(self, trans): +    prot = TBinaryProtocol(trans, self.strictRead, self.strictWrite) +    return prot + + +class TBinaryProtocolAccelerated(TBinaryProtocol): + +  """C-Accelerated version of TBinaryProtocol. + +  This class does not override any of TBinaryProtocol's methods, +  but the generated code recognizes it directly and will call into +  our C module to do the encoding, bypassing this object entirely. +  We inherit from TBinaryProtocol so that the normal TBinaryProtocol +  encoding can happen if the fastbinary module doesn't work for some +  reason.  (TODO(dreiss): Make this happen sanely in more cases.) + +  In order to take advantage of the C module, just use +  TBinaryProtocolAccelerated instead of TBinaryProtocol. + +  NOTE:  This code was contributed by an external developer. +         The internal Thrift team has reviewed and tested it, +         but we cannot guarantee that it is production-ready. +         Please feel free to report bugs and/or success stories +         to the public mailing list. +  """ + +  pass + + +class TBinaryProtocolAcceleratedFactory: +  def getProtocol(self, trans): +    return TBinaryProtocolAccelerated(trans) diff --git a/module/lib/thrift/protocol/TCompactProtocol.py b/module/lib/thrift/protocol/TCompactProtocol.py new file mode 100644 index 000000000..fbc156a8f --- /dev/null +++ b/module/lib/thrift/protocol/TCompactProtocol.py @@ -0,0 +1,368 @@ +from TProtocol import * +from struct import pack, unpack + +__all__ = ['TCompactProtocol', 'TCompactProtocolFactory'] + +CLEAR = 0 +FIELD_WRITE = 1 +VALUE_WRITE = 2 +CONTAINER_WRITE = 3 +BOOL_WRITE = 4 +FIELD_READ = 5 +CONTAINER_READ = 6 +VALUE_READ = 7 +BOOL_READ = 8 + +def make_helper(v_from, container): +  def helper(func): +    def nested(self, *args, **kwargs): +      assert self.state in (v_from, container), (self.state, v_from, container) +      return func(self, *args, **kwargs) +    return nested +  return helper +writer = make_helper(VALUE_WRITE, CONTAINER_WRITE) +reader = make_helper(VALUE_READ, CONTAINER_READ) + +def makeZigZag(n, bits): +  return (n << 1) ^ (n >> (bits - 1)) + +def fromZigZag(n): +  return (n >> 1) ^ -(n & 1) + +def writeVarint(trans, n): +  out = [] +  while True: +    if n & ~0x7f == 0: +      out.append(n) +      break +    else: +      out.append((n & 0xff) | 0x80) +      n = n >> 7 +  trans.write(''.join(map(chr, out))) + +def readVarint(trans): +  result = 0 +  shift = 0 +  while True: +    x = trans.readAll(1) +    byte = ord(x) +    result |= (byte & 0x7f) << shift +    if byte >> 7 == 0: +      return result +    shift += 7 + +class CompactType: +  TRUE = 1 +  FALSE = 2 +  BYTE = 0x03 +  I16 = 0x04 +  I32 = 0x05 +  I64 = 0x06 +  DOUBLE = 0x07 +  BINARY = 0x08 +  LIST = 0x09 +  SET = 0x0A +  MAP = 0x0B +  STRUCT = 0x0C + +CTYPES = {TType.BOOL: CompactType.TRUE, # used for collection +          TType.BYTE: CompactType.BYTE, +          TType.I16: CompactType.I16, +          TType.I32: CompactType.I32, +          TType.I64: CompactType.I64, +          TType.DOUBLE: CompactType.DOUBLE, +          TType.STRING: CompactType.BINARY, +          TType.STRUCT: CompactType.STRUCT, +          TType.LIST: CompactType.LIST, +          TType.SET: CompactType.SET, +          TType.MAP: CompactType.MAP, +          } + +TTYPES = {} +for k, v in CTYPES.items(): +  TTYPES[v] = k +TTYPES[CompactType.FALSE] = TType.BOOL +del k +del v + +class TCompactProtocol(TProtocolBase): +  "Compact implementation of the Thrift protocol driver." + +  PROTOCOL_ID = 0x82 +  VERSION = 1 +  VERSION_MASK = 0x1f +  TYPE_MASK = 0xe0 +  TYPE_SHIFT_AMOUNT = 5 + +  def __init__(self, trans): +    TProtocolBase.__init__(self, trans) +    self.state = CLEAR +    self.__last_fid = 0 +    self.__bool_fid = None +    self.__bool_value = None +    self.__structs = [] +    self.__containers = [] + +  def __writeVarint(self, n): +    writeVarint(self.trans, n) + +  def writeMessageBegin(self, name, type, seqid): +    assert self.state == CLEAR +    self.__writeUByte(self.PROTOCOL_ID) +    self.__writeUByte(self.VERSION | (type << self.TYPE_SHIFT_AMOUNT)) +    self.__writeVarint(seqid) +    self.__writeString(name) +    self.state = VALUE_WRITE + +  def writeMessageEnd(self): +    assert self.state == VALUE_WRITE +    self.state = CLEAR + +  def writeStructBegin(self, name): +    assert self.state in (CLEAR, CONTAINER_WRITE, VALUE_WRITE), self.state +    self.__structs.append((self.state, self.__last_fid)) +    self.state = FIELD_WRITE +    self.__last_fid = 0 + +  def writeStructEnd(self): +    assert self.state == FIELD_WRITE +    self.state, self.__last_fid = self.__structs.pop() + +  def writeFieldStop(self): +    self.__writeByte(0) + +  def __writeFieldHeader(self, type, fid): +    delta = fid - self.__last_fid +    if 0 < delta <= 15: +      self.__writeUByte(delta << 4 | type) +    else: +      self.__writeByte(type) +      self.__writeI16(fid) +    self.__last_fid = fid + +  def writeFieldBegin(self, name, type, fid): +    assert self.state == FIELD_WRITE, self.state +    if type == TType.BOOL: +      self.state = BOOL_WRITE +      self.__bool_fid = fid +    else: +      self.state = VALUE_WRITE +      self.__writeFieldHeader(CTYPES[type], fid) + +  def writeFieldEnd(self): +    assert self.state in (VALUE_WRITE, BOOL_WRITE), self.state +    self.state = FIELD_WRITE + +  def __writeUByte(self, byte): +    self.trans.write(pack('!B', byte)) + +  def __writeByte(self, byte): +    self.trans.write(pack('!b', byte)) + +  def __writeI16(self, i16): +    self.__writeVarint(makeZigZag(i16, 16)) + +  def __writeSize(self, i32): +    self.__writeVarint(i32) + +  def writeCollectionBegin(self, etype, size): +    assert self.state in (VALUE_WRITE, CONTAINER_WRITE), self.state +    if size <= 14: +      self.__writeUByte(size << 4 | CTYPES[etype]) +    else: +      self.__writeUByte(0xf0 | CTYPES[etype]) +      self.__writeSize(size) +    self.__containers.append(self.state) +    self.state = CONTAINER_WRITE +  writeSetBegin = writeCollectionBegin +  writeListBegin = writeCollectionBegin + +  def writeMapBegin(self, ktype, vtype, size): +    assert self.state in (VALUE_WRITE, CONTAINER_WRITE), self.state +    if size == 0: +      self.__writeByte(0) +    else: +      self.__writeSize(size) +      self.__writeUByte(CTYPES[ktype] << 4 | CTYPES[vtype]) +    self.__containers.append(self.state) +    self.state = CONTAINER_WRITE + +  def writeCollectionEnd(self): +    assert self.state == CONTAINER_WRITE, self.state +    self.state = self.__containers.pop() +  writeMapEnd = writeCollectionEnd +  writeSetEnd = writeCollectionEnd +  writeListEnd = writeCollectionEnd + +  def writeBool(self, bool): +    if self.state == BOOL_WRITE: +      self.__writeFieldHeader(types[bool], self.__bool_fid) +    elif self.state == CONTAINER_WRITE: +      self.__writeByte(int(bool)) +    else: +      raise AssertetionError, "Invalid state in compact protocol" + +  writeByte = writer(__writeByte) +  writeI16 = writer(__writeI16) + +  @writer +  def writeI32(self, i32): +    self.__writeVarint(makeZigZag(i32, 32)) + +  @writer +  def writeI64(self, i64): +    self.__writeVarint(makeZigZag(i64, 64)) + +  @writer +  def writeDouble(self, dub): +    self.trans.write(pack('!d', dub)) + +  def __writeString(self, s): +    self.__writeSize(len(s)) +    self.trans.write(s) +  writeString = writer(__writeString) + +  def readFieldBegin(self): +    assert self.state == FIELD_READ, self.state +    type = self.__readUByte() +    if type & 0x0f == TType.STOP: +      return (None, 0, 0) +    delta = type >> 4 +    if delta == 0: +      fid = self.__readI16() +    else: +      fid = self.__last_fid + delta +    self.__last_fid = fid +    type = type & 0x0f +    if type == CompactType.TRUE: +      self.state = BOOL_READ +      self.__bool_value = True +    elif type == CompactType.FALSE: +      self.state = BOOL_READ +      self.__bool_value = False +    else: +      self.state = VALUE_READ +    return (None, self.__getTType(type), fid) + +  def readFieldEnd(self): +    assert self.state in (VALUE_READ, BOOL_READ), self.state +    self.state = FIELD_READ + +  def __readUByte(self): +    result, = unpack('!B', self.trans.readAll(1)) +    return result + +  def __readByte(self): +    result, = unpack('!b', self.trans.readAll(1)) +    return result + +  def __readVarint(self): +    return readVarint(self.trans) + +  def __readZigZag(self): +    return fromZigZag(self.__readVarint()) + +  def __readSize(self): +    result = self.__readVarint() +    if result < 0: +      raise TException("Length < 0") +    return result + +  def readMessageBegin(self): +    assert self.state == CLEAR +    proto_id = self.__readUByte() +    if proto_id != self.PROTOCOL_ID: +      raise TProtocolException(TProtocolException.BAD_VERSION, +          'Bad protocol id in the message: %d' % proto_id) +    ver_type = self.__readUByte() +    type = (ver_type & self.TYPE_MASK) >> self.TYPE_SHIFT_AMOUNT +    version = ver_type & self.VERSION_MASK +    if version != self.VERSION: +      raise TProtocolException(TProtocolException.BAD_VERSION, +          'Bad version: %d (expect %d)' % (version, self.VERSION)) +    seqid = self.__readVarint() +    name = self.__readString() +    return (name, type, seqid) + +  def readMessageEnd(self): +    assert self.state == VALUE_READ +    assert len(self.__structs) == 0 +    self.state = CLEAR + +  def readStructBegin(self): +    assert self.state in (CLEAR, CONTAINER_READ, VALUE_READ), self.state +    self.__structs.append((self.state, self.__last_fid)) +    self.state = FIELD_READ +    self.__last_fid = 0 + +  def readStructEnd(self): +    assert self.state == FIELD_READ +    self.state, self.__last_fid = self.__structs.pop() + +  def readCollectionBegin(self): +    assert self.state in (VALUE_READ, CONTAINER_READ), self.state +    size_type = self.__readUByte() +    size = size_type >> 4 +    type = self.__getTType(size_type) +    if size == 15: +      size = self.__readSize() +    self.__containers.append(self.state) +    self.state = CONTAINER_READ +    return type, size +  readSetBegin = readCollectionBegin +  readListBegin = readCollectionBegin + +  def readMapBegin(self): +    assert self.state in (VALUE_READ, CONTAINER_READ), self.state +    size = self.__readSize() +    types = 0 +    if size > 0: +      types = self.__readUByte() +    vtype = self.__getTType(types) +    ktype = self.__getTType(types >> 4) +    self.__containers.append(self.state) +    self.state = CONTAINER_READ +    return (ktype, vtype, size) + +  def readCollectionEnd(self): +    assert self.state == CONTAINER_READ, self.state +    self.state = self.__containers.pop() +  readSetEnd = readCollectionEnd +  readListEnd = readCollectionEnd +  readMapEnd = readCollectionEnd + +  def readBool(self): +    if self.state == BOOL_READ: +      return self.__bool_value +    elif self.state == CONTAINER_READ: +      return bool(self.__readByte()) +    else: +      raise AssertionError, "Invalid state in compact protocol: %d" % self.state + +  readByte = reader(__readByte) +  __readI16 = __readZigZag +  readI16 = reader(__readZigZag) +  readI32 = reader(__readZigZag) +  readI64 = reader(__readZigZag) + +  @reader +  def readDouble(self): +    buff = self.trans.readAll(8) +    val, = unpack('!d', buff) +    return val + +  def __readString(self): +    len = self.__readSize() +    return self.trans.readAll(len) +  readString = reader(__readString) + +  def __getTType(self, byte): +    return TTYPES[byte & 0x0f] + + +class TCompactProtocolFactory: +  def __init__(self): +    pass + +  def getProtocol(self, trans): +    return TCompactProtocol(trans) diff --git a/module/lib/thrift/protocol/TProtocol.py b/module/lib/thrift/protocol/TProtocol.py new file mode 100644 index 000000000..be3cb1403 --- /dev/null +++ b/module/lib/thrift/protocol/TProtocol.py @@ -0,0 +1,205 @@ +# +# 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. +# + +from thrift.Thrift import * + +class TProtocolException(TException): + +  """Custom Protocol Exception class""" + +  UNKNOWN = 0 +  INVALID_DATA = 1 +  NEGATIVE_SIZE = 2 +  SIZE_LIMIT = 3 +  BAD_VERSION = 4 + +  def __init__(self, type=UNKNOWN, message=None): +    TException.__init__(self, message) +    self.type = type + +class TProtocolBase: + +  """Base class for Thrift protocol driver.""" + +  def __init__(self, trans): +    self.trans = trans + +  def writeMessageBegin(self, name, type, seqid): +    pass + +  def writeMessageEnd(self): +    pass + +  def writeStructBegin(self, name): +    pass + +  def writeStructEnd(self): +    pass + +  def writeFieldBegin(self, name, type, id): +    pass + +  def writeFieldEnd(self): +    pass + +  def writeFieldStop(self): +    pass + +  def writeMapBegin(self, ktype, vtype, size): +    pass + +  def writeMapEnd(self): +    pass + +  def writeListBegin(self, etype, size): +    pass + +  def writeListEnd(self): +    pass + +  def writeSetBegin(self, etype, size): +    pass + +  def writeSetEnd(self): +    pass + +  def writeBool(self, bool): +    pass + +  def writeByte(self, byte): +    pass + +  def writeI16(self, i16): +    pass + +  def writeI32(self, i32): +    pass + +  def writeI64(self, i64): +    pass + +  def writeDouble(self, dub): +    pass + +  def writeString(self, str): +    pass + +  def readMessageBegin(self): +    pass + +  def readMessageEnd(self): +    pass + +  def readStructBegin(self): +    pass + +  def readStructEnd(self): +    pass + +  def readFieldBegin(self): +    pass + +  def readFieldEnd(self): +    pass + +  def readMapBegin(self): +    pass + +  def readMapEnd(self): +    pass + +  def readListBegin(self): +    pass + +  def readListEnd(self): +    pass + +  def readSetBegin(self): +    pass + +  def readSetEnd(self): +    pass + +  def readBool(self): +    pass + +  def readByte(self): +    pass + +  def readI16(self): +    pass + +  def readI32(self): +    pass + +  def readI64(self): +    pass + +  def readDouble(self): +    pass + +  def readString(self): +    pass + +  def skip(self, type): +    if type == TType.STOP: +      return +    elif type == TType.BOOL: +      self.readBool() +    elif type == TType.BYTE: +      self.readByte() +    elif type == TType.I16: +      self.readI16() +    elif type == TType.I32: +      self.readI32() +    elif type == TType.I64: +      self.readI64() +    elif type == TType.DOUBLE: +      self.readDouble() +    elif type == TType.STRING: +      self.readString() +    elif type == TType.STRUCT: +      name = self.readStructBegin() +      while True: +        (name, type, id) = self.readFieldBegin() +        if type == TType.STOP: +          break +        self.skip(type) +        self.readFieldEnd() +      self.readStructEnd() +    elif type == TType.MAP: +      (ktype, vtype, size) = self.readMapBegin() +      for i in range(size): +        self.skip(ktype) +        self.skip(vtype) +      self.readMapEnd() +    elif type == TType.SET: +      (etype, size) = self.readSetBegin() +      for i in range(size): +        self.skip(etype) +      self.readSetEnd() +    elif type == TType.LIST: +      (etype, size) = self.readListBegin() +      for i in range(size): +        self.skip(etype) +      self.readListEnd() + +class TProtocolFactory: +  def getProtocol(self, trans): +    pass diff --git a/module/lib/thrift/protocol/__init__.py b/module/lib/thrift/protocol/__init__.py new file mode 100644 index 000000000..01bfe18e5 --- /dev/null +++ b/module/lib/thrift/protocol/__init__.py @@ -0,0 +1,20 @@ +# +# 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. +# + +__all__ = ['TProtocol', 'TBinaryProtocol', 'fastbinary'] diff --git a/module/lib/thrift/protocol/fastbinary.c b/module/lib/thrift/protocol/fastbinary.c new file mode 100644 index 000000000..67b215a83 --- /dev/null +++ b/module/lib/thrift/protocol/fastbinary.c @@ -0,0 +1,1203 @@ +/* + * 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. + */ + +#include <Python.h> +#include "cStringIO.h" +#include <stdbool.h> +#include <stdint.h> +#include <netinet/in.h> + +/* Fix endianness issues on Solaris */ +#if defined (__SVR4) && defined (__sun) + #if defined(__i386) && !defined(__i386__) +  #define __i386__ + #endif + + #ifndef BIG_ENDIAN +  #define BIG_ENDIAN (4321) + #endif + #ifndef LITTLE_ENDIAN +  #define LITTLE_ENDIAN (1234) + #endif + + /* I386 is LE, even on Solaris */ + #if !defined(BYTE_ORDER) && defined(__i386__) +  #define BYTE_ORDER LITTLE_ENDIAN + #endif +#endif + +// TODO(dreiss): defval appears to be unused.  Look into removing it. +// TODO(dreiss): Make parse_spec_args recursive, and cache the output +//               permanently in the object.  (Malloc and orphan.) +// TODO(dreiss): Why do we need cStringIO for reading, why not just char*? +//               Can cStringIO let us work with a BufferedTransport? +// TODO(dreiss): Don't ignore the rv from cwrite (maybe). + +/* ====== BEGIN UTILITIES ====== */ + +#define INIT_OUTBUF_SIZE 128 + +// Stolen out of TProtocol.h. +// It would be a huge pain to have both get this from one place. +typedef enum TType { +  T_STOP       = 0, +  T_VOID       = 1, +  T_BOOL       = 2, +  T_BYTE       = 3, +  T_I08        = 3, +  T_I16        = 6, +  T_I32        = 8, +  T_U64        = 9, +  T_I64        = 10, +  T_DOUBLE     = 4, +  T_STRING     = 11, +  T_UTF7       = 11, +  T_STRUCT     = 12, +  T_MAP        = 13, +  T_SET        = 14, +  T_LIST       = 15, +  T_UTF8       = 16, +  T_UTF16      = 17 +} TType; + +#ifndef __BYTE_ORDER +# if defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN) +#  define __BYTE_ORDER BYTE_ORDER +#  define __LITTLE_ENDIAN LITTLE_ENDIAN +#  define __BIG_ENDIAN BIG_ENDIAN +# else +#  error "Cannot determine endianness" +# endif +#endif + +// Same comment as the enum.  Sorry. +#if __BYTE_ORDER == __BIG_ENDIAN +# define ntohll(n) (n) +# define htonll(n) (n) +#elif __BYTE_ORDER == __LITTLE_ENDIAN +# if defined(__GNUC__) && defined(__GLIBC__) +#  include <byteswap.h> +#  define ntohll(n) bswap_64(n) +#  define htonll(n) bswap_64(n) +# else /* GNUC & GLIBC */ +#  define ntohll(n) ( (((unsigned long long)ntohl(n)) << 32) + ntohl(n >> 32) ) +#  define htonll(n) ( (((unsigned long long)htonl(n)) << 32) + htonl(n >> 32) ) +# endif /* GNUC & GLIBC */ +#else /* __BYTE_ORDER */ +# error "Can't define htonll or ntohll!" +#endif + +// Doing a benchmark shows that interning actually makes a difference, amazingly. +#define INTERN_STRING(value) _intern_ ## value + +#define INT_CONV_ERROR_OCCURRED(v) ( ((v) == -1) && PyErr_Occurred() ) +#define CHECK_RANGE(v, min, max) ( ((v) <= (max)) && ((v) >= (min)) ) + +// Py_ssize_t was not defined before Python 2.5 +#if (PY_VERSION_HEX < 0x02050000) +typedef int Py_ssize_t; +#endif + +/** + * A cache of the spec_args for a set or list, + * so we don't have to keep calling PyTuple_GET_ITEM. + */ +typedef struct { +  TType element_type; +  PyObject* typeargs; +} SetListTypeArgs; + +/** + * A cache of the spec_args for a map, + * so we don't have to keep calling PyTuple_GET_ITEM. + */ +typedef struct { +  TType ktag; +  TType vtag; +  PyObject* ktypeargs; +  PyObject* vtypeargs; +} MapTypeArgs; + +/** + * A cache of the spec_args for a struct, + * so we don't have to keep calling PyTuple_GET_ITEM. + */ +typedef struct { +  PyObject* klass; +  PyObject* spec; +} StructTypeArgs; + +/** + * A cache of the item spec from a struct specification, + * so we don't have to keep calling PyTuple_GET_ITEM. + */ +typedef struct { +  int tag; +  TType type; +  PyObject* attrname; +  PyObject* typeargs; +  PyObject* defval; +} StructItemSpec; + +/** + * A cache of the two key attributes of a CReadableTransport, + * so we don't have to keep calling PyObject_GetAttr. + */ +typedef struct { +  PyObject* stringiobuf; +  PyObject* refill_callable; +} DecodeBuffer; + +/** Pointer to interned string to speed up attribute lookup. */ +static PyObject* INTERN_STRING(cstringio_buf); +/** Pointer to interned string to speed up attribute lookup. */ +static PyObject* INTERN_STRING(cstringio_refill); + +static inline bool +check_ssize_t_32(Py_ssize_t len) { +  // error from getting the int +  if (INT_CONV_ERROR_OCCURRED(len)) { +    return false; +  } +  if (!CHECK_RANGE(len, 0, INT32_MAX)) { +    PyErr_SetString(PyExc_OverflowError, "string size out of range"); +    return false; +  } +  return true; +} + +static inline bool +parse_pyint(PyObject* o, int32_t* ret, int32_t min, int32_t max) { +  long val = PyInt_AsLong(o); + +  if (INT_CONV_ERROR_OCCURRED(val)) { +    return false; +  } +  if (!CHECK_RANGE(val, min, max)) { +    PyErr_SetString(PyExc_OverflowError, "int out of range"); +    return false; +  } + +  *ret = (int32_t) val; +  return true; +} + + +/* --- FUNCTIONS TO PARSE STRUCT SPECIFICATOINS --- */ + +static bool +parse_set_list_args(SetListTypeArgs* dest, PyObject* typeargs) { +  if (PyTuple_Size(typeargs) != 2) { +    PyErr_SetString(PyExc_TypeError, "expecting tuple of size 2 for list/set type args"); +    return false; +  } + +  dest->element_type = PyInt_AsLong(PyTuple_GET_ITEM(typeargs, 0)); +  if (INT_CONV_ERROR_OCCURRED(dest->element_type)) { +    return false; +  } + +  dest->typeargs = PyTuple_GET_ITEM(typeargs, 1); + +  return true; +} + +static bool +parse_map_args(MapTypeArgs* dest, PyObject* typeargs) { +  if (PyTuple_Size(typeargs) != 4) { +    PyErr_SetString(PyExc_TypeError, "expecting 4 arguments for typeargs to map"); +    return false; +  } + +  dest->ktag = PyInt_AsLong(PyTuple_GET_ITEM(typeargs, 0)); +  if (INT_CONV_ERROR_OCCURRED(dest->ktag)) { +    return false; +  } + +  dest->vtag = PyInt_AsLong(PyTuple_GET_ITEM(typeargs, 2)); +  if (INT_CONV_ERROR_OCCURRED(dest->vtag)) { +    return false; +  } + +  dest->ktypeargs = PyTuple_GET_ITEM(typeargs, 1); +  dest->vtypeargs = PyTuple_GET_ITEM(typeargs, 3); + +  return true; +} + +static bool +parse_struct_args(StructTypeArgs* dest, PyObject* typeargs) { +  if (PyTuple_Size(typeargs) != 2) { +    PyErr_SetString(PyExc_TypeError, "expecting tuple of size 2 for struct args"); +    return false; +  } + +  dest->klass = PyTuple_GET_ITEM(typeargs, 0); +  dest->spec = PyTuple_GET_ITEM(typeargs, 1); + +  return true; +} + +static int +parse_struct_item_spec(StructItemSpec* dest, PyObject* spec_tuple) { + +  // i'd like to use ParseArgs here, but it seems to be a bottleneck. +  if (PyTuple_Size(spec_tuple) != 5) { +    PyErr_SetString(PyExc_TypeError, "expecting 5 arguments for spec tuple"); +    return false; +  } + +  dest->tag = PyInt_AsLong(PyTuple_GET_ITEM(spec_tuple, 0)); +  if (INT_CONV_ERROR_OCCURRED(dest->tag)) { +    return false; +  } + +  dest->type = PyInt_AsLong(PyTuple_GET_ITEM(spec_tuple, 1)); +  if (INT_CONV_ERROR_OCCURRED(dest->type)) { +    return false; +  } + +  dest->attrname = PyTuple_GET_ITEM(spec_tuple, 2); +  dest->typeargs = PyTuple_GET_ITEM(spec_tuple, 3); +  dest->defval = PyTuple_GET_ITEM(spec_tuple, 4); +  return true; +} + +/* ====== END UTILITIES ====== */ + + +/* ====== BEGIN WRITING FUNCTIONS ====== */ + +/* --- LOW-LEVEL WRITING FUNCTIONS --- */ + +static void writeByte(PyObject* outbuf, int8_t val) { +  int8_t net = val; +  PycStringIO->cwrite(outbuf, (char*)&net, sizeof(int8_t)); +} + +static void writeI16(PyObject* outbuf, int16_t val) { +  int16_t net = (int16_t)htons(val); +  PycStringIO->cwrite(outbuf, (char*)&net, sizeof(int16_t)); +} + +static void writeI32(PyObject* outbuf, int32_t val) { +  int32_t net = (int32_t)htonl(val); +  PycStringIO->cwrite(outbuf, (char*)&net, sizeof(int32_t)); +} + +static void writeI64(PyObject* outbuf, int64_t val) { +  int64_t net = (int64_t)htonll(val); +  PycStringIO->cwrite(outbuf, (char*)&net, sizeof(int64_t)); +} + +static void writeDouble(PyObject* outbuf, double dub) { +  // Unfortunately, bitwise_cast doesn't work in C.  Bad C! +  union { +    double f; +    int64_t t; +  } transfer; +  transfer.f = dub; +  writeI64(outbuf, transfer.t); +} + + +/* --- MAIN RECURSIVE OUTPUT FUCNTION -- */ + +static int +output_val(PyObject* output, PyObject* value, TType type, PyObject* typeargs) { +  /* +   * Refcounting Strategy: +   * +   * We assume that elements of the thrift_spec tuple are not going to be +   * mutated, so we don't ref count those at all. Other than that, we try to +   * keep a reference to all the user-created objects while we work with them. +   * output_val assumes that a reference is already held. The *caller* is +   * responsible for handling references +   */ + +  switch (type) { + +  case T_BOOL: { +    int v = PyObject_IsTrue(value); +    if (v == -1) { +      return false; +    } + +    writeByte(output, (int8_t) v); +    break; +  } +  case T_I08: { +    int32_t val; + +    if (!parse_pyint(value, &val, INT8_MIN, INT8_MAX)) { +      return false; +    } + +    writeByte(output, (int8_t) val); +    break; +  } +  case T_I16: { +    int32_t val; + +    if (!parse_pyint(value, &val, INT16_MIN, INT16_MAX)) { +      return false; +    } + +    writeI16(output, (int16_t) val); +    break; +  } +  case T_I32: { +    int32_t val; + +    if (!parse_pyint(value, &val, INT32_MIN, INT32_MAX)) { +      return false; +    } + +    writeI32(output, val); +    break; +  } +  case T_I64: { +    int64_t nval = PyLong_AsLongLong(value); + +    if (INT_CONV_ERROR_OCCURRED(nval)) { +      return false; +    } + +    if (!CHECK_RANGE(nval, INT64_MIN, INT64_MAX)) { +      PyErr_SetString(PyExc_OverflowError, "int out of range"); +      return false; +    } + +    writeI64(output, nval); +    break; +  } + +  case T_DOUBLE: { +    double nval = PyFloat_AsDouble(value); +    if (nval == -1.0 && PyErr_Occurred()) { +      return false; +    } + +    writeDouble(output, nval); +    break; +  } + +  case T_STRING: { +    Py_ssize_t len = PyString_Size(value); + +    if (!check_ssize_t_32(len)) { +      return false; +    } + +    writeI32(output, (int32_t) len); +    PycStringIO->cwrite(output, PyString_AsString(value), (int32_t) len); +    break; +  } + +  case T_LIST: +  case T_SET: { +    Py_ssize_t len; +    SetListTypeArgs parsedargs; +    PyObject *item; +    PyObject *iterator; + +    if (!parse_set_list_args(&parsedargs, typeargs)) { +      return false; +    } + +    len = PyObject_Length(value); + +    if (!check_ssize_t_32(len)) { +      return false; +    } + +    writeByte(output, parsedargs.element_type); +    writeI32(output, (int32_t) len); + +    iterator =  PyObject_GetIter(value); +    if (iterator == NULL) { +      return false; +    } + +    while ((item = PyIter_Next(iterator))) { +      if (!output_val(output, item, parsedargs.element_type, parsedargs.typeargs)) { +        Py_DECREF(item); +        Py_DECREF(iterator); +        return false; +      } +      Py_DECREF(item); +    } + +    Py_DECREF(iterator); + +    if (PyErr_Occurred()) { +      return false; +    } + +    break; +  } + +  case T_MAP: { +    PyObject *k, *v; +    Py_ssize_t pos = 0; +    Py_ssize_t len; + +    MapTypeArgs parsedargs; + +    len = PyDict_Size(value); +    if (!check_ssize_t_32(len)) { +      return false; +    } + +    if (!parse_map_args(&parsedargs, typeargs)) { +      return false; +    } + +    writeByte(output, parsedargs.ktag); +    writeByte(output, parsedargs.vtag); +    writeI32(output, len); + +    // TODO(bmaurer): should support any mapping, not just dicts +    while (PyDict_Next(value, &pos, &k, &v)) { +      // TODO(dreiss): Think hard about whether these INCREFs actually +      //               turn any unsafe scenarios into safe scenarios. +      Py_INCREF(k); +      Py_INCREF(v); + +      if (!output_val(output, k, parsedargs.ktag, parsedargs.ktypeargs) +          || !output_val(output, v, parsedargs.vtag, parsedargs.vtypeargs)) { +        Py_DECREF(k); +        Py_DECREF(v); +        return false; +      } +      Py_DECREF(k); +      Py_DECREF(v); +    } +    break; +  } + +  // TODO(dreiss): Consider breaking this out as a function +  //               the way we did for decode_struct. +  case T_STRUCT: { +    StructTypeArgs parsedargs; +    Py_ssize_t nspec; +    Py_ssize_t i; + +    if (!parse_struct_args(&parsedargs, typeargs)) { +      return false; +    } + +    nspec = PyTuple_Size(parsedargs.spec); + +    if (nspec == -1) { +      return false; +    } + +    for (i = 0; i < nspec; i++) { +      StructItemSpec parsedspec; +      PyObject* spec_tuple; +      PyObject* instval = NULL; + +      spec_tuple = PyTuple_GET_ITEM(parsedargs.spec, i); +      if (spec_tuple == Py_None) { +        continue; +      } + +      if (!parse_struct_item_spec (&parsedspec, spec_tuple)) { +        return false; +      } + +      instval = PyObject_GetAttr(value, parsedspec.attrname); + +      if (!instval) { +        return false; +      } + +      if (instval == Py_None) { +        Py_DECREF(instval); +        continue; +      } + +      writeByte(output, (int8_t) parsedspec.type); +      writeI16(output, parsedspec.tag); + +      if (!output_val(output, instval, parsedspec.type, parsedspec.typeargs)) { +        Py_DECREF(instval); +        return false; +      } + +      Py_DECREF(instval); +    } + +    writeByte(output, (int8_t)T_STOP); +    break; +  } + +  case T_STOP: +  case T_VOID: +  case T_UTF16: +  case T_UTF8: +  case T_U64: +  default: +    PyErr_SetString(PyExc_TypeError, "Unexpected TType"); +    return false; + +  } + +  return true; +} + + +/* --- TOP-LEVEL WRAPPER FOR OUTPUT -- */ + +static PyObject * +encode_binary(PyObject *self, PyObject *args) { +  PyObject* enc_obj; +  PyObject* type_args; +  PyObject* buf; +  PyObject* ret = NULL; + +  if (!PyArg_ParseTuple(args, "OO", &enc_obj, &type_args)) { +    return NULL; +  } + +  buf = PycStringIO->NewOutput(INIT_OUTBUF_SIZE); +  if (output_val(buf, enc_obj, T_STRUCT, type_args)) { +    ret = PycStringIO->cgetvalue(buf); +  } + +  Py_DECREF(buf); +  return ret; +} + +/* ====== END WRITING FUNCTIONS ====== */ + + +/* ====== BEGIN READING FUNCTIONS ====== */ + +/* --- LOW-LEVEL READING FUNCTIONS --- */ + +static void +free_decodebuf(DecodeBuffer* d) { +  Py_XDECREF(d->stringiobuf); +  Py_XDECREF(d->refill_callable); +} + +static bool +decode_buffer_from_obj(DecodeBuffer* dest, PyObject* obj) { +  dest->stringiobuf = PyObject_GetAttr(obj, INTERN_STRING(cstringio_buf)); +  if (!dest->stringiobuf) { +    return false; +  } + +  if (!PycStringIO_InputCheck(dest->stringiobuf)) { +    free_decodebuf(dest); +    PyErr_SetString(PyExc_TypeError, "expecting stringio input"); +    return false; +  } + +  dest->refill_callable = PyObject_GetAttr(obj, INTERN_STRING(cstringio_refill)); + +  if(!dest->refill_callable) { +    free_decodebuf(dest); +    return false; +  } + +  if (!PyCallable_Check(dest->refill_callable)) { +    free_decodebuf(dest); +    PyErr_SetString(PyExc_TypeError, "expecting callable"); +    return false; +  } + +  return true; +} + +static bool readBytes(DecodeBuffer* input, char** output, int len) { +  int read; + +  // TODO(dreiss): Don't fear the malloc.  Think about taking a copy of +  //               the partial read instead of forcing the transport +  //               to prepend it to its buffer. + +  read = PycStringIO->cread(input->stringiobuf, output, len); + +  if (read == len) { +    return true; +  } else if (read == -1) { +    return false; +  } else { +    PyObject* newiobuf; + +    // using building functions as this is a rare codepath +    newiobuf = PyObject_CallFunction( +        input->refill_callable, "s#i", *output, read, len, NULL); +    if (newiobuf == NULL) { +      return false; +    } + +    // must do this *AFTER* the call so that we don't deref the io buffer +    Py_CLEAR(input->stringiobuf); +    input->stringiobuf = newiobuf; + +    read = PycStringIO->cread(input->stringiobuf, output, len); + +    if (read == len) { +      return true; +    } else if (read == -1) { +      return false; +    } else { +      // TODO(dreiss): This could be a valid code path for big binary blobs. +      PyErr_SetString(PyExc_TypeError, +          "refill claimed to have refilled the buffer, but didn't!!"); +      return false; +    } +  } +} + +static int8_t readByte(DecodeBuffer* input) { +  char* buf; +  if (!readBytes(input, &buf, sizeof(int8_t))) { +    return -1; +  } + +  return *(int8_t*) buf; +} + +static int16_t readI16(DecodeBuffer* input) { +  char* buf; +  if (!readBytes(input, &buf, sizeof(int16_t))) { +    return -1; +  } + +  return (int16_t) ntohs(*(int16_t*) buf); +} + +static int32_t readI32(DecodeBuffer* input) { +  char* buf; +  if (!readBytes(input, &buf, sizeof(int32_t))) { +    return -1; +  } +  return (int32_t) ntohl(*(int32_t*) buf); +} + + +static int64_t readI64(DecodeBuffer* input) { +  char* buf; +  if (!readBytes(input, &buf, sizeof(int64_t))) { +    return -1; +  } + +  return (int64_t) ntohll(*(int64_t*) buf); +} + +static double readDouble(DecodeBuffer* input) { +  union { +    int64_t f; +    double t; +  } transfer; + +  transfer.f = readI64(input); +  if (transfer.f == -1) { +    return -1; +  } +  return transfer.t; +} + +static bool +checkTypeByte(DecodeBuffer* input, TType expected) { +  TType got = readByte(input); +  if (INT_CONV_ERROR_OCCURRED(got)) { +    return false; +  } + +  if (expected != got) { +    PyErr_SetString(PyExc_TypeError, "got wrong ttype while reading field"); +    return false; +  } +  return true; +} + +static bool +skip(DecodeBuffer* input, TType type) { +#define SKIPBYTES(n) \ +  do { \ +    if (!readBytes(input, &dummy_buf, (n))) { \ +      return false; \ +    } \ +  } while(0) + +  char* dummy_buf; + +  switch (type) { + +  case T_BOOL: +  case T_I08: SKIPBYTES(1); break; +  case T_I16: SKIPBYTES(2); break; +  case T_I32: SKIPBYTES(4); break; +  case T_I64: +  case T_DOUBLE: SKIPBYTES(8); break; + +  case T_STRING: { +    // TODO(dreiss): Find out if these check_ssize_t32s are really necessary. +    int len = readI32(input); +    if (!check_ssize_t_32(len)) { +      return false; +    } +    SKIPBYTES(len); +    break; +  } + +  case T_LIST: +  case T_SET: { +    TType etype; +    int len, i; + +    etype = readByte(input); +    if (etype == -1) { +      return false; +    } + +    len = readI32(input); +    if (!check_ssize_t_32(len)) { +      return false; +    } + +    for (i = 0; i < len; i++) { +      if (!skip(input, etype)) { +        return false; +      } +    } +    break; +  } + +  case T_MAP: { +    TType ktype, vtype; +    int len, i; + +    ktype = readByte(input); +    if (ktype == -1) { +      return false; +    } + +    vtype = readByte(input); +    if (vtype == -1) { +      return false; +    } + +    len = readI32(input); +    if (!check_ssize_t_32(len)) { +      return false; +    } + +    for (i = 0; i < len; i++) { +      if (!(skip(input, ktype) && skip(input, vtype))) { +        return false; +      } +    } +    break; +  } + +  case T_STRUCT: { +    while (true) { +      TType type; + +      type = readByte(input); +      if (type == -1) { +        return false; +      } + +      if (type == T_STOP) +        break; + +      SKIPBYTES(2); // tag +      if (!skip(input, type)) { +        return false; +      } +    } +    break; +  } + +  case T_STOP: +  case T_VOID: +  case T_UTF16: +  case T_UTF8: +  case T_U64: +  default: +    PyErr_SetString(PyExc_TypeError, "Unexpected TType"); +    return false; + +  } + +  return true; + +#undef SKIPBYTES +} + + +/* --- HELPER FUNCTION FOR DECODE_VAL --- */ + +static PyObject* +decode_val(DecodeBuffer* input, TType type, PyObject* typeargs); + +static bool +decode_struct(DecodeBuffer* input, PyObject* output, PyObject* spec_seq) { +  int spec_seq_len = PyTuple_Size(spec_seq); +  if (spec_seq_len == -1) { +    return false; +  } + +  while (true) { +    TType type; +    int16_t tag; +    PyObject* item_spec; +    PyObject* fieldval = NULL; +    StructItemSpec parsedspec; + +    type = readByte(input); +    if (type == -1) { +      return false; +    } +    if (type == T_STOP) { +      break; +    } +    tag = readI16(input); +    if (INT_CONV_ERROR_OCCURRED(tag)) { +      return false; +    } +    if (tag >= 0 && tag < spec_seq_len) { +      item_spec = PyTuple_GET_ITEM(spec_seq, tag); +    } else { +      item_spec = Py_None; +    } + +    if (item_spec == Py_None) { +      if (!skip(input, type)) { +        return false; +      } else { +        continue; +      } +    } + +    if (!parse_struct_item_spec(&parsedspec, item_spec)) { +      return false; +    } +    if (parsedspec.type != type) { +      if (!skip(input, type)) { +        PyErr_SetString(PyExc_TypeError, "struct field had wrong type while reading and can't be skipped"); +        return false; +      } else { +        continue; +      } +    } + +    fieldval = decode_val(input, parsedspec.type, parsedspec.typeargs); +    if (fieldval == NULL) { +      return false; +    } + +    if (PyObject_SetAttr(output, parsedspec.attrname, fieldval) == -1) { +      Py_DECREF(fieldval); +      return false; +    } +    Py_DECREF(fieldval); +  } +  return true; +} + + +/* --- MAIN RECURSIVE INPUT FUCNTION --- */ + +// Returns a new reference. +static PyObject* +decode_val(DecodeBuffer* input, TType type, PyObject* typeargs) { +  switch (type) { + +  case T_BOOL: { +    int8_t v = readByte(input); +    if (INT_CONV_ERROR_OCCURRED(v)) { +      return NULL; +    } + +    switch (v) { +    case 0: Py_RETURN_FALSE; +    case 1: Py_RETURN_TRUE; +    // Don't laugh.  This is a potentially serious issue. +    default: PyErr_SetString(PyExc_TypeError, "boolean out of range"); return NULL; +    } +    break; +  } +  case T_I08: { +    int8_t v = readByte(input); +    if (INT_CONV_ERROR_OCCURRED(v)) { +      return NULL; +    } + +    return PyInt_FromLong(v); +  } +  case T_I16: { +    int16_t v = readI16(input); +    if (INT_CONV_ERROR_OCCURRED(v)) { +      return NULL; +    } +    return PyInt_FromLong(v); +  } +  case T_I32: { +    int32_t v = readI32(input); +    if (INT_CONV_ERROR_OCCURRED(v)) { +      return NULL; +    } +    return PyInt_FromLong(v); +  } + +  case T_I64: { +    int64_t v = readI64(input); +    if (INT_CONV_ERROR_OCCURRED(v)) { +      return NULL; +    } +    // TODO(dreiss): Find out if we can take this fastpath always when +    //               sizeof(long) == sizeof(long long). +    if (CHECK_RANGE(v, LONG_MIN, LONG_MAX)) { +      return PyInt_FromLong((long) v); +    } + +    return PyLong_FromLongLong(v); +  } + +  case T_DOUBLE: { +    double v = readDouble(input); +    if (v == -1.0 && PyErr_Occurred()) { +      return false; +    } +    return PyFloat_FromDouble(v); +  } + +  case T_STRING: { +    Py_ssize_t len = readI32(input); +    char* buf; +    if (!readBytes(input, &buf, len)) { +      return NULL; +    } + +    return PyString_FromStringAndSize(buf, len); +  } + +  case T_LIST: +  case T_SET: { +    SetListTypeArgs parsedargs; +    int32_t len; +    PyObject* ret = NULL; +    int i; + +    if (!parse_set_list_args(&parsedargs, typeargs)) { +      return NULL; +    } + +    if (!checkTypeByte(input, parsedargs.element_type)) { +      return NULL; +    } + +    len = readI32(input); +    if (!check_ssize_t_32(len)) { +      return NULL; +    } + +    ret = PyList_New(len); +    if (!ret) { +      return NULL; +    } + +    for (i = 0; i < len; i++) { +      PyObject* item = decode_val(input, parsedargs.element_type, parsedargs.typeargs); +      if (!item) { +        Py_DECREF(ret); +        return NULL; +      } +      PyList_SET_ITEM(ret, i, item); +    } + +    // TODO(dreiss): Consider biting the bullet and making two separate cases +    //               for list and set, avoiding this post facto conversion. +    if (type == T_SET) { +      PyObject* setret; +#if (PY_VERSION_HEX < 0x02050000) +      // hack needed for older versions +      setret = PyObject_CallFunctionObjArgs((PyObject*)&PySet_Type, ret, NULL); +#else +      // official version +      setret = PySet_New(ret); +#endif +      Py_DECREF(ret); +      return setret; +    } +    return ret; +  } + +  case T_MAP: { +    int32_t len; +    int i; +    MapTypeArgs parsedargs; +    PyObject* ret = NULL; + +    if (!parse_map_args(&parsedargs, typeargs)) { +      return NULL; +    } + +    if (!checkTypeByte(input, parsedargs.ktag)) { +      return NULL; +    } +    if (!checkTypeByte(input, parsedargs.vtag)) { +      return NULL; +    } + +    len = readI32(input); +    if (!check_ssize_t_32(len)) { +      return false; +    } + +    ret = PyDict_New(); +    if (!ret) { +      goto error; +    } + +    for (i = 0; i < len; i++) { +      PyObject* k = NULL; +      PyObject* v = NULL; +      k = decode_val(input, parsedargs.ktag, parsedargs.ktypeargs); +      if (k == NULL) { +        goto loop_error; +      } +      v = decode_val(input, parsedargs.vtag, parsedargs.vtypeargs); +      if (v == NULL) { +        goto loop_error; +      } +      if (PyDict_SetItem(ret, k, v) == -1) { +        goto loop_error; +      } + +      Py_DECREF(k); +      Py_DECREF(v); +      continue; + +      // Yuck!  Destructors, anyone? +      loop_error: +      Py_XDECREF(k); +      Py_XDECREF(v); +      goto error; +    } + +    return ret; + +    error: +    Py_XDECREF(ret); +    return NULL; +  } + +  case T_STRUCT: { +    StructTypeArgs parsedargs; +    if (!parse_struct_args(&parsedargs, typeargs)) { +      return NULL; +    } + +    PyObject* ret = PyObject_CallObject(parsedargs.klass, NULL); +    if (!ret) { +      return NULL; +    } + +    if (!decode_struct(input, ret, parsedargs.spec)) { +      Py_DECREF(ret); +      return NULL; +    } + +    return ret; +  } + +  case T_STOP: +  case T_VOID: +  case T_UTF16: +  case T_UTF8: +  case T_U64: +  default: +    PyErr_SetString(PyExc_TypeError, "Unexpected TType"); +    return NULL; +  } +} + + +/* --- TOP-LEVEL WRAPPER FOR INPUT -- */ + +static PyObject* +decode_binary(PyObject *self, PyObject *args) { +  PyObject* output_obj = NULL; +  PyObject* transport = NULL; +  PyObject* typeargs = NULL; +  StructTypeArgs parsedargs; +  DecodeBuffer input = {}; + +  if (!PyArg_ParseTuple(args, "OOO", &output_obj, &transport, &typeargs)) { +    return NULL; +  } + +  if (!parse_struct_args(&parsedargs, typeargs)) { +    return NULL; +  } + +  if (!decode_buffer_from_obj(&input, transport)) { +    return NULL; +  } + +  if (!decode_struct(&input, output_obj, parsedargs.spec)) { +    free_decodebuf(&input); +    return NULL; +  } + +  free_decodebuf(&input); + +  Py_RETURN_NONE; +} + +/* ====== END READING FUNCTIONS ====== */ + + +/* -- PYTHON MODULE SETUP STUFF --- */ + +static PyMethodDef ThriftFastBinaryMethods[] = { + +  {"encode_binary",  encode_binary, METH_VARARGS, ""}, +  {"decode_binary",  decode_binary, METH_VARARGS, ""}, + +  {NULL, NULL, 0, NULL}        /* Sentinel */ +}; + +PyMODINIT_FUNC +initfastbinary(void) { +#define INIT_INTERN_STRING(value) \ +  do { \ +    INTERN_STRING(value) = PyString_InternFromString(#value); \ +    if(!INTERN_STRING(value)) return; \ +  } while(0) + +  INIT_INTERN_STRING(cstringio_buf); +  INIT_INTERN_STRING(cstringio_refill); +#undef INIT_INTERN_STRING + +  PycString_IMPORT; +  if (PycStringIO == NULL) return; + +  (void) Py_InitModule("thrift.protocol.fastbinary", ThriftFastBinaryMethods); +} | 
