Merge lp:~nknotts/dccl/encode-decode-iterators into lp:~dccl-dev/dccl/3.0

Proposed by Nathan Knotts
Status: Merged
Merged at revision: 314
Proposed branch: lp:~nknotts/dccl/encode-decode-iterators
Merge into: lp:~dccl-dev/dccl/3.0
Diff against target: 591 lines (+297/-152)
4 files modified
src/binary.h (+34/-11)
src/bitset.h (+33/-3)
src/codec.cpp (+81/-138)
src/codec.h (+149/-0)
To merge this branch: bzr merge lp:~nknotts/dccl/encode-decode-iterators
Reviewer Review Type Date Requested Status
toby schneider Approve
Review via email: mp+247068@code.launchpad.net

Description of the change

I added an InputIterator overload to codec::decode
I also added a char* overload to codec::encode

For our application, our stream data is stored in a boost::circular_buffer. We did not want to incur the overhead of a copy/string allocation. I added the InputIterator overload to codec::decode to reduce/eliminate copying/allocation.

On the codec::encode side, I added a char* overload to reduce/eliminate allocations.

All unit tests that previously passed still pass.

However, some tests are failing (they were failing before this change)

The following tests FAILED:
  12 - dccl_test_ccl (OTHER_FAULT)
  13 - dccl_test_arithmetic (Failed)

To post a comment you must log in.
Revision history for this message
toby schneider (tes) wrote :

This makes sense to me.

Minor style request: could you move the implementation of these methods to the bottom of codec.h after the declaration of dccl::Codec? (I want to keep the declaration of dccl::Codec clean):

1. template<typename CharIterator> unsigned id(CharIterator begin, CharIterator end)
2. template <typename CharIterator> void decode(CharIterator begin, CharIterator end, google::protobuf::Message* msg, bool header_only = false)

Otherwise should be fine. I'll give it one more detailed look over after you make that change and then I'll merge it in.

Also, dccl_test_ccl and dccl_test_arithmetic run fine on my machine for both lp:dccl/3.0 and this branch. I don't know if they are properly handling the .dylib extension on OSX (I think this is what you're using?). If you could open a separate bug report on those with the command line output when you run them, that'd be great.

Thanks-
-Toby

315. By Nathan Knotts

src/codec.h: moved template implementations to bottom of file

Revision history for this message
Nathan Knotts (nknotts) wrote :

I am currently building on OSX. Based on the PPAs, I'm assuming you're building on Ubuntu/Debian, is that correct? I will try and track down why the tests are failing on my system. Any solution will be committed in a separate merge request.

Revision history for this message
toby schneider (tes) wrote :

Yes, I use Debian 6, 7 and Ubuntu 12.04/14.04 regularly. Ubuntu 14.04 is what I just tested your branch on.

Revision history for this message
Nathan Knotts (nknotts) wrote :

I did not realize those particular tests were using shared libraries. After setting my DYLD_LIBRARY_PATH, the tests now succeed.

316. By Nathan Knotts

bitset.h: 0 initialize to_byte_string output buffer

Revision history for this message
toby schneider (tes) wrote :

Appears to be a clean implementation of char iterator overloads for Codec::encode, decode and related helper functions.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/binary.h'
--- src/binary.h 2014-10-08 18:19:16 +0000
+++ src/binary.h 2015-01-22 21:49:07 +0000
@@ -85,27 +85,30 @@
85 return out;85 return out;
86 }86 }
8787
88 /// \brief Encodes a (little-endian) hexadecimal string from a byte string. Index 0 of `in` is written to index 0 and 1 (first byte) of `out`88 /// \brief Encodes a (little-endian) hexadecimal string from a byte string. Index 0 of `begin` is written to index 0 and 1 (first byte) of `out`
89 ///89 ///
90 /// \param in byte string to encode (e.g. "TOM")90 /// \param begin iterator to first byte of string to encode (e.g. "TOM")
91 /// \param end iterator pointing to the past-the-end character of the string.
91 /// \param out pointer to string to store result (e.g. "544f4d")92 /// \param out pointer to string to store result (e.g. "544f4d")
92 /// \param upper_case set true to use upper case for the alphabet characters (i.e. A,B,C,D,E,F), otherwise lowercase is used (a,b,c,d,e,f).93 /// \param upper_case set true to use upper case for the alphabet characters (i.e. A,B,C,D,E,F), otherwise lowercase is used (a,b,c,d,e,f).
93 inline void hex_encode(const std::string& in, std::string* out, bool upper_case = false)94 template <typename CharIterator>
95 inline void hex_encode(CharIterator begin, CharIterator end, std::string* out, bool upper_case = false)
94 {96 {
95 static const short char0_9_to_number = 48;97 static const short char0_9_to_number = 48;
96 static const short charA_F_to_number = 55; 98 static const short charA_F_to_number = 55;
97 static const short chara_f_to_number = 87; 99 static const short chara_f_to_number = 87;
98100
99 int in_size = in.size();101 size_t in_size = std::distance(begin, end);
100 int out_size = in_size << 1;102 size_t out_size = in_size << 1;
101 103
104 out->clear();
102 out->resize(out_size);105 out->resize(out_size);
103 for(int i = 0, n = in_size;106
104 i < n;107 size_t i = 0;
105 ++i)108 for(CharIterator it = begin; it != end; ++it)
106 {109 {
107 short msn = (in[i] >> 4) & 0x0f;110 short msn = (*it >> 4) & 0x0f;
108 short lsn = in[i] & 0x0f;111 short lsn = *it & 0x0f;
109112
110 if(msn >= 0 && msn <= 9)113 if(msn >= 0 && msn <= 9)
111 (*out)[2*i] = msn + char0_9_to_number;114 (*out)[2*i] = msn + char0_9_to_number;
@@ -117,9 +120,29 @@
117 else if(lsn >= 10 && lsn <= 15)120 else if(lsn >= 10 && lsn <= 15)
118 (*out)[2*i+1] = lsn + (upper_case ? charA_F_to_number : chara_f_to_number);121 (*out)[2*i+1] = lsn + (upper_case ? charA_F_to_number : chara_f_to_number);
119122
123 i++;
120 }124 }
121 }125 }
122126
127 template <typename CharIterator>
128 inline std::string hex_encode(CharIterator begin, CharIterator end)
129 {
130 std::string out;
131 hex_encode(begin, end, &out);
132 return out;
133
134 }
135
136 /// \brief Encodes a (little-endian) hexadecimal string from a byte string. Index 0 of `in` is written to index 0 and 1 (first byte) of `out`
137 ///
138 /// \param in byte string to encode (e.g. "TOM")
139 /// \param out pointer to string to store result (e.g. "544f4d")
140 /// \param upper_case set true to use upper case for the alphabet characters (i.e. A,B,C,D,E,F), otherwise lowercase is used (a,b,c,d,e,f).
141 inline void hex_encode(const std::string& in, std::string* out, bool upper_case = false)
142 {
143 hex_encode(in.begin(), in.end(), out, upper_case);
144 }
145
123 inline std::string hex_encode(const std::string& in)146 inline std::string hex_encode(const std::string& in)
124 {147 {
125 std::string out;148 std::string out;
126149
=== modified file 'src/bitset.h'
--- src/bitset.h 2015-01-14 01:25:28 +0000
+++ src/bitset.h 2015-01-22 21:49:07 +0000
@@ -28,6 +28,7 @@
28#include <algorithm>28#include <algorithm>
29#include <limits>29#include <limits>
30#include <string>30#include <string>
31#include <cassert>
3132
32#include "exception.h"33#include "exception.h"
3334
@@ -319,15 +320,44 @@
319 return s;320 return s;
320 }321 }
321322
323 /// \brief Generate a byte string representation of the Bitset, where each character represents 8 bits of the Bitset. The string is used as a byte container, and is not intended to be printed.
324 /// \param buf An output string containing the value of the Bitset, with the least signficant byte in string[0] and the most significant byte in string[size()-1]
325 /// \param max_len Maximum length of buf
326 /// \return number of bytes written to buf
327 size_t to_byte_string(char* buf, size_t max_len)
328 {
329 // number of bytes needed is ceil(size() / 8)
330 size_t len = this->size()/8 + (this->size()%8 ? 1 : 0);
331
332 assert( max_len >= len );
333
334 // initialize buffer to all zeroes
335 std::memset(buf, 0, len);
336
337 for(size_type i = 0, n = this->size(); i < n; ++i)
338 buf[i/8] |= static_cast<char>((*this)[i] << (i%8));
339
340 return len;
341 }
342
322 /// \brief Sets the value of the Bitset to the contents of a byte string, where each character represents 8 bits of the Bitset.343 /// \brief Sets the value of the Bitset to the contents of a byte string, where each character represents 8 bits of the Bitset.
323 ///344 ///
324 /// \param s A string container the values where the least signficant byte in string[0] and the most significant byte in string[size()-1]345 /// \param s A string container the values where the least signficant byte in string[0] and the most significant byte in string[size()-1]
325 void from_byte_string(const std::string& s)346 void from_byte_string(const std::string& s)
326 {347 {
327 this->resize(s.size() * 8);348 from_byte_stream(s.begin(), s.end());
349 }
350
351 /// \brief Sets the value of the Bitset to the contents of a byte string, where each character represents 8 bits of the Bitset.
352 /// A string container the values where the least signficant byte in string[0] and the most significant byte in string[size()-1]
353 /// \param begin Iterator pointing to the begining of the input buffer
354 /// \param end Iterator pointing to the end of the input bufer
355 template<typename CharIterator>
356 void from_byte_stream(CharIterator begin, CharIterator end)
357 {
358 this->resize(std::distance(begin, end) * 8);
328 int i = 0;359 int i = 0;
329 for(std::string::const_iterator it = s.begin(), n = s.end();360 for(CharIterator it = begin; it != end; ++it)
330 it != n; ++it)
331 {361 {
332 for(size_type j = 0; j < 8; ++j)362 for(size_type j = 0; j < 8; ++j)
333 (*this)[i*8+j] = (*it) & (1 << j);363 (*this)[i*8+j] = (*it) & (1 << j);
334364
=== modified file 'src/codec.cpp'
--- src/codec.cpp 2014-10-05 18:36:46 +0000
+++ src/codec.cpp 2015-01-22 21:49:07 +0000
@@ -150,8 +150,7 @@
150 }150 }
151}151}
152152
153153void dccl::Codec::encode_internal(const google::protobuf::Message& msg, bool header_only, Bitset& head_bits, Bitset& body_bits)
154void dccl::Codec::encode(std::string* bytes, const google::protobuf::Message& msg, bool header_only /* = false */)
155{154{
156 const Descriptor* desc = msg.GetDescriptor();155 const Descriptor* desc = msg.GetDescriptor();
157156
@@ -159,6 +158,9 @@
159158
160 try159 try
161 {160 {
161 size_t head_byte_size = 0;
162 size_t body_byte_size = 0;
163
162 if(!msg.IsInitialized() && !header_only)164 if(!msg.IsInitialized() && !header_only)
163 throw(Exception("Message is not properly initialized. All `required` fields must be set."));165 throw(Exception("Message is not properly initialized. All `required` fields must be set."));
164 166
@@ -173,26 +175,15 @@
173 if(codec)175 if(codec)
174 {176 {
175 //fixed header177 //fixed header
176 Bitset head_bits;
177 id_codec()->field_encode(&head_bits, id(desc), 0);178 id_codec()->field_encode(&head_bits, id(desc), 0);
178 179
179 internal::MessageStack msg_stack;180 internal::MessageStack msg_stack;
180 msg_stack.push(msg.GetDescriptor());181 msg_stack.push(msg.GetDescriptor());
181 codec->base_encode(&head_bits, msg, HEAD);182 codec->base_encode(&head_bits, msg, HEAD);
182183
183 std::string body_bytes;
184
185 // given header of not even byte size (e.g. 01011), make even byte size (e.g. 00001011)184 // given header of not even byte size (e.g. 01011), make even byte size (e.g. 00001011)
186 unsigned head_byte_size = ceil_bits2bytes(head_bits.size());185 head_byte_size = ceil_bits2bytes(head_bits.size());
187 unsigned head_bits_diff = head_byte_size * BITS_IN_BYTE - (head_bits.size());186 head_bits.resize(head_byte_size * BITS_IN_BYTE);
188 head_bits.resize(head_bits.size() + head_bits_diff);
189
190 std::string head_bytes = head_bits.to_byte_string();
191
192 dlog.is(DEBUG2, ENCODE) && dlog << "Head bytes (bits): " << head_bytes.size() << "(" << head_bits.size() << ")" << std::endl;
193 dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Head (bin): " << head_bits << std::endl;
194 dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Head (hex): " << hex_encode(head_bytes) << std::endl;
195
196187
197 if(header_only)188 if(header_only)
198 {189 {
@@ -200,23 +191,8 @@
200 }191 }
201 else192 else
202 {193 {
203 Bitset body_bits;
204 codec->base_encode(&body_bits, msg, BODY);194 codec->base_encode(&body_bits, msg, BODY);
205 body_bytes = body_bits.to_byte_string();
206 dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Body (bin): " << body_bits << std::endl;
207 dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Body (hex): " << hex_encode(body_bytes) << std::endl;
208 dlog.is(DEBUG2, ENCODE) && dlog << "Body bytes (bits): " << body_bytes.size() << "(" << body_bits.size() << ")" << std::endl;
209
210 if(!crypto_key_.empty() && !skip_crypto_ids_.count(id(desc)))
211 encrypt(&body_bytes, head_bytes);
212
213 dlog.is(DEBUG3, ENCODE) && dlog << "Encrypted Body (hex): " << hex_encode(body_bytes) << std::endl;
214 }195 }
215
216 dlog.is(DEBUG1, ENCODE) && dlog << "Successfully encoded message of type: " << desc->full_name() << std::endl;
217
218 *bytes += head_bytes + body_bytes;
219
220 }196 }
221 else197 else
222 {198 {
@@ -235,27 +211,83 @@
235 }211 }
236}212}
237213
238214size_t dccl::Codec::encode(char* bytes, size_t max_len, const google::protobuf::Message& msg, bool header_only /* = false */)
215{
216 const Descriptor* desc = msg.GetDescriptor();
217 Bitset head_bits;
218 Bitset body_bits;
219 encode_internal(msg, header_only, head_bits, body_bits);
220
221 size_t head_byte_size = ceil_bits2bytes(head_bits.size());
222 assert(max_len >= head_byte_size);
223 head_bits.to_byte_string(bytes, head_byte_size);
224
225 dlog.is(DEBUG2, ENCODE) && dlog << "Head bytes (bits): " << head_byte_size << "(" << head_bits.size() << ")" << std::endl;
226 dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Head (bin): " << head_bits << std::endl;
227 dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Head (hex): " << hex_encode(bytes, bytes+head_byte_size) << std::endl;
228
229 size_t body_byte_size = 0;
230 if (!header_only)
231 {
232 body_byte_size = ceil_bits2bytes(body_bits.size());
233 assert(max_len >= head_byte_size + body_byte_size);
234 body_bits.to_byte_string(bytes+head_byte_size, max_len-head_byte_size);
235
236 dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Body (bin): " << body_bits << std::endl;
237 dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Body (hex): " << hex_encode(bytes+head_byte_size, bytes+head_byte_size+body_byte_size) << std::endl;
238 dlog.is(DEBUG2, ENCODE) && dlog << "Body bytes (bits): " << body_byte_size << "(" << body_bits.size() << ")" << std::endl;
239
240 if(!crypto_key_.empty() && !skip_crypto_ids_.count(id(desc))) {
241 std::string head_bytes(bytes, bytes+head_byte_size);
242 std::string body_bytes(bytes+head_byte_size, bytes+head_byte_size+body_byte_size);
243 encrypt(&body_bytes, head_bytes);
244 std::memcpy(bytes+head_byte_size, body_bytes.data(), body_bytes.size());
245 }
246
247 dlog.is(logger::DEBUG3, logger::ENCODE) && dlog << "Encrypted Body (hex): " << hex_encode(bytes+head_byte_size, bytes+head_byte_size+body_byte_size) << std::endl;
248 }
249
250 dlog.is(DEBUG1, ENCODE) && dlog << "Successfully encoded message of type: " << desc->full_name() << std::endl;
251
252 return head_byte_size + body_byte_size;
253}
254
255
256void dccl::Codec::encode(std::string* bytes, const google::protobuf::Message& msg, bool header_only /* = false */)
257{
258 const Descriptor* desc = msg.GetDescriptor();
259 Bitset head_bits;
260 Bitset body_bits;
261 encode_internal(msg, header_only, head_bits, body_bits);
262
263 std::string head_bytes = head_bits.to_byte_string();
264
265 dlog.is(DEBUG2, ENCODE) && dlog << "Head bytes (bits): " << head_bytes.size() << "(" << head_bits.size() << ")" << std::endl;
266 dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Head (bin): " << head_bits << std::endl;
267 dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Head (hex): " << hex_encode(head_bytes) << std::endl;
268
269 std::string body_bytes;
270 if (!header_only)
271 {
272 body_bytes = body_bits.to_byte_string();
273
274 dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Body (bin): " << body_bits << std::endl;
275 dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Body (hex): " << hex_encode(body_bytes) << std::endl;
276 dlog.is(DEBUG2, ENCODE) && dlog << "Body bytes (bits): " << body_bytes.size() << "(" << body_bits.size() << ")" << std::endl;
277
278 if(!crypto_key_.empty() && !skip_crypto_ids_.count(id(desc)))
279 encrypt(&body_bytes, head_bytes);
280
281 dlog.is(logger::DEBUG3, logger::ENCODE) && dlog << "Encrypted Body (hex): " << hex_encode(body_bytes) << std::endl;
282 }
283
284 dlog.is(DEBUG1, ENCODE) && dlog << "Successfully encoded message of type: " << desc->full_name() << std::endl;
285 *bytes += head_bytes + body_bytes;
286}
239287
240unsigned dccl::Codec::id(const std::string& bytes)288unsigned dccl::Codec::id(const std::string& bytes)
241{289{
242 unsigned id_min_size = 0, id_max_size = 0;290 return id(bytes.begin(), bytes.end());
243 id_codec()->field_min_size(&id_min_size, 0);
244 id_codec()->field_max_size(&id_max_size, 0);
245
246 if(bytes.length() < (id_min_size / BITS_IN_BYTE))
247 throw(Exception("Bytes passed (hex: " + hex_encode(bytes) + ") is too small to be a valid DCCL message"));
248
249 Bitset fixed_header_bits;
250 fixed_header_bits.from_byte_string(bytes.substr(0, (size_t)std::ceil(double(id_max_size) / BITS_IN_BYTE)));
251
252 Bitset these_bits(&fixed_header_bits);
253 these_bits.get_more_bits(id_min_size);
254
255 boost::any return_value;
256 id_codec()->field_decode(&these_bits, &return_value, 0);
257
258 return boost::any_cast<uint32>(return_value);
259}291}
260292
261293
@@ -268,96 +300,7 @@
268300
269void dccl::Codec::decode(const std::string& bytes, google::protobuf::Message* msg, bool header_only /* = false */)301void dccl::Codec::decode(const std::string& bytes, google::protobuf::Message* msg, bool header_only /* = false */)
270{302{
271 try303 decode(bytes.begin(), bytes.end(), msg, header_only);
272 {
273 unsigned this_id = id(bytes);
274
275 dlog.is(DEBUG1, DECODE) && dlog << "Began decoding message of id: " << this_id << std::endl;
276
277 if(!id2desc_.count(this_id))
278 throw(Exception("Message id " + boost::lexical_cast<std::string>(this_id) + " has not been validated. Call validate() before decoding this type."));
279
280 const Descriptor* desc = msg->GetDescriptor();
281
282 dlog.is(DEBUG1, DECODE) && dlog << "Type name: " << desc->full_name() << std::endl;
283
284 boost::shared_ptr<FieldCodecBase> codec = FieldCodecManager::find(desc);
285 boost::shared_ptr<internal::FromProtoCppTypeBase> helper = internal::TypeHelper::find(desc);
286
287 if(codec)
288 {
289 unsigned head_size_bits;
290 unsigned body_size_bits;
291 codec->base_max_size(&head_size_bits, desc, HEAD);
292 codec->base_max_size(&body_size_bits, desc, BODY);
293 unsigned id_size = 0;
294 id_codec()->field_size(&id_size, this_id, 0);
295 head_size_bits += id_size;
296
297 unsigned head_size_bytes = ceil_bits2bytes(head_size_bits);
298 unsigned body_size_bytes = ceil_bits2bytes(body_size_bits);
299
300 dlog.is(DEBUG2, DECODE) && dlog << "Head bytes (bits): " << head_size_bytes << "(" << head_size_bits
301 << "), max body bytes (bits): " << body_size_bytes << "(" << body_size_bits << ")" << std::endl;
302
303 std::string head_bytes = bytes.substr(0, head_size_bytes);
304 dlog.is(DEBUG3, DECODE) && dlog << "Unencrypted Head (hex): " << hex_encode(head_bytes) << std::endl;
305
306 Bitset head_bits;
307 head_bits.from_byte_string(head_bytes);
308 dlog.is(DEBUG3, DECODE) && dlog << "Unencrypted Head (bin): " << head_bits << std::endl;
309
310 // shift off ID bits
311 head_bits >>= id_size;
312
313 dlog.is(DEBUG3, DECODE) && dlog << "Unencrypted Head after ID bits removal (bin): " << head_bits << std::endl;
314
315 internal::MessageStack msg_stack;
316 msg_stack.push(msg->GetDescriptor());
317
318 codec->base_decode(&head_bits, msg, HEAD);
319 dlog.is(DEBUG2, DECODE) && dlog << "after header decode, message is: " << *msg << std::endl;
320
321
322 if(header_only)
323 {
324 dlog.is(DEBUG2, DECODE) && dlog << "as requested, skipping decrypting and decoding body." << std::endl;
325 }
326 else
327 {
328 std::string body_bytes = bytes.substr(head_size_bytes);
329 dlog.is(DEBUG3, DECODE) && dlog << "Encrypted Body (hex): " << hex_encode(body_bytes) << std::endl;
330
331 if(!crypto_key_.empty() && !skip_crypto_ids_.count(this_id))
332 decrypt(&body_bytes, head_bytes);
333
334 dlog.is(DEBUG3, DECODE) && dlog << "Unencrypted Body (hex): " << hex_encode(body_bytes) << std::endl;
335
336 Bitset body_bits;
337 body_bits.from_byte_string(body_bytes);
338 dlog.is(DEBUG3, DECODE) && dlog << "Unencrypted Body (bin): " << body_bits << std::endl;
339
340 codec->base_decode(&body_bits, msg, BODY);
341 dlog.is(DEBUG2, DECODE) && dlog << "after header & body decode, message is: " << *msg << std::endl;
342 }
343 }
344 else
345 {
346 throw(Exception("Failed to find (dccl.msg).codec `" + desc->options().GetExtension(dccl::msg).codec() + "`"));
347 }
348
349 dlog.is(DEBUG1, DECODE) && dlog << "Successfully decoded message of type: " << desc->full_name() << std::endl;
350 }
351 catch(std::exception& e)
352 {
353 std::stringstream ss;
354
355 ss << "Message " << hex_encode(bytes) << " failed to decode. Reason: " << e.what() << std::endl;
356
357 dlog.is(DEBUG1, DECODE) && dlog << ss.str() << std::endl;
358 throw(Exception(ss.str()));
359 }
360
361}304}
362305
363// makes sure we can actual encode / decode a message of this descriptor given the loaded FieldCodecs306// makes sure we can actual encode / decode a message of this descriptor given the loaded FieldCodecs
364307
=== modified file 'src/codec.h'
--- src/codec.h 2014-10-08 18:19:16 +0000
+++ src/codec.h 2015-01-22 21:49:07 +0000
@@ -178,6 +178,10 @@
178 unsigned id(const std::string& bytes);178 unsigned id(const std::string& bytes);
179179
180 /// \brief Provides the DCCL ID given a DCCL type.180 /// \brief Provides the DCCL ID given a DCCL type.
181 template<typename CharIterator>
182 unsigned id(CharIterator begin, CharIterator end);
183
184 /// \brief Provides the DCCL ID given a DCCL type.
181 unsigned id(const google::protobuf::Descriptor* desc) const {185 unsigned id(const google::protobuf::Descriptor* desc) const {
182 return desc->options().GetExtension(dccl::msg).id();186 return desc->options().GetExtension(dccl::msg).id();
183 } 187 }
@@ -201,6 +205,26 @@
201 /// \throw Exception if message cannot be encoded.205 /// \throw Exception if message cannot be encoded.
202 void encode(std::string* bytes, const google::protobuf::Message& msg, bool header_only = false);206 void encode(std::string* bytes, const google::protobuf::Message& msg, bool header_only = false);
203 207
208 /// \brief Encodes a DCCL message
209 ///
210 /// \param bytes Output buffer to store encoded msg
211 /// \param max_len Maximum size of output buffer
212 /// \param msg Message to encode (must already have been validated)
213 /// \param header_only If true, only decode the header (do not try to decrypt (if applicable) and decode the message body)
214 /// \throw Exception if message cannot be encoded.
215 /// \return size of encoded message
216 size_t encode(char* bytes, size_t max_len, const google::protobuf::Message& msg, bool header_only = false);
217
218 /// \brief Decode a DCCL message when the type is known at compile time.
219 ///
220 /// \param begin Iterator to the first byte of encoded message to decode (must already have been validated)
221 /// \param end Iterator pointing to the past-the-end character of the message.
222 /// \param msg Pointer to any Google Protobuf Message generated by protoc (i.e. subclass of google::protobuf::Message). The decoded message will be written here.
223 /// \param header_only If true, only decode the header (do not try to decrypt (if applicable) and decode the message body)
224 /// \throw Exception if message cannot be decoded.
225 template <typename CharIterator>
226 void decode(CharIterator begin, CharIterator end, google::protobuf::Message* msg, bool header_only = false);
227
204 /// \brief Decode a DCCL message when the type is known at compile time.228 /// \brief Decode a DCCL message when the type is known at compile time.
205 ///229 ///
206 /// \param bytes encoded message to decode (must already have been validated)230 /// \param bytes encoded message to decode (must already have been validated)
@@ -266,6 +290,8 @@
266 Codec(const Codec&);290 Codec(const Codec&);
267 Codec& operator= (const Codec&);291 Codec& operator= (const Codec&);
268292
293 void encode_internal(const google::protobuf::Message& msg, bool header_only, Bitset& header_bits, Bitset& body_bits);
294
269 void encrypt(std::string* s, const std::string& nonce);295 void encrypt(std::string* s, const std::string& nonce);
270 void decrypt(std::string* s, const std::string& nonce);296 void decrypt(std::string* s, const std::string& nonce);
271297
@@ -326,4 +352,127 @@
326 return msg;352 return msg;
327}353}
328354
355template<typename CharIterator>
356unsigned dccl::Codec::id(CharIterator begin, CharIterator end)
357{
358 unsigned id_min_size = 0, id_max_size = 0;
359 id_codec()->field_min_size(&id_min_size, 0);
360 id_codec()->field_max_size(&id_max_size, 0);
361
362 if(std::distance(begin, end) < (id_min_size / BITS_IN_BYTE))
363 throw(Exception("Bytes passed (hex: " + hex_encode(begin, end) + ") is too small to be a valid DCCL message"));
364
365 Bitset fixed_header_bits;
366 fixed_header_bits.from_byte_stream(begin, begin+(size_t)std::ceil(double(id_max_size) / BITS_IN_BYTE));
367
368 Bitset these_bits(&fixed_header_bits);
369 these_bits.get_more_bits(id_min_size);
370
371 boost::any return_value;
372 id_codec()->field_decode(&these_bits, &return_value, 0);
373
374 return boost::any_cast<uint32>(return_value);
375}
376
377template <typename CharIterator>
378void dccl::Codec::decode(CharIterator begin, CharIterator end, google::protobuf::Message* msg, bool header_only /*= false*/)
379{
380 try
381 {
382 unsigned this_id = id(begin, end);
383
384 dlog.is(logger::DEBUG1, logger::DECODE) && dlog << "Began decoding message of id: " << this_id << std::endl;
385
386 if(!id2desc_.count(this_id))
387 throw(Exception("Message id " + boost::lexical_cast<std::string>(this_id) + " has not been validated. Call validate() before decoding this type."));
388
389 const google::protobuf::Descriptor* desc = msg->GetDescriptor();
390
391 dlog.is(logger::DEBUG1, logger::DECODE) && dlog << "Type name: " << desc->full_name() << std::endl;
392
393 boost::shared_ptr<FieldCodecBase> codec = FieldCodecManager::find(desc);
394 boost::shared_ptr<internal::FromProtoCppTypeBase> helper = internal::TypeHelper::find(desc);
395
396 if(codec)
397 {
398 unsigned head_size_bits;
399 unsigned body_size_bits;
400 codec->base_max_size(&head_size_bits, desc, HEAD);
401 codec->base_max_size(&body_size_bits, desc, BODY);
402 unsigned id_size = 0;
403 id_codec()->field_size(&id_size, this_id, 0);
404 head_size_bits += id_size;
405
406 unsigned head_size_bytes = ceil_bits2bytes(head_size_bits);
407 unsigned body_size_bytes = ceil_bits2bytes(body_size_bits);
408
409 dlog.is(logger::DEBUG2, logger::DECODE) && dlog << "Head bytes (bits): " << head_size_bytes << "(" << head_size_bits
410 << "), max body bytes (bits): " << body_size_bytes << "(" << body_size_bits << ")" << std::endl;
411
412 CharIterator head_bytes_end = begin + head_size_bytes;
413 dlog.is(logger::DEBUG3, logger::DECODE) && dlog << "Unencrypted Head (hex): " << hex_encode(begin, head_bytes_end) << std::endl;
414
415 Bitset head_bits;
416 head_bits.from_byte_stream(begin, head_bytes_end);
417 dlog.is(logger::DEBUG3, logger::DECODE) && dlog << "Unencrypted Head (bin): " << head_bits << std::endl;
418
419 // shift off ID bits
420 head_bits >>= id_size;
421
422 dlog.is(logger::DEBUG3, logger::DECODE) && dlog << "Unencrypted Head after ID bits removal (bin): " << head_bits << std::endl;
423
424 internal::MessageStack msg_stack;
425 msg_stack.push(msg->GetDescriptor());
426
427 codec->base_decode(&head_bits, msg, HEAD);
428 dlog.is(logger::DEBUG2, logger::DECODE) && dlog << "after header decode, message is: " << *msg << std::endl;
429
430
431 if(header_only)
432 {
433 dlog.is(logger::DEBUG2, logger::DECODE) && dlog << "as requested, skipping decrypting and decoding body." << std::endl;
434 }
435 else
436 {
437 dlog.is(logger::DEBUG3, logger::DECODE) && dlog << "Encrypted Body (hex): " << hex_encode(head_bytes_end, end) << std::endl;
438
439 Bitset body_bits;
440 if(!crypto_key_.empty() && !skip_crypto_ids_.count(this_id))
441 {
442 std::string head_bytes(begin, head_bytes_end);
443 std::string body_bytes(head_bytes_end, end);
444 decrypt(&body_bytes, head_bytes);
445 dlog.is(logger::DEBUG3, logger::DECODE) && dlog << "Unencrypted Body (hex): " << hex_encode(body_bytes) << std::endl;
446 body_bits.from_byte_stream(body_bytes.begin(), body_bytes.end());
447 }
448 else
449 {
450 dlog.is(logger::DEBUG3, logger::DECODE) && dlog << "Unencrypted Body (hex): " << hex_encode(head_bytes_end, end) << std::endl;
451 body_bits.from_byte_stream(head_bytes_end, end);
452 }
453
454 dlog.is(logger::DEBUG3, logger::DECODE) && dlog << "Unencrypted Body (bin): " << body_bits << std::endl;
455
456 codec->base_decode(&body_bits, msg, BODY);
457 dlog.is(logger::DEBUG2, logger::DECODE) && dlog << "after header & body decode, message is: " << *msg << std::endl;
458 }
459 }
460 else
461 {
462 throw(Exception("Failed to find (dccl.msg).codec `" + desc->options().GetExtension(dccl::msg).codec() + "`"));
463 }
464
465 dlog.is(logger::DEBUG1, logger::DECODE) && dlog << "Successfully decoded message of type: " << desc->full_name() << std::endl;
466 }
467 catch(std::exception& e)
468 {
469 std::stringstream ss;
470
471 ss << "Message " << hex_encode(begin, end) << " failed to decode. Reason: " << e.what() << std::endl;
472
473 dlog.is(logger::DEBUG1, logger::DECODE) && dlog << ss.str() << std::endl;
474 throw(Exception(ss.str()));
475 }
476}
477
329#endif478#endif

Subscribers

People subscribed via source and target branches

to all changes: