Refactor WireEncoder.

Change-Id: I50106755ce5571821d6e36767b16d08b91f054de
This commit is contained in:
Peter Johnson
2015-06-21 22:42:01 -07:00
parent dbed3fea6f
commit 047cccda89
7 changed files with 356 additions and 357 deletions

View File

@@ -312,20 +312,6 @@ void NT_InitString(struct NT_String *str);
void NT_DisposeConnectionInfoArray(struct NT_ConnectionInfo *arr, size_t count);
/*
* Message Encoding Functions
*/
size_t NT_WriteType(char *buf, enum NT_Type type, unsigned int proto_rev);
size_t NT_ReadType(const char *buf, enum NT_Type *type, unsigned int proto_rev);
size_t NT_GetStringSize(const struct NT_String *value, unsigned int proto_rev);
size_t NT_WriteString(char *buf, const struct NT_String *str,
unsigned int proto_rev);
size_t NT_GetValueSize(const struct NT_Value *value, unsigned int proto_rev);
size_t NT_WriteValue(char *buf, const struct NT_Value *value,
unsigned int proto_rev);
#ifdef __cplusplus
}
#endif

View File

@@ -1,171 +0,0 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#include "nt_encoding.h"
#include "ntcore.h"
#include <cstdint>
#include <cstring>
#include "nt_leb128.h"
using namespace NtImpl;
void
NtImpl::WriteDouble(char* &buf, double val)
{
std::uint64_t v = *reinterpret_cast<uint64_t*>(&val);
*buf++ = (char)((v >> 56) & 0xff);
*buf++ = (char)((v >> 48) & 0xff);
*buf++ = (char)((v >> 40) & 0xff);
*buf++ = (char)((v >> 32) & 0xff);
*buf++ = (char)((v >> 24) & 0xff);
*buf++ = (char)((v >> 16) & 0xff);
*buf++ = (char)((v >> 8) & 0xff);
*buf++ = (char)(v & 0xff);
}
size_t
NT_WriteType(char *buf, NT_Type type, unsigned int proto_rev)
{
switch (type)
{
case NT_BOOLEAN: *buf = 0x00; break;
case NT_DOUBLE: *buf = 0x01; break;
case NT_STRING: *buf = 0x02; break;
case NT_RAW:
if (proto_rev < 0x0300u)
return 0;
*buf = 0x03;
break;
case NT_BOOLEAN_ARRAY: *buf = 0x10; break;
case NT_DOUBLE_ARRAY: *buf = 0x11; break;
case NT_STRING_ARRAY: *buf = 0x12; break;
case NT_RPC:
if (proto_rev < 0x0300u)
return 0;
*buf = 0x20;
break;
default:
return 0;
}
return 1;
}
size_t
NT_ReadType(const char *buf, NT_Type *type, unsigned int proto_rev)
{
switch (buf[0])
{
case 0x00: *type = NT_BOOLEAN; break;
case 0x01: *type = NT_DOUBLE; break;
case 0x02: *type = NT_STRING; break;
case 0x03: *type = NT_RAW; break;
case 0x10: *type = NT_BOOLEAN_ARRAY; break;
case 0x11: *type = NT_DOUBLE_ARRAY; break;
case 0x12: *type = NT_STRING_ARRAY; break;
case 0x20: *type = NT_RPC; break;
default: return 0;
}
return 1;
}
size_t
NT_GetStringSize(const NT_String *str, unsigned int proto_rev)
{
if (proto_rev < 0x0300u)
return 2 + str->len;
return size_uleb128(str->len) + str->len;
}
size_t
NT_WriteString(char *buf, const NT_String *str, unsigned int proto_rev)
{
char *start = buf;
if (proto_rev < 0x0300u)
Write16(buf, str->len);
else
buf += write_uleb128(buf, str->len);
std::memcpy(buf, str->str, str->len);
return (buf - start) + str->len;
}
size_t
NT_GetValueSize(const NT_Value *value, unsigned int proto_rev)
{
switch (value->type)
{
case NT_BOOLEAN:
return 1;
case NT_DOUBLE:
return 8;
case NT_STRING:
return NT_GetStringSize(&value->data.v_string, proto_rev);
case NT_RAW:
case NT_RPC:
if (proto_rev < 0x0300u)
return 0;
return NT_GetStringSize(&value->data.v_raw, proto_rev);
case NT_BOOLEAN_ARRAY:
return 1 + value->data.arr_boolean.size;
case NT_DOUBLE_ARRAY:
return 1 + value->data.arr_double.size * 8;
case NT_STRING_ARRAY:
{
size_t len = 1;
for (size_t i=0; i<value->data.arr_string.size; ++i)
len += NT_GetStringSize(&value->data.arr_string.arr[i], proto_rev);
return len;
}
default:
return 0;
}
return 1;
}
size_t
NT_WriteValue(char *buf, const NT_Value *value, unsigned int proto_rev)
{
char *start = buf;
switch (value->type)
{
case NT_BOOLEAN:
Write8(buf, value->data.v_boolean ? 1 : 0);
break;
case NT_DOUBLE:
WriteDouble(buf, value->data.v_double);
break;
case NT_STRING:
buf += NT_WriteString(buf, &value->data.v_string, proto_rev);
break;
case NT_RAW:
case NT_RPC:
if (proto_rev < 0x0300u)
return 0;
buf += NT_WriteString(buf, &value->data.v_raw, proto_rev);
break;
case NT_BOOLEAN_ARRAY:
Write8(buf, value->data.arr_boolean.size);
for (size_t i=0; i<value->data.arr_boolean.size; ++i)
Write8(buf, value->data.arr_boolean.arr[i] ? 1 : 0);
break;
case NT_DOUBLE_ARRAY:
Write8(buf, value->data.arr_double.size);
for (size_t i=0; i<value->data.arr_double.size; ++i)
WriteDouble(buf, value->data.arr_double.arr[i] ? 1 : 0);
break;
case NT_STRING_ARRAY:
Write8(buf, value->data.arr_string.size);
for (size_t i=0; i<value->data.arr_string.size; ++i)
buf += NT_WriteString(buf, &value->data.arr_string.arr[i],
proto_rev);
break;
default:
return 0;
}
return buf - start;
}

View File

@@ -1,39 +0,0 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#ifndef NT_UTIL_H_
#define NT_UTIL_H_
namespace NtImpl {
inline void
Write8(char* &buf, unsigned int val)
{
*buf++ = (char)(val & 0xff);
}
inline void
Write16(char* &buf, unsigned int val)
{
*buf++ = (char)((val >> 8) & 0xff);
*buf++ = (char)(val & 0xff);
}
inline void
Write32(char* &buf, unsigned long val)
{
*buf++ = (char)((val >> 24) & 0xff);
*buf++ = (char)((val >> 16) & 0xff);
*buf++ = (char)((val >> 8) & 0xff);
*buf++ = (char)(val & 0xff);
}
void WriteDouble(char* &buf, double val);
} // namespace NtImpl
#endif /* NT_UTIL_H */

View File

@@ -7,53 +7,25 @@
#include "nt_messagewriter.h"
#include <cassert>
#include <cstdlib>
#include "nt_internal.h"
#include "nt_leb128.h"
using namespace NtImpl;
MessageWriter::MessageWriter(unsigned int proto_rev)
{
m_start = m_cur = (char *)std::malloc(1024);
m_end = m_start + 1024;
m_proto_rev = proto_rev;
m_error = 0;
}
MessageWriter::~MessageWriter()
{
std::free(m_start);
}
void
MessageWriter::EnsureSlow(std::size_t len)
{
assert(m_end > m_cur);
if (static_cast<std::size_t>(m_end - m_cur) >= len)
return;
std::size_t pos = m_cur - m_start;
std::size_t newlen = (m_end - m_start) * 2;
while (newlen < (pos + len))
newlen *= 2;
m_start = (char *)std::realloc(m_start, newlen);
m_cur = m_start + pos;
m_end = m_start + newlen;
}
void
MessageWriter::WriteKeepAlive()
{
Ensure(1);
Reserve(1);
Write8(NT_MSG_KEEP_ALIVE);
}
void
MessageWriter::WriteClientHello(const NT_String &self_id)
{
Ensure(3);
Reserve(3);
Write8(NT_MSG_CLIENT_HELLO);
Write16(m_proto_rev);
if (m_proto_rev < 0x0300u)
@@ -64,7 +36,7 @@ MessageWriter::WriteClientHello(const NT_String &self_id)
void
MessageWriter::WriteProtoUnsup()
{
Ensure(3);
Reserve(3);
Write8(NT_MSG_PROTO_UNSUP);
Write16(m_proto_rev);
}
@@ -72,7 +44,7 @@ MessageWriter::WriteProtoUnsup()
void
MessageWriter::WriteServerHelloDone()
{
Ensure(1);
Reserve(1);
Write8(NT_MSG_SERVER_HELLO_DONE);
}
@@ -81,7 +53,7 @@ MessageWriter::WriteServerHello(unsigned int flags, const NT_String &self_id)
{
if (m_proto_rev < 0x0300u)
return; // new message in version 3.0
Ensure(2);
Reserve(2);
Write8(NT_MSG_SERVER_HELLO);
Write8(flags);
WriteString(self_id);
@@ -92,7 +64,7 @@ MessageWriter::WriteClientHelloDone()
{
if (m_proto_rev < 0x0300u)
return; // new message in version 3.0
Ensure(1);
Reserve(1);
Write8(NT_MSG_CLIENT_HELLO_DONE);
}
@@ -103,10 +75,10 @@ MessageWriter::WriteEntryAssign(const NT_String &name,
const NT_Value &value,
unsigned int flags)
{
Ensure(1);
Reserve(1);
Write8(NT_MSG_ENTRY_ASSIGN);
WriteString(name);
Ensure(6);
Reserve(6);
WriteType(value.type);
Write16(id);
Write16(seq_num);
@@ -120,7 +92,7 @@ MessageWriter::WriteEntryUpdate(unsigned int id,
unsigned int seq_num,
const NT_Value &value)
{
Ensure(6);
Reserve(6);
Write8(NT_MSG_ENTRY_UPDATE);
Write16(id);
Write16(seq_num);
@@ -134,7 +106,7 @@ MessageWriter::WriteFlagsUpdate(unsigned int id, unsigned int flags)
{
if (m_proto_rev < 0x0300u)
return; // new message in version 3.0
Ensure(4);
Reserve(4);
Write8(NT_MSG_FLAGS_UPDATE);
Write16(id);
Write8(flags);
@@ -145,7 +117,7 @@ MessageWriter::WriteEntryDelete(unsigned int id)
{
if (m_proto_rev < 0x0300u)
return; // new message in version 3.0
Ensure(3);
Reserve(3);
Write8(NT_MSG_ENTRY_DELETE);
Write16(id);
}
@@ -156,7 +128,7 @@ MessageWriter::WriteClearEntries()
if (m_proto_rev < 0x0300u)
return; // new message in version 3.0
Ensure(5);
Reserve(5);
Write8(NT_MSG_CLEAR_ENTRIES);
Write32(NT_CLEAR_ALL_MAGIC);
}
@@ -189,59 +161,16 @@ MessageWriter::WriteRpc(unsigned int msg_type,
if (m_proto_rev < 0x0300u)
return; // new message in version 3.0
Ensure(5);
Reserve(5);
Write8(msg_type);
Write16(id);
Write16(uid);
unsigned long len = 0;
for (const NT_Value *value = values_start; value != values_end; ++value)
len += NT_GetValueSize(value, m_proto_rev);
len += GetValueSize(*value);
WriteULEB128(len);
for (const NT_Value *value = values_start; value != values_end; ++value)
WriteValue(*value);
}
void
MessageWriter::WriteULEB128(unsigned long val)
{
Ensure(size_uleb128(val));
m_cur += write_uleb128(m_cur, val);
}
void
MessageWriter::WriteType(NT_Type type)
{
Ensure(1);
size_t len = NT_WriteType(m_cur, type, m_proto_rev);
if (len == 0)
m_error = "unrecognized type";
m_cur += len;
}
void
MessageWriter::WriteValue(const NT_Value &value)
{
std::size_t len = NT_GetValueSize(&value, m_proto_rev);
if (len == 0)
{
m_error = "unrecognized type when writing value";
return;
}
Ensure(len);
m_cur += NT_WriteValue(m_cur, &value, m_proto_rev);
}
void
MessageWriter::WriteString(const NT_String &str)
{
std::size_t len = NT_GetStringSize(&str, m_proto_rev);
if (len == 0)
{
m_error = "invalid string";
return;
}
Ensure(len);
m_cur += NT_WriteString(m_cur, &str, m_proto_rev);
}

View File

@@ -8,44 +8,39 @@
#ifndef NT_MESSAGEWRITER_H_
#define NT_MESSAGEWRITER_H_
#include <cassert>
#include <cstddef>
#include "ntcore.h"
#include "nt_encoding.h"
#include "nt_wireencoder.h"
namespace NtImpl {
class MessageWriter
class MessageWriter : private WireEncoder
{
public:
explicit MessageWriter(unsigned int proto_rev);
explicit MessageWriter(unsigned int proto_rev) : WireEncoder(proto_rev) {}
~MessageWriter();
void SetProtocolRev(unsigned int proto_rev)
{
m_proto_rev = proto_rev;
WireEncoder::SetProtocolRev(proto_rev);
}
void Reset()
{
m_cur = m_start;
m_error = 0;
WireEncoder::Reset();
}
const char *GetError() const
{
return m_error;
return WireEncoder::GetError();
}
const char *GetData() const
{
return m_start;
return WireEncoder::GetData();
}
std::size_t GetSize() const
{
return m_cur - m_start;
return WireEncoder::GetSize();
}
void WriteKeepAlive();
@@ -74,50 +69,15 @@ public:
const NT_Value *results_start,
const NT_Value *results_end);
protected:
void Ensure(std::size_t len)
{
assert(m_end > m_cur);
if (static_cast<size_t>(m_end - m_cur) < len)
EnsureSlow(len);
}
void Write8(unsigned int val)
{
NtImpl::Write8(m_cur, val);
}
void Write16(unsigned int val)
{
NtImpl::Write16(m_cur, val);
}
void Write32(unsigned long val)
{
NtImpl::Write32(m_cur, val);
}
void WriteULEB128(unsigned long val);
void WriteType(NT_Type type);
void WriteValue(const NT_Value &value);
void WriteString(const NT_String &str);
private:
MessageWriter(const MessageWriter&);
MessageWriter& operator= (const MessageWriter&);
void EnsureSlow(std::size_t len);
void WriteRpc(unsigned int msg_type,
unsigned int id,
unsigned int uid,
const NT_Value *values_start,
const NT_Value *values_end);
char *m_start;
char *m_cur;
char *m_end;
unsigned int m_proto_rev;
const char *m_error;
};
} // namespace NtImpl

228
src/nt_wireencoder.cpp Normal file
View File

@@ -0,0 +1,228 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#include "nt_wireencoder.h"
#include <cassert>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include "nt_internal.h"
#include "nt_leb128.h"
using namespace NtImpl;
WireEncoder::WireEncoder(unsigned int proto_rev)
{
m_start = m_cur = (char *)std::malloc(1024);
m_end = m_start + 1024;
m_proto_rev = proto_rev;
m_error = 0;
}
WireEncoder::~WireEncoder()
{
std::free(m_start);
}
void
WireEncoder::WriteDouble(double val)
{
Reserve(8);
std::uint64_t v = *reinterpret_cast<uint64_t*>(&val);
*m_cur++ = (char)((v >> 56) & 0xff);
*m_cur++ = (char)((v >> 48) & 0xff);
*m_cur++ = (char)((v >> 40) & 0xff);
*m_cur++ = (char)((v >> 32) & 0xff);
*m_cur++ = (char)((v >> 24) & 0xff);
*m_cur++ = (char)((v >> 16) & 0xff);
*m_cur++ = (char)((v >> 8) & 0xff);
*m_cur++ = (char)(v & 0xff);
}
void
WireEncoder::ReserveSlow(std::size_t len)
{
assert(m_end > m_cur);
if (static_cast<std::size_t>(m_end - m_cur) >= len)
return;
std::size_t pos = m_cur - m_start;
std::size_t newlen = (m_end - m_start) * 2;
while (newlen < (pos + len))
newlen *= 2;
m_start = (char *)std::realloc(m_start, newlen);
m_cur = m_start + pos;
m_end = m_start + newlen;
}
void
WireEncoder::WriteULEB128(unsigned long val)
{
Reserve(size_uleb128(val));
m_cur += write_uleb128(m_cur, val);
}
void
WireEncoder::WriteType(NT_Type type)
{
Reserve(1);
switch (type)
{
case NT_BOOLEAN: *m_cur = 0x00; break;
case NT_DOUBLE: *m_cur = 0x01; break;
case NT_STRING: *m_cur = 0x02; break;
case NT_RAW:
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;
case NT_RPC:
if (m_proto_rev < 0x0300u)
{
m_error = "RPC type not supported in protocol < 3.0";
return;
}
*m_cur = 0x20;
break;
default:
m_error = "unrecognized type";
return;
}
++m_cur;
}
std::size_t
WireEncoder::GetValueSize(const NT_Value &value)
{
switch (value.type)
{
case NT_BOOLEAN:
return 1;
case NT_DOUBLE:
return 8;
case NT_STRING:
return GetStringSize(value.data.v_string);
case NT_RAW:
case NT_RPC:
if (m_proto_rev < 0x0300u)
return 0;
return GetStringSize(value.data.v_raw);
case NT_BOOLEAN_ARRAY:
return 1 + value.data.arr_boolean.size;
case NT_DOUBLE_ARRAY:
return 1 + value.data.arr_double.size * 8;
case NT_STRING_ARRAY:
{
size_t len = 1;
for (size_t i=0; i<value.data.arr_string.size; ++i)
len += GetStringSize(value.data.arr_string.arr[i]);
return len;
}
default:
return 0;
}
}
void
WireEncoder::WriteValue(const NT_Value &value)
{
switch (value.type)
{
case NT_BOOLEAN:
Write8(value.data.v_boolean ? 1 : 0);
break;
case NT_DOUBLE:
WriteDouble(value.data.v_double);
break;
case NT_STRING:
WriteString(value.data.v_string);
break;
case NT_RAW:
case NT_RPC:
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;
if (size > 0xff)
size = 0xff;
Reserve(1+size);
Write8(size);
for (std::size_t i=0; i<size; ++i)
Write8(value.data.arr_boolean.arr[i] ? 1 : 0);
break;
}
case NT_DOUBLE_ARRAY:
{
std::size_t size = value.data.arr_double.size;
if (size > 0xff)
size = 0xff;
Reserve(1+size*8);
Write8(size);
for (std::size_t i=0; i<size; ++i)
WriteDouble(value.data.arr_double.arr[i] ? 1 : 0);
break;
}
case NT_STRING_ARRAY:
{
std::size_t size = value.data.arr_string.size;
if (size > 0xff)
size = 0xff;
Write8(size);
for (std::size_t i=0; i<size; ++i)
WriteString(value.data.arr_string.arr[i]);
break;
}
default:
m_error = "unrecognized type when writing value";
return;
}
}
std::size_t
WireEncoder::GetStringSize(const NT_String &str)
{
if (m_proto_rev < 0x0300u)
return 2 + str.len;
return size_uleb128(str.len) + str.len;
}
void
WireEncoder::WriteString(const NT_String &str)
{
// length
std::size_t len = str.len;
if (m_proto_rev < 0x0300u)
{
// Limited to 64K length; truncate if necessary
if (len > 0xffff)
len = 0xffff;
Write16(len);
}
else
WriteULEB128(len);
// contents
Reserve(len);
std::memcpy(m_cur, str.str, len);
m_cur += len;
}

106
src/nt_wireencoder.h Normal file
View File

@@ -0,0 +1,106 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#ifndef NT_WIREENCODER_H_
#define NT_WIREENCODER_H_
#include <cassert>
#include <cstddef>
#include "ntcore.h"
namespace NtImpl {
class WireEncoder
{
public:
explicit WireEncoder(unsigned int proto_rev);
~WireEncoder();
void SetProtocolRev(unsigned int proto_rev)
{
m_proto_rev = proto_rev;
}
void Reset()
{
m_cur = m_start;
m_error = 0;
}
const char *GetError() const
{
return m_error;
}
const char *GetData() const
{
return m_start;
}
std::size_t GetSize() const
{
return m_cur - m_start;
}
void Reserve(std::size_t len)
{
//assert(m_end > m_cur);
if (static_cast<size_t>(m_end - m_cur) < len)
ReserveSlow(len);
}
void Write8(unsigned int val)
{
Reserve(1);
*m_cur++ = (char)(val & 0xff);
}
void Write16(unsigned int val)
{
Reserve(2);
*m_cur++ = (char)((val >> 8) & 0xff);
*m_cur++ = (char)(val & 0xff);
}
void Write32(unsigned long val)
{
Reserve(4);
*m_cur++ = (char)((val >> 24) & 0xff);
*m_cur++ = (char)((val >> 16) & 0xff);
*m_cur++ = (char)((val >> 8) & 0xff);
*m_cur++ = (char)(val & 0xff);
}
void WriteDouble(double val);
void WriteULEB128(unsigned long val);
void WriteType(NT_Type type);
void WriteValue(const NT_Value &value);
void WriteString(const NT_String &str);
std::size_t GetValueSize(const NT_Value &value);
std::size_t GetStringSize(const NT_String &str);
protected:
unsigned int m_proto_rev;
const char *m_error;
private:
WireEncoder(const WireEncoder&);
WireEncoder& operator= (const WireEncoder&);
void ReserveSlow(std::size_t len);
char *m_start;
char *m_cur;
char *m_end;
};
} // namespace NtImpl
#endif /* NT_WIREENCODER_H_ */