blob: b3e9fbcc7a7eaef197fcff4469f1b59ab3a77db6 [file] [log] [blame]
// Copyright (c) 2006- Facebook
// Distributed under the Thrift Software License
//
// See accompanying file LICENSE or visit the Thrift site at:
// http://developers.facebook.com/thrift/
#include "TBinaryProtocol.h"
#include <boost/static_assert.hpp>
using std::string;
// Use this to get around strict aliasing rules.
// For example, uint64_t i = bitwise_cast<uint64_t>(returns_double());
// The most obvious implementation is to just cast a pointer,
// but that doesn't work.
// For a pretty in-depth explanation of the problem, see
// http://www.cellperformance.com/mike_acton/2006/06/ (...)
// understanding_strict_aliasing.html
template <typename To, typename From>
static inline To bitwise_cast(From from) {
BOOST_STATIC_ASSERT(sizeof(From) == sizeof(To));
// BAD!!! These are all broken with -O2.
//return *reinterpret_cast<To*>(&from); // BAD!!!
//return *static_cast<To*>(static_cast<void*>(&from)); // BAD!!!
//return *(To*)(void*)&from; // BAD!!!
// Super clean and paritally blessed by section 3.9 of the standard.
//unsigned char c[sizeof(from)];
//memcpy(c, &from, sizeof(from));
//To to;
//memcpy(&to, c, sizeof(c));
//return to;
// Slightly more questionable.
// Same code emitted by GCC.
//To to;
//memcpy(&to, &from, sizeof(from));
//return to;
// Technically undefined, but almost universally supported,
// and the most efficient implementation.
union {
From f;
To t;
} u;
u.f = from;
return u.t;
}
namespace facebook { namespace thrift { namespace protocol {
uint32_t TBinaryProtocol::writeMessageBegin(const std::string& name,
const TMessageType messageType,
const int32_t seqid) {
if (strict_write_) {
int32_t version = (VERSION_1) | ((int32_t)messageType);
uint32_t wsize = 0;
wsize += writeI32(version);
wsize += writeString(name);
wsize += writeI32(seqid);
return wsize;
} else {
uint32_t wsize = 0;
wsize += writeString(name);
wsize += writeByte((int8_t)messageType);
wsize += writeI32(seqid);
return wsize;
}
}
uint32_t TBinaryProtocol::writeMessageEnd() {
return 0;
}
uint32_t TBinaryProtocol::writeStructBegin(const string& name) {
return 0;
}
uint32_t TBinaryProtocol::writeStructEnd() {
return 0;
}
uint32_t TBinaryProtocol::writeFieldBegin(const string& name,
const TType fieldType,
const int16_t fieldId) {
uint32_t wsize = 0;
wsize += writeByte((int8_t)fieldType);
wsize += writeI16(fieldId);
return wsize;
}
uint32_t TBinaryProtocol::writeFieldEnd() {
return 0;
}
uint32_t TBinaryProtocol::writeFieldStop() {
return
writeByte((int8_t)T_STOP);
}
uint32_t TBinaryProtocol::writeMapBegin(const TType keyType,
const TType valType,
const uint32_t size) {
uint32_t wsize = 0;
wsize += writeByte((int8_t)keyType);
wsize += writeByte((int8_t)valType);
wsize += writeI32((int32_t)size);
return wsize;
}
uint32_t TBinaryProtocol::writeMapEnd() {
return 0;
}
uint32_t TBinaryProtocol::writeListBegin(const TType elemType,
const uint32_t size) {
uint32_t wsize = 0;
wsize += writeByte((int8_t) elemType);
wsize += writeI32((int32_t)size);
return wsize;
}
uint32_t TBinaryProtocol::writeListEnd() {
return 0;
}
uint32_t TBinaryProtocol::writeSetBegin(const TType elemType,
const uint32_t size) {
uint32_t wsize = 0;
wsize += writeByte((int8_t)elemType);
wsize += writeI32((int32_t)size);
return wsize;
}
uint32_t TBinaryProtocol::writeSetEnd() {
return 0;
}
uint32_t TBinaryProtocol::writeBool(const bool value) {
uint8_t tmp = value ? 1 : 0;
trans_->write(&tmp, 1);
return 1;
}
uint32_t TBinaryProtocol::writeByte(const int8_t byte) {
trans_->write((uint8_t*)&byte, 1);
return 1;
}
uint32_t TBinaryProtocol::writeI16(const int16_t i16) {
int16_t net = (int16_t)htons(i16);
trans_->write((uint8_t*)&net, 2);
return 2;
}
uint32_t TBinaryProtocol::writeI32(const int32_t i32) {
int32_t net = (int32_t)htonl(i32);
trans_->write((uint8_t*)&net, 4);
return 4;
}
uint32_t TBinaryProtocol::writeI64(const int64_t i64) {
int64_t net = (int64_t)htonll(i64);
trans_->write((uint8_t*)&net, 8);
return 8;
}
uint32_t TBinaryProtocol::writeDouble(const double dub) {
BOOST_STATIC_ASSERT(sizeof(double) == sizeof(uint64_t));
BOOST_STATIC_ASSERT(std::numeric_limits<double>::is_iec559);
uint64_t bits = bitwise_cast<uint64_t>(dub);
bits = htonll(bits);
trans_->write((uint8_t*)&bits, 8);
return 8;
}
uint32_t TBinaryProtocol::writeString(const string& str) {
uint32_t size = str.size();
uint32_t result = writeI32((int32_t)size);
if (size > 0) {
trans_->write((uint8_t*)str.data(), size);
}
return result + size;
}
uint32_t TBinaryProtocol::writeBinary(const string& str) {
return TBinaryProtocol::writeString(str);
}
/**
* Reading functions
*/
uint32_t TBinaryProtocol::readMessageBegin(std::string& name,
TMessageType& messageType,
int32_t& seqid) {
uint32_t result = 0;
int32_t sz;
result += readI32(sz);
if (sz < 0) {
// Check for correct version number
int32_t version = sz & VERSION_MASK;
if (version != VERSION_1) {
throw TProtocolException(TProtocolException::BAD_VERSION, "Bad version identifier");
}
messageType = (TMessageType)(sz & 0x000000ff);
result += readString(name);
result += readI32(seqid);
} else {
if (strict_read_) {
throw TProtocolException(TProtocolException::BAD_VERSION, "No version identifier... old protocol client in strict mode?");
} else {
// Handle pre-versioned input
int8_t type;
result += readStringBody(name, sz);
result += readByte(type);
messageType = (TMessageType)type;
result += readI32(seqid);
}
}
return result;
}
uint32_t TBinaryProtocol::readMessageEnd() {
return 0;
}
uint32_t TBinaryProtocol::readStructBegin(string& name) {
name = "";
return 0;
}
uint32_t TBinaryProtocol::readStructEnd() {
return 0;
}
uint32_t TBinaryProtocol::readFieldBegin(string& name,
TType& fieldType,
int16_t& fieldId) {
uint32_t result = 0;
int8_t type;
result += readByte(type);
fieldType = (TType)type;
if (fieldType == T_STOP) {
fieldId = 0;
return result;
}
result += readI16(fieldId);
return result;
}
uint32_t TBinaryProtocol::readFieldEnd() {
return 0;
}
uint32_t TBinaryProtocol::readMapBegin(TType& keyType,
TType& valType,
uint32_t& size) {
int8_t k, v;
uint32_t result = 0;
int32_t sizei;
result += readByte(k);
keyType = (TType)k;
result += readByte(v);
valType = (TType)v;
result += readI32(sizei);
if (sizei < 0) {
throw TProtocolException(TProtocolException::NEGATIVE_SIZE);
} else if (container_limit_ && sizei > container_limit_) {
throw TProtocolException(TProtocolException::SIZE_LIMIT);
}
size = (uint32_t)sizei;
return result;
}
uint32_t TBinaryProtocol::readMapEnd() {
return 0;
}
uint32_t TBinaryProtocol::readListBegin(TType& elemType,
uint32_t& size) {
int8_t e;
uint32_t result = 0;
int32_t sizei;
result += readByte(e);
elemType = (TType)e;
result += readI32(sizei);
if (sizei < 0) {
throw TProtocolException(TProtocolException::NEGATIVE_SIZE);
} else if (container_limit_ && sizei > container_limit_) {
throw TProtocolException(TProtocolException::SIZE_LIMIT);
}
size = (uint32_t)sizei;
return result;
}
uint32_t TBinaryProtocol::readListEnd() {
return 0;
}
uint32_t TBinaryProtocol::readSetBegin(TType& elemType,
uint32_t& size) {
int8_t e;
uint32_t result = 0;
int32_t sizei;
result += readByte(e);
elemType = (TType)e;
result += readI32(sizei);
if (sizei < 0) {
throw TProtocolException(TProtocolException::NEGATIVE_SIZE);
} else if (container_limit_ && sizei > container_limit_) {
throw TProtocolException(TProtocolException::SIZE_LIMIT);
}
size = (uint32_t)sizei;
return result;
}
uint32_t TBinaryProtocol::readSetEnd() {
return 0;
}
uint32_t TBinaryProtocol::readBool(bool& value) {
uint8_t b[1];
trans_->readAll(b, 1);
value = *(int8_t*)b != 0;
return 1;
}
uint32_t TBinaryProtocol::readByte(int8_t& byte) {
uint8_t b[1];
trans_->readAll(b, 1);
byte = *(int8_t*)b;
return 1;
}
uint32_t TBinaryProtocol::readI16(int16_t& i16) {
uint8_t b[2];
trans_->readAll(b, 2);
i16 = *(int16_t*)b;
i16 = (int16_t)ntohs(i16);
return 2;
}
uint32_t TBinaryProtocol::readI32(int32_t& i32) {
uint8_t b[4];
trans_->readAll(b, 4);
i32 = *(int32_t*)b;
i32 = (int32_t)ntohl(i32);
return 4;
}
uint32_t TBinaryProtocol::readI64(int64_t& i64) {
uint8_t b[8];
trans_->readAll(b, 8);
i64 = *(int64_t*)b;
i64 = (int64_t)ntohll(i64);
return 8;
}
uint32_t TBinaryProtocol::readDouble(double& dub) {
BOOST_STATIC_ASSERT(sizeof(double) == sizeof(uint64_t));
BOOST_STATIC_ASSERT(std::numeric_limits<double>::is_iec559);
uint64_t bits;
uint8_t b[8];
trans_->readAll(b, 8);
bits = *(uint64_t*)b;
bits = ntohll(bits);
dub = bitwise_cast<double>(bits);
return 8;
}
uint32_t TBinaryProtocol::readString(string& str) {
uint32_t result;
int32_t size;
result = readI32(size);
return result + readStringBody(str, size);
}
uint32_t TBinaryProtocol::readBinary(string& str) {
return TBinaryProtocol::readString(str);
}
uint32_t TBinaryProtocol::readStringBody(string& str, int32_t size) {
uint32_t result = 0;
// Catch error cases
if (size < 0) {
throw TProtocolException(TProtocolException::NEGATIVE_SIZE);
}
if (string_limit_ > 0 && size > string_limit_) {
throw TProtocolException(TProtocolException::SIZE_LIMIT);
}
// Catch empty string case
if (size == 0) {
str = "";
return result;
}
// Use the heap here to prevent stack overflow for v. large strings
if (size > string_buf_size_ || string_buf_ == NULL) {
string_buf_ = (uint8_t*)std::realloc(string_buf_, (uint32_t)size);
if (string_buf_ == NULL) {
string_buf_size_ = 0;
throw TProtocolException(TProtocolException::UNKNOWN, "Out of memory in TBinaryProtocol::readString");
}
string_buf_size_ = size;
}
trans_->readAll(string_buf_, size);
str = string((char*)string_buf_, size);
return (uint32_t)size;
}
}}} // facebook::thrift::protocol