blob: 610a53f5b5825d22c85c33ec3b697d13e567d791 [file] [log] [blame]
Brian Silvermanf7bd1c22015-12-24 16:07:11 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2015. All Rights Reserved. */
3/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
8#include "WireEncoder.h"
9
10#include <cassert>
11#include <cstdint>
12#include <cstdlib>
13#include <cstring>
14
15#include "llvm/MathExtras.h"
16#include "leb128.h"
17
18using namespace nt;
19
20WireEncoder::WireEncoder(unsigned int proto_rev) {
21 m_proto_rev = proto_rev;
22 m_error = nullptr;
23}
24
25void WireEncoder::WriteDouble(double val) {
26 // The highest performance way to do this, albeit non-portable.
27 std::uint64_t v = llvm::DoubleToBits(val);
28 m_data.append({
29 (char)((v >> 56) & 0xff),
30 (char)((v >> 48) & 0xff),
31 (char)((v >> 40) & 0xff),
32 (char)((v >> 32) & 0xff),
33 (char)((v >> 24) & 0xff),
34 (char)((v >> 16) & 0xff),
35 (char)((v >> 8) & 0xff),
36 (char)(v & 0xff)
37 });
38}
39
40void WireEncoder::WriteUleb128(unsigned long val) {
41 nt::WriteUleb128(m_data, val);
42}
43
44void WireEncoder::WriteType(NT_Type type) {
45 char ch;
46 // Convert from enum to actual byte value.
47 switch (type) {
48 case NT_BOOLEAN:
49 ch = 0x00;
50 break;
51 case NT_DOUBLE:
52 ch = 0x01;
53 break;
54 case NT_STRING:
55 ch = 0x02;
56 break;
57 case NT_RAW:
58 if (m_proto_rev < 0x0300u) {
59 m_error = "raw type not supported in protocol < 3.0";
60 return;
61 }
62 ch = 0x03;
63 break;
64 case NT_BOOLEAN_ARRAY:
65 ch = 0x10;
66 break;
67 case NT_DOUBLE_ARRAY:
68 ch = 0x11;
69 break;
70 case NT_STRING_ARRAY:
71 ch = 0x12;
72 break;
73 case NT_RPC:
74 if (m_proto_rev < 0x0300u) {
75 m_error = "RPC type not supported in protocol < 3.0";
76 return;
77 }
78 ch = 0x20;
79 break;
80 default:
81 m_error = "unrecognized type";
82 return;
83 }
84 m_data.push_back(ch);
85}
86
87std::size_t WireEncoder::GetValueSize(const Value& value) const {
88 switch (value.type()) {
89 case NT_BOOLEAN:
90 return 1;
91 case NT_DOUBLE:
92 return 8;
93 case NT_STRING:
94 return GetStringSize(value.GetString());
95 case NT_RAW:
96 if (m_proto_rev < 0x0300u) return 0;
97 return GetStringSize(value.GetRaw());
98 case NT_RPC:
99 if (m_proto_rev < 0x0300u) return 0;
100 return GetStringSize(value.GetRpc());
101 case NT_BOOLEAN_ARRAY: {
102 // 1-byte size, 1 byte per element
103 std::size_t size = value.GetBooleanArray().size();
104 if (size > 0xff) size = 0xff; // size is only 1 byte, truncate
105 return 1 + size;
106 }
107 case NT_DOUBLE_ARRAY: {
108 // 1-byte size, 8 bytes per element
109 std::size_t size = value.GetDoubleArray().size();
110 if (size > 0xff) size = 0xff; // size is only 1 byte, truncate
111 return 1 + size * 8;
112 }
113 case NT_STRING_ARRAY: {
114 auto v = value.GetStringArray();
115 std::size_t size = v.size();
116 if (size > 0xff) size = 0xff; // size is only 1 byte, truncate
117 std::size_t len = 1; // 1-byte size
118 for (std::size_t i = 0; i < size; ++i)
119 len += GetStringSize(v[i]);
120 return len;
121 }
122 default:
123 return 0;
124 }
125}
126
127void WireEncoder::WriteValue(const Value& value) {
128 switch (value.type()) {
129 case NT_BOOLEAN:
130 Write8(value.GetBoolean() ? 1 : 0);
131 break;
132 case NT_DOUBLE:
133 WriteDouble(value.GetDouble());
134 break;
135 case NT_STRING:
136 WriteString(value.GetString());
137 break;
138 case NT_RAW:
139 if (m_proto_rev < 0x0300u) {
140 m_error = "raw values not supported in protocol < 3.0";
141 return;
142 }
143 WriteString(value.GetRaw());
144 break;
145 case NT_RPC:
146 if (m_proto_rev < 0x0300u) {
147 m_error = "RPC values not supported in protocol < 3.0";
148 return;
149 }
150 WriteString(value.GetRpc());
151 break;
152 case NT_BOOLEAN_ARRAY: {
153 auto v = value.GetBooleanArray();
154 std::size_t size = v.size();
155 if (size > 0xff) size = 0xff; // size is only 1 byte, truncate
156 Write8(size);
157
158 for (std::size_t i = 0; i < size; ++i)
159 Write8(v[i] ? 1 : 0);
160 break;
161 }
162 case NT_DOUBLE_ARRAY: {
163 auto v = value.GetDoubleArray();
164 std::size_t size = v.size();
165 if (size > 0xff) size = 0xff; // size is only 1 byte, truncate
166 Write8(size);
167
168 for (std::size_t i = 0; i < size; ++i)
169 WriteDouble(v[i]);
170 break;
171 }
172 case NT_STRING_ARRAY: {
173 auto v = value.GetStringArray();
174 std::size_t size = v.size();
175 if (size > 0xff) size = 0xff; // size is only 1 byte, truncate
176 Write8(size);
177
178 for (std::size_t i = 0; i < size; ++i)
179 WriteString(v[i]);
180 break;
181 }
182 default:
183 m_error = "unrecognized type when writing value";
184 return;
185 }
186}
187
188std::size_t WireEncoder::GetStringSize(llvm::StringRef str) const {
189 if (m_proto_rev < 0x0300u) {
190 std::size_t len = str.size();
191 if (len > 0xffff) len = 0xffff; // Limited to 64K length; truncate
192 return 2 + len;
193 }
194 return SizeUleb128(str.size()) + str.size();
195}
196
197void WireEncoder::WriteString(llvm::StringRef str) {
198 // length
199 std::size_t len = str.size();
200 if (m_proto_rev < 0x0300u) {
201 if (len > 0xffff) len = 0xffff; // Limited to 64K length; truncate
202 Write16(len);
203 } else
204 WriteUleb128(len);
205
206 // contents
207 m_data.append(str.data(), str.data() + len);
208}