// TCompactProtocol: the same messages in fewer bytes. Field ids travel as a delta from
// the previous field when the jump fits in four bits, integers are zigzag varints, a
// boolean's value rides in its field header, and a double is little-endian — the one
// place this protocol and TBinaryProtocol disagree on byte order.
///|
let compact_protocol_id : Int32 = 0x82
///|
let compact_version : Int32 = 1
///|
let compact_true : Int32 = 0x01
///|
let compact_false : Int32 = 0x02
///|
let compact_byte : Int32 = 0x03
///|
let compact_i16 : Int32 = 0x04
///|
let compact_i32 : Int32 = 0x05
///|
let compact_i64 : Int32 = 0x06
///|
let compact_double : Int32 = 0x07
///|
let compact_binary : Int32 = 0x08
///|
let compact_list : Int32 = 0x09
///|
let compact_set : Int32 = 0x0A
///|
let compact_map : Int32 = 0x0B
///|
let compact_struct : Int32 = 0x0C
///|
fn compact_of(type_ : Int32) -> Int32 {
match type_ {
t if t == type_bool => compact_true
t if t == type_byte => compact_byte
t if t == type_i16 => compact_i16
t if t == type_i32 => compact_i32
t if t == type_i64 => compact_i64
t if t == type_double => compact_double
t if t == type_string => compact_binary
t if t == type_list => compact_list
t if t == type_set => compact_set
t if t == type_map => compact_map
t if t == type_struct => compact_struct
_ => type_stop
}
}
///|
fn type_of(compact : Int32) -> Int32 raise ThriftError {
match compact & 0x0F {
0x00 => type_stop
0x01 | 0x02 => type_bool
0x03 => type_byte
0x04 => type_i16
0x05 => type_i32
0x06 => type_i64
0x07 => type_double
0x08 => type_string
0x09 => type_list
0x0A => type_set
0x0B => type_map
0x0C => type_struct
other => raise BadType(other)
}
}
///|
/// A compact-protocol reader and writer over one message.
pub struct Compact {
mut data : Bytes
mut off : Int32
mut out : @buffer.Buffer
// The field id a delta counts from, saved and restored around a struct.
mut last_field : Int32
mut field_stack : Array[Int32]
// A boolean field carries its value in the header, so the header is written when the
// value is, and the value is remembered when the header is read.
mut pending_field : Bool
mut pending_id : Int32
mut bool_value : Bool
mut bool_ready : Bool
}
///|
pub fn Compact::new(input : Bytes) -> Compact {
{
data: input,
off: 0,
out: @buffer.Buffer(),
last_field: 0,
field_stack: [],
pending_field: false,
pending_id: 0,
bool_value: false,
bool_ready: false,
}
}
///|
fn Compact::need(self : Compact, n : Int32) -> Unit raise ThriftError {
if n < 0 || self.off + n > self.data.length() {
raise Truncated
}
}
///|
fn Compact::read_raw_byte(self : Compact) -> Int32 raise ThriftError {
self.need(1)
let value = self.data[self.off].to_int()
self.off = self.off + 1
value
}
///|
fn Compact::write_varint(self : Compact, value : Int64) -> Unit {
// The value is non-negative, so an arithmetic shift here is a logical one.
let mut v = value
while v >= 0x80L {
self.write_byte((v & 0x7FL).to_int() | 0x80)
v = v >> 7
}
self.write_byte(v.to_int())
}
///|
fn Compact::read_varint(self : Compact) -> Int64 raise ThriftError {
let mut result = 0L
let mut shift = 0
for _ in 0..<10 {
let b = self.read_raw_byte()
result = result | ((b & 0x7F).to_int64() << shift)
if (b & 0x80) == 0 {
return result
}
shift = shift + 7
}
raise BadType(-1)
}
///|
fn zigzag32(value : Int32) -> Int64 {
let wide = value.to_int64()
(wide << 1) ^ (wide >> 63)
}
///|
fn unzigzag(value : Int64) -> Int32 {
let magnitude = (value >> 1) ^ -(value & 1L)
to_i32(magnitude)
}
///|
fn to_i32(value : Int64) -> Int32 {
let bits = value & 0xFFFFFFFFL
if bits >= 0x80000000L {
(bits - 0x100000000L).to_int()
} else {
bits.to_int()
}
}
///|
fn Compact::write_field_header(
self : Compact,
compact_type : Int32,
id : Int32,
) -> Unit {
let delta = id - self.last_field
if delta > 0 && delta <= 15 {
self.write_byte((delta << 4) | compact_type)
} else {
self.write_byte(compact_type)
self.write_varint(zigzag32(id))
}
self.last_field = id
}
///|
///|
pub impl Protocol for Compact with fn reset(self, input) {
self.data = input
self.off = 0
self.out = @buffer.Buffer()
self.last_field = 0
self.field_stack = []
self.pending_field = false
self.pending_id = 0
self.bool_value = false
self.bool_ready = false
}
///|
pub impl Protocol for Compact with fn output(self) {
self.out.to_bytes()
}
///|
pub impl Protocol for Compact with fn write_byte(self, value) {
self.out.write_byte((value & 0xFF).to_byte())
}
///|
pub impl Protocol for Compact with fn read_byte(self) {
self.read_raw_byte()
}
///|
pub impl Protocol for Compact with fn write_i16(self, value) {
self.write_varint(zigzag32(value))
}
///|
pub impl Protocol for Compact with fn read_i16(self) {
unzigzag(self.read_varint())
}
///|
pub impl Protocol for Compact with fn write_i32(self, value) {
self.write_varint(zigzag32(value))
}
///|
pub impl Protocol for Compact with fn read_i32(self) {
unzigzag(self.read_varint())
}
///|
pub impl Protocol for Compact with fn write_i64(self, value) {
let zz = (value << 1) ^ (value >> 63)
self.write_varint(zz)
}
///|
pub impl Protocol for Compact with fn read_i64(self) {
let zz = self.read_varint()
(zz >> 1) ^ -(zz & 1L)
}
///|
pub impl Protocol for Compact with fn write_bool(self, value) {
let compact_type = if value { compact_true } else { compact_false }
if self.pending_field {
self.pending_field = false
self.write_field_header(compact_type, self.pending_id)
} else {
self.write_byte(compact_type)
}
}
///|
pub impl Protocol for Compact with fn read_bool(self) {
if self.bool_ready {
self.bool_ready = false
return self.bool_value
}
self.read_raw_byte() == compact_true
}
// Eight bytes little-endian, where TBinaryProtocol writes them big-endian.
///|
pub impl Protocol for Compact with fn write_double(self, value) {
let bits = value.reinterpret_as_uint64().to_be_bytes()
for i in 0..<8 {
self.write_byte(bits[7 - i].to_int())
}
}
///|
pub impl Protocol for Compact with fn read_double(self) {
let mut bits = 0L
for i in 0..<8 {
bits = bits | (self.read_raw_byte().to_int64() << (8 * i))
}
bits.reinterpret_as_double()
}
///|
pub impl Protocol for Compact with fn write_string(self, value) {
let body = @utf8.encode(value)
self.write_varint(body.length().to_int64())
self.out.write_bytes(body)
}
///|
pub impl Protocol for Compact with fn read_string(self) {
let n = self.read_varint().to_int()
if n < 0 {
raise Truncated
}
self.need(n)
let body = self.data[self.off:self.off + n].to_owned()
self.off = self.off + n
@utf8.decode_lossy(body[:])
}
///|
pub impl Protocol for Compact with fn write_bytes(self, body) {
self.write_varint(body.length().to_int64())
self.out.write_bytes(body)
}
///|
pub impl Protocol for Compact with fn read_bytes(self) {
let n = self.read_varint().to_int()
if n < 0 {
raise Truncated
}
self.need(n)
let body = self.data[self.off:self.off + n].to_owned()
self.off = self.off + n
body
}
///|
pub impl Protocol for Compact with fn write_field_begin(self, type_, id) {
if type_ == type_bool {
self.pending_field = true
self.pending_id = id
} else {
self.write_field_header(compact_of(type_), id)
}
}
///|
pub impl Protocol for Compact with fn read_field_begin(self) {
let header = self.read_raw_byte()
if (header & 0x0F) == type_stop {
return (type_stop, 0)
}
let modifier = (header >> 4) & 0x0F
let id = if modifier == 0 {
unzigzag(self.read_varint())
} else {
self.last_field + modifier
}
let compact_type = header & 0x0F
if compact_type == compact_true || compact_type == compact_false {
self.bool_ready = true
self.bool_value = compact_type == compact_true
}
self.last_field = id
(type_of(compact_type), id)
}
///|
pub impl Protocol for Compact with fn write_field_stop(self) {
self.write_byte(type_stop)
}
///|
pub impl Protocol for Compact with fn write_message_begin(
self,
name,
kind,
seqid,
) {
self.write_byte(compact_protocol_id)
self.write_byte((compact_version & 0x1F) | ((kind << 5) & 0xE0))
self.write_varint(seqid.to_int64() & 0xFFFFFFFFL)
self.write_string(name)
}
///|
pub impl Protocol for Compact with fn read_message_begin(self) {
let id = self.read_raw_byte()
if id != compact_protocol_id {
raise BadVersion(id)
}
let version_and_kind = self.read_raw_byte()
let version = version_and_kind & 0x1F
if version != compact_version {
raise BadVersion(version_and_kind)
}
let kind = (version_and_kind >> 5) & 0x07
let seqid = to_i32(self.read_varint())
let name = self.read_string()
(name, kind, seqid)
}
///|
pub impl Protocol for Compact with fn write_list_begin(self, elem, size) {
let compact_type = compact_of(elem)
if size <= 14 {
self.write_byte((size << 4) | compact_type)
} else {
self.write_byte(0xF0 | compact_type)
self.write_varint(size.to_int64() & 0xFFFFFFFFL)
}
}
///|
pub impl Protocol for Compact with fn read_list_begin(self) {
let header = self.read_raw_byte()
let mut size = (header >> 4) & 0x0F
if size == 15 {
size = self.read_varint().to_int()
}
(type_of(header & 0x0F), size)
}
///|
pub impl Protocol for Compact with fn write_map_begin(self, key, value, size) {
if size == 0 {
self.write_byte(0)
return
}
self.write_varint(size.to_int64() & 0xFFFFFFFFL)
self.write_byte((compact_of(key) << 4) | compact_of(value))
}
///|
pub impl Protocol for Compact with fn read_map_begin(self) {
let size = self.read_varint().to_int()
if size == 0 {
return (type_stop, type_stop, 0)
}
let header = self.read_raw_byte()
(type_of((header >> 4) & 0x0F), type_of(header & 0x0F), size)
}
// A struct's fields restart the delta at zero, and the enclosing struct's last field
// id is restored afterwards.
///|
pub impl Protocol for Compact with fn read_struct_begin(self) {
self.field_stack.push(self.last_field)
self.last_field = 0
}
///|
pub impl Protocol for Compact with fn read_struct_end(self) {
self.last_field = self.field_stack.pop().unwrap_or(0)
}
///|
pub impl Protocol for Compact with fn write_struct_begin(self, name) {
ignore(name)
self.field_stack.push(self.last_field)
self.last_field = 0
}
///|
pub impl Protocol for Compact with fn write_struct_end(self) {
self.last_field = self.field_stack.pop().unwrap_or(0)
}
///|
pub extend Compact with Protocol::{
reset,
output,
read_bytes,
write_bytes,
read_message_begin,
write_message_begin,
read_struct_begin,
read_struct_end,
write_struct_begin,
write_struct_end,
read_field_begin,
write_field_begin,
write_field_stop,
read_bool,
read_byte,
read_i16,
read_i32,
read_i64,
read_double,
read_string,
read_list_begin,
read_map_begin,
write_bool,
write_byte,
write_i16,
write_i32,
write_i64,
write_double,
write_string,
write_list_begin,
write_map_begin,
}