2015-06-21 22:42:01 -07:00
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
/* Copyright (c) FIRST 2015. All Rights Reserved. */
|
|
|
|
|
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
|
|
|
|
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
|
|
|
|
/* the project. */
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
2015-06-25 23:12:49 -07:00
|
|
|
#include "WireEncoder.h"
|
2015-06-21 22:42:01 -07:00
|
|
|
|
|
|
|
|
#include <cassert>
|
|
|
|
|
#include <cstdint>
|
|
|
|
|
#include <cstdlib>
|
|
|
|
|
#include <cstring>
|
|
|
|
|
|
2015-06-25 23:12:49 -07:00
|
|
|
#include "leb128.h"
|
2015-06-21 22:42:01 -07:00
|
|
|
|
2015-06-25 22:57:43 -07:00
|
|
|
using namespace ntimpl;
|
2015-06-21 22:42:01 -07:00
|
|
|
|
2015-06-25 22:57:43 -07:00
|
|
|
WireEncoder::WireEncoder(unsigned int proto_rev) {
|
2015-06-26 01:23:36 -07:00
|
|
|
// Start with a 1024-byte buffer. Use malloc instead of new so we can
|
|
|
|
|
// realloc.
|
|
|
|
|
m_start = m_cur = static_cast<char*>(std::malloc(1024));
|
2015-06-25 22:57:43 -07:00
|
|
|
m_end = m_start + 1024;
|
|
|
|
|
m_proto_rev = proto_rev;
|
|
|
|
|
m_error = nullptr;
|
2015-06-21 22:42:01 -07:00
|
|
|
}
|
|
|
|
|
|
2015-06-25 22:57:43 -07:00
|
|
|
WireEncoder::~WireEncoder() { std::free(m_start); }
|
|
|
|
|
|
|
|
|
|
void WireEncoder::WriteDouble(double val) {
|
|
|
|
|
Reserve(8);
|
2015-06-26 01:23:36 -07:00
|
|
|
// The highest performance way to do this, albeit non-portable.
|
2015-07-04 10:48:47 -07:00
|
|
|
std::uint64_t v = reinterpret_cast<uint64_t&>(val);
|
|
|
|
|
m_cur[7] = (char)(v & 0xff); v >>= 8;
|
|
|
|
|
m_cur[6] = (char)(v & 0xff); v >>= 8;
|
|
|
|
|
m_cur[5] = (char)(v & 0xff); v >>= 8;
|
|
|
|
|
m_cur[4] = (char)(v & 0xff); v >>= 8;
|
|
|
|
|
m_cur[3] = (char)(v & 0xff); v >>= 8;
|
|
|
|
|
m_cur[2] = (char)(v & 0xff); v >>= 8;
|
|
|
|
|
m_cur[1] = (char)(v & 0xff); v >>= 8;
|
|
|
|
|
m_cur[0] = (char)(v & 0xff);
|
|
|
|
|
m_cur += 8;
|
2015-06-21 22:42:01 -07:00
|
|
|
}
|
|
|
|
|
|
2015-06-25 22:57:43 -07:00
|
|
|
void WireEncoder::ReserveSlow(std::size_t len) {
|
2015-06-26 01:23:36 -07:00
|
|
|
assert(m_end >= m_cur);
|
|
|
|
|
// should never happen due to checks in Reserve(), but check anyway
|
2015-06-25 22:57:43 -07:00
|
|
|
if (static_cast<std::size_t>(m_end - m_cur) >= len) return;
|
2015-06-26 01:23:36 -07:00
|
|
|
|
|
|
|
|
// Double current buffer size until we have enough space. Since we're
|
|
|
|
|
// reserving space, it's likely more will be required soon in any case.
|
2015-06-25 22:57:43 -07:00
|
|
|
std::size_t pos = m_cur - m_start;
|
|
|
|
|
std::size_t newlen = (m_end - m_start) * 2;
|
|
|
|
|
while (newlen < (pos + len)) newlen *= 2;
|
2015-06-26 01:23:36 -07:00
|
|
|
m_start = static_cast<char*>(std::realloc(m_start, newlen));
|
2015-06-25 22:57:43 -07:00
|
|
|
m_cur = m_start + pos;
|
|
|
|
|
m_end = m_start + newlen;
|
2015-06-21 22:42:01 -07:00
|
|
|
}
|
|
|
|
|
|
2015-06-25 23:19:24 -07:00
|
|
|
void WireEncoder::WriteUleb128(unsigned long val) {
|
|
|
|
|
Reserve(SizeUleb128(val));
|
|
|
|
|
m_cur += ntimpl::WriteUleb128(m_cur, val);
|
2015-06-21 22:42:01 -07:00
|
|
|
}
|
|
|
|
|
|
2015-06-25 22:57:43 -07:00
|
|
|
void WireEncoder::WriteType(NT_Type type) {
|
|
|
|
|
Reserve(1);
|
2015-06-26 01:23:36 -07:00
|
|
|
// Convert from enum to actual byte value.
|
2015-06-25 22:57:43 -07:00
|
|
|
switch (type) {
|
|
|
|
|
case NT_BOOLEAN:
|
|
|
|
|
*m_cur = 0x00;
|
|
|
|
|
break;
|
|
|
|
|
case NT_DOUBLE:
|
|
|
|
|
*m_cur = 0x01;
|
|
|
|
|
break;
|
|
|
|
|
case NT_STRING:
|
|
|
|
|
*m_cur = 0x02;
|
|
|
|
|
break;
|
2015-06-21 22:42:01 -07:00
|
|
|
case NT_RAW:
|
2015-06-25 22:57:43 -07:00
|
|
|
if (m_proto_rev < 0x0300u) {
|
|
|
|
|
m_error = "raw type not supported in protocol < 3.0";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
*m_cur = 0x03;
|
|
|
|
|
break;
|
|
|
|
|
case NT_BOOLEAN_ARRAY:
|
|
|
|
|
*m_cur = 0x10;
|
|
|
|
|
break;
|
|
|
|
|
case NT_DOUBLE_ARRAY:
|
|
|
|
|
*m_cur = 0x11;
|
|
|
|
|
break;
|
|
|
|
|
case NT_STRING_ARRAY:
|
|
|
|
|
*m_cur = 0x12;
|
|
|
|
|
break;
|
2015-06-21 22:42:01 -07:00
|
|
|
case NT_RPC:
|
2015-06-25 22:57:43 -07:00
|
|
|
if (m_proto_rev < 0x0300u) {
|
|
|
|
|
m_error = "RPC type not supported in protocol < 3.0";
|
2015-06-21 22:42:01 -07:00
|
|
|
return;
|
2015-06-25 22:57:43 -07:00
|
|
|
}
|
|
|
|
|
*m_cur = 0x20;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
m_error = "unrecognized type";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
++m_cur;
|
2015-06-21 22:42:01 -07:00
|
|
|
}
|
|
|
|
|
|
2015-06-26 01:23:36 -07:00
|
|
|
std::size_t WireEncoder::GetValueSize(const NT_Value& value) const {
|
2015-06-25 22:57:43 -07:00
|
|
|
switch (value.type) {
|
2015-06-21 22:42:01 -07:00
|
|
|
case NT_BOOLEAN:
|
2015-06-25 22:57:43 -07:00
|
|
|
return 1;
|
2015-06-21 22:42:01 -07:00
|
|
|
case NT_DOUBLE:
|
2015-06-25 22:57:43 -07:00
|
|
|
return 8;
|
2015-06-21 22:42:01 -07:00
|
|
|
case NT_STRING:
|
2015-06-25 22:57:43 -07:00
|
|
|
return GetStringSize(value.data.v_string);
|
2015-06-21 22:42:01 -07:00
|
|
|
case NT_RAW:
|
|
|
|
|
case NT_RPC:
|
2015-06-25 22:57:43 -07:00
|
|
|
if (m_proto_rev < 0x0300u) return 0;
|
|
|
|
|
return GetStringSize(value.data.v_raw);
|
2015-06-26 01:23:36 -07:00
|
|
|
case NT_BOOLEAN_ARRAY: {
|
|
|
|
|
// 1-byte size, 1 byte per element
|
|
|
|
|
std::size_t size = value.data.arr_boolean.size;
|
|
|
|
|
if (size > 0xff) size = 0xff; // size is only 1 byte, truncate
|
|
|
|
|
return 1 + size;
|
|
|
|
|
}
|
|
|
|
|
case NT_DOUBLE_ARRAY: {
|
|
|
|
|
// 1-byte size, 8 bytes per element
|
|
|
|
|
std::size_t size = value.data.arr_double.size;
|
|
|
|
|
if (size > 0xff) size = 0xff; // size is only 1 byte, truncate
|
|
|
|
|
return 1 + size * 8;
|
|
|
|
|
}
|
2015-06-25 22:57:43 -07:00
|
|
|
case NT_STRING_ARRAY: {
|
2015-06-26 01:23:36 -07:00
|
|
|
std::size_t size = value.data.arr_string.size;
|
|
|
|
|
if (size > 0xff) size = 0xff; // size is only 1 byte, truncate
|
|
|
|
|
std::size_t len = 1; // 1-byte size
|
|
|
|
|
for (std::size_t i = 0; i < size; ++i)
|
2015-06-25 22:57:43 -07:00
|
|
|
len += GetStringSize(value.data.arr_string.arr[i]);
|
|
|
|
|
return len;
|
2015-06-21 22:42:01 -07:00
|
|
|
}
|
|
|
|
|
default:
|
2015-06-25 22:57:43 -07:00
|
|
|
return 0;
|
|
|
|
|
}
|
2015-06-21 22:42:01 -07:00
|
|
|
}
|
|
|
|
|
|
2015-06-25 22:57:43 -07:00
|
|
|
void WireEncoder::WriteValue(const NT_Value& value) {
|
|
|
|
|
switch (value.type) {
|
2015-06-21 22:42:01 -07:00
|
|
|
case NT_BOOLEAN:
|
2015-06-25 22:57:43 -07:00
|
|
|
Write8(value.data.v_boolean ? 1 : 0);
|
|
|
|
|
break;
|
2015-06-21 22:42:01 -07:00
|
|
|
case NT_DOUBLE:
|
2015-06-25 22:57:43 -07:00
|
|
|
WriteDouble(value.data.v_double);
|
|
|
|
|
break;
|
2015-06-21 22:42:01 -07:00
|
|
|
case NT_STRING:
|
2015-06-25 22:57:43 -07:00
|
|
|
WriteString(value.data.v_string);
|
|
|
|
|
break;
|
2015-06-21 22:42:01 -07:00
|
|
|
case NT_RAW:
|
|
|
|
|
case NT_RPC:
|
2015-06-25 22:57:43 -07:00
|
|
|
if (m_proto_rev < 0x0300u) {
|
|
|
|
|
m_error = "raw and rpc values not supported in protocol < 3.0";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
WriteString(value.data.v_raw);
|
|
|
|
|
break;
|
|
|
|
|
case NT_BOOLEAN_ARRAY: {
|
|
|
|
|
std::size_t size = value.data.arr_boolean.size;
|
2015-06-26 01:23:36 -07:00
|
|
|
if (size > 0xff) size = 0xff; // size is only 1 byte, truncate
|
2015-06-25 22:57:43 -07:00
|
|
|
Reserve(1 + size);
|
|
|
|
|
Write8(size);
|
|
|
|
|
|
|
|
|
|
for (std::size_t i = 0; i < size; ++i)
|
|
|
|
|
Write8(value.data.arr_boolean.arr[i] ? 1 : 0);
|
|
|
|
|
break;
|
2015-06-21 22:42:01 -07:00
|
|
|
}
|
2015-06-25 22:57:43 -07:00
|
|
|
case NT_DOUBLE_ARRAY: {
|
|
|
|
|
std::size_t size = value.data.arr_double.size;
|
2015-06-26 01:23:36 -07:00
|
|
|
if (size > 0xff) size = 0xff; // size is only 1 byte, truncate
|
2015-06-25 22:57:43 -07:00
|
|
|
Reserve(1 + size * 8);
|
|
|
|
|
Write8(size);
|
|
|
|
|
|
|
|
|
|
for (std::size_t i = 0; i < size; ++i)
|
2015-07-04 10:18:30 -07:00
|
|
|
WriteDouble(value.data.arr_double.arr[i]);
|
2015-06-25 22:57:43 -07:00
|
|
|
break;
|
2015-06-21 22:42:01 -07:00
|
|
|
}
|
2015-06-25 22:57:43 -07:00
|
|
|
case NT_STRING_ARRAY: {
|
|
|
|
|
std::size_t size = value.data.arr_string.size;
|
2015-06-26 01:23:36 -07:00
|
|
|
if (size > 0xff) size = 0xff; // size is only 1 byte, truncate
|
2015-06-25 22:57:43 -07:00
|
|
|
Write8(size);
|
|
|
|
|
|
|
|
|
|
for (std::size_t i = 0; i < size; ++i)
|
|
|
|
|
WriteString(value.data.arr_string.arr[i]);
|
|
|
|
|
break;
|
2015-06-21 22:42:01 -07:00
|
|
|
}
|
|
|
|
|
default:
|
2015-06-25 22:57:43 -07:00
|
|
|
m_error = "unrecognized type when writing value";
|
|
|
|
|
return;
|
|
|
|
|
}
|
2015-06-21 22:42:01 -07:00
|
|
|
}
|
|
|
|
|
|
2015-07-05 16:05:58 -07:00
|
|
|
std::size_t WireEncoder::GetStringSize(llvm::StringRef str) const {
|
2015-06-26 01:23:36 -07:00
|
|
|
if (m_proto_rev < 0x0300u) {
|
2015-07-05 16:05:58 -07:00
|
|
|
std::size_t len = str.size();
|
2015-06-26 01:23:36 -07:00
|
|
|
if (len > 0xffff) len = 0xffff; // Limited to 64K length; truncate
|
|
|
|
|
return 2 + len;
|
|
|
|
|
}
|
2015-07-05 16:05:58 -07:00
|
|
|
return SizeUleb128(str.size()) + str.size();
|
2015-06-21 22:42:01 -07:00
|
|
|
}
|
|
|
|
|
|
2015-07-05 16:05:58 -07:00
|
|
|
void WireEncoder::WriteString(llvm::StringRef str) {
|
2015-06-25 22:57:43 -07:00
|
|
|
// length
|
2015-07-05 16:05:58 -07:00
|
|
|
std::size_t len = str.size();
|
2015-06-25 22:57:43 -07:00
|
|
|
if (m_proto_rev < 0x0300u) {
|
2015-06-26 01:23:36 -07:00
|
|
|
if (len > 0xffff) len = 0xffff; // Limited to 64K length; truncate
|
2015-06-25 22:57:43 -07:00
|
|
|
Write16(len);
|
|
|
|
|
} else
|
2015-06-25 23:19:24 -07:00
|
|
|
WriteUleb128(len);
|
2015-06-25 22:57:43 -07:00
|
|
|
|
|
|
|
|
// contents
|
|
|
|
|
Reserve(len);
|
2015-07-05 16:05:58 -07:00
|
|
|
std::memcpy(m_cur, str.data(), len);
|
2015-06-25 22:57:43 -07:00
|
|
|
m_cur += len;
|
2015-06-21 22:42:01 -07:00
|
|
|
}
|