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
1=== modified file 'src/binary.h'
2--- src/binary.h 2014-10-08 18:19:16 +0000
3+++ src/binary.h 2015-01-22 21:49:07 +0000
4@@ -85,27 +85,30 @@
5 return out;
6 }
7
8- /// \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`
9+ /// \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`
10 ///
11- /// \param in byte string to encode (e.g. "TOM")
12+ /// \param begin iterator to first byte of string to encode (e.g. "TOM")
13+ /// \param end iterator pointing to the past-the-end character of the string.
14 /// \param out pointer to string to store result (e.g. "544f4d")
15 /// \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).
16- inline void hex_encode(const std::string& in, std::string* out, bool upper_case = false)
17+ template <typename CharIterator>
18+ inline void hex_encode(CharIterator begin, CharIterator end, std::string* out, bool upper_case = false)
19 {
20 static const short char0_9_to_number = 48;
21 static const short charA_F_to_number = 55;
22 static const short chara_f_to_number = 87;
23
24- int in_size = in.size();
25- int out_size = in_size << 1;
26-
27+ size_t in_size = std::distance(begin, end);
28+ size_t out_size = in_size << 1;
29+
30+ out->clear();
31 out->resize(out_size);
32- for(int i = 0, n = in_size;
33- i < n;
34- ++i)
35+
36+ size_t i = 0;
37+ for(CharIterator it = begin; it != end; ++it)
38 {
39- short msn = (in[i] >> 4) & 0x0f;
40- short lsn = in[i] & 0x0f;
41+ short msn = (*it >> 4) & 0x0f;
42+ short lsn = *it & 0x0f;
43
44 if(msn >= 0 && msn <= 9)
45 (*out)[2*i] = msn + char0_9_to_number;
46@@ -117,9 +120,29 @@
47 else if(lsn >= 10 && lsn <= 15)
48 (*out)[2*i+1] = lsn + (upper_case ? charA_F_to_number : chara_f_to_number);
49
50+ i++;
51 }
52 }
53
54+ template <typename CharIterator>
55+ inline std::string hex_encode(CharIterator begin, CharIterator end)
56+ {
57+ std::string out;
58+ hex_encode(begin, end, &out);
59+ return out;
60+
61+ }
62+
63+ /// \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`
64+ ///
65+ /// \param in byte string to encode (e.g. "TOM")
66+ /// \param out pointer to string to store result (e.g. "544f4d")
67+ /// \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).
68+ inline void hex_encode(const std::string& in, std::string* out, bool upper_case = false)
69+ {
70+ hex_encode(in.begin(), in.end(), out, upper_case);
71+ }
72+
73 inline std::string hex_encode(const std::string& in)
74 {
75 std::string out;
76
77=== modified file 'src/bitset.h'
78--- src/bitset.h 2015-01-14 01:25:28 +0000
79+++ src/bitset.h 2015-01-22 21:49:07 +0000
80@@ -28,6 +28,7 @@
81 #include <algorithm>
82 #include <limits>
83 #include <string>
84+#include <cassert>
85
86 #include "exception.h"
87
88@@ -319,15 +320,44 @@
89 return s;
90 }
91
92+ /// \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.
93+ /// \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]
94+ /// \param max_len Maximum length of buf
95+ /// \return number of bytes written to buf
96+ size_t to_byte_string(char* buf, size_t max_len)
97+ {
98+ // number of bytes needed is ceil(size() / 8)
99+ size_t len = this->size()/8 + (this->size()%8 ? 1 : 0);
100+
101+ assert( max_len >= len );
102+
103+ // initialize buffer to all zeroes
104+ std::memset(buf, 0, len);
105+
106+ for(size_type i = 0, n = this->size(); i < n; ++i)
107+ buf[i/8] |= static_cast<char>((*this)[i] << (i%8));
108+
109+ return len;
110+ }
111+
112 /// \brief Sets the value of the Bitset to the contents of a byte string, where each character represents 8 bits of the Bitset.
113 ///
114 /// \param s A string container the values where the least signficant byte in string[0] and the most significant byte in string[size()-1]
115 void from_byte_string(const std::string& s)
116 {
117- this->resize(s.size() * 8);
118+ from_byte_stream(s.begin(), s.end());
119+ }
120+
121+ /// \brief Sets the value of the Bitset to the contents of a byte string, where each character represents 8 bits of the Bitset.
122+ /// A string container the values where the least signficant byte in string[0] and the most significant byte in string[size()-1]
123+ /// \param begin Iterator pointing to the begining of the input buffer
124+ /// \param end Iterator pointing to the end of the input bufer
125+ template<typename CharIterator>
126+ void from_byte_stream(CharIterator begin, CharIterator end)
127+ {
128+ this->resize(std::distance(begin, end) * 8);
129 int i = 0;
130- for(std::string::const_iterator it = s.begin(), n = s.end();
131- it != n; ++it)
132+ for(CharIterator it = begin; it != end; ++it)
133 {
134 for(size_type j = 0; j < 8; ++j)
135 (*this)[i*8+j] = (*it) & (1 << j);
136
137=== modified file 'src/codec.cpp'
138--- src/codec.cpp 2014-10-05 18:36:46 +0000
139+++ src/codec.cpp 2015-01-22 21:49:07 +0000
140@@ -150,8 +150,7 @@
141 }
142 }
143
144-
145-void dccl::Codec::encode(std::string* bytes, const google::protobuf::Message& msg, bool header_only /* = false */)
146+void dccl::Codec::encode_internal(const google::protobuf::Message& msg, bool header_only, Bitset& head_bits, Bitset& body_bits)
147 {
148 const Descriptor* desc = msg.GetDescriptor();
149
150@@ -159,6 +158,9 @@
151
152 try
153 {
154+ size_t head_byte_size = 0;
155+ size_t body_byte_size = 0;
156+
157 if(!msg.IsInitialized() && !header_only)
158 throw(Exception("Message is not properly initialized. All `required` fields must be set."));
159
160@@ -173,26 +175,15 @@
161 if(codec)
162 {
163 //fixed header
164- Bitset head_bits;
165 id_codec()->field_encode(&head_bits, id(desc), 0);
166
167 internal::MessageStack msg_stack;
168 msg_stack.push(msg.GetDescriptor());
169 codec->base_encode(&head_bits, msg, HEAD);
170
171- std::string body_bytes;
172-
173 // given header of not even byte size (e.g. 01011), make even byte size (e.g. 00001011)
174- unsigned head_byte_size = ceil_bits2bytes(head_bits.size());
175- unsigned head_bits_diff = head_byte_size * BITS_IN_BYTE - (head_bits.size());
176- head_bits.resize(head_bits.size() + head_bits_diff);
177-
178- std::string head_bytes = head_bits.to_byte_string();
179-
180- dlog.is(DEBUG2, ENCODE) && dlog << "Head bytes (bits): " << head_bytes.size() << "(" << head_bits.size() << ")" << std::endl;
181- dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Head (bin): " << head_bits << std::endl;
182- dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Head (hex): " << hex_encode(head_bytes) << std::endl;
183-
184+ head_byte_size = ceil_bits2bytes(head_bits.size());
185+ head_bits.resize(head_byte_size * BITS_IN_BYTE);
186
187 if(header_only)
188 {
189@@ -200,23 +191,8 @@
190 }
191 else
192 {
193- Bitset body_bits;
194 codec->base_encode(&body_bits, msg, BODY);
195- body_bytes = body_bits.to_byte_string();
196- dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Body (bin): " << body_bits << std::endl;
197- dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Body (hex): " << hex_encode(body_bytes) << std::endl;
198- dlog.is(DEBUG2, ENCODE) && dlog << "Body bytes (bits): " << body_bytes.size() << "(" << body_bits.size() << ")" << std::endl;
199-
200- if(!crypto_key_.empty() && !skip_crypto_ids_.count(id(desc)))
201- encrypt(&body_bytes, head_bytes);
202-
203- dlog.is(DEBUG3, ENCODE) && dlog << "Encrypted Body (hex): " << hex_encode(body_bytes) << std::endl;
204 }
205-
206- dlog.is(DEBUG1, ENCODE) && dlog << "Successfully encoded message of type: " << desc->full_name() << std::endl;
207-
208- *bytes += head_bytes + body_bytes;
209-
210 }
211 else
212 {
213@@ -235,27 +211,83 @@
214 }
215 }
216
217-
218+size_t dccl::Codec::encode(char* bytes, size_t max_len, const google::protobuf::Message& msg, bool header_only /* = false */)
219+{
220+ const Descriptor* desc = msg.GetDescriptor();
221+ Bitset head_bits;
222+ Bitset body_bits;
223+ encode_internal(msg, header_only, head_bits, body_bits);
224+
225+ size_t head_byte_size = ceil_bits2bytes(head_bits.size());
226+ assert(max_len >= head_byte_size);
227+ head_bits.to_byte_string(bytes, head_byte_size);
228+
229+ dlog.is(DEBUG2, ENCODE) && dlog << "Head bytes (bits): " << head_byte_size << "(" << head_bits.size() << ")" << std::endl;
230+ dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Head (bin): " << head_bits << std::endl;
231+ dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Head (hex): " << hex_encode(bytes, bytes+head_byte_size) << std::endl;
232+
233+ size_t body_byte_size = 0;
234+ if (!header_only)
235+ {
236+ body_byte_size = ceil_bits2bytes(body_bits.size());
237+ assert(max_len >= head_byte_size + body_byte_size);
238+ body_bits.to_byte_string(bytes+head_byte_size, max_len-head_byte_size);
239+
240+ dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Body (bin): " << body_bits << std::endl;
241+ dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Body (hex): " << hex_encode(bytes+head_byte_size, bytes+head_byte_size+body_byte_size) << std::endl;
242+ dlog.is(DEBUG2, ENCODE) && dlog << "Body bytes (bits): " << body_byte_size << "(" << body_bits.size() << ")" << std::endl;
243+
244+ if(!crypto_key_.empty() && !skip_crypto_ids_.count(id(desc))) {
245+ std::string head_bytes(bytes, bytes+head_byte_size);
246+ std::string body_bytes(bytes+head_byte_size, bytes+head_byte_size+body_byte_size);
247+ encrypt(&body_bytes, head_bytes);
248+ std::memcpy(bytes+head_byte_size, body_bytes.data(), body_bytes.size());
249+ }
250+
251+ 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;
252+ }
253+
254+ dlog.is(DEBUG1, ENCODE) && dlog << "Successfully encoded message of type: " << desc->full_name() << std::endl;
255+
256+ return head_byte_size + body_byte_size;
257+}
258+
259+
260+void dccl::Codec::encode(std::string* bytes, const google::protobuf::Message& msg, bool header_only /* = false */)
261+{
262+ const Descriptor* desc = msg.GetDescriptor();
263+ Bitset head_bits;
264+ Bitset body_bits;
265+ encode_internal(msg, header_only, head_bits, body_bits);
266+
267+ std::string head_bytes = head_bits.to_byte_string();
268+
269+ dlog.is(DEBUG2, ENCODE) && dlog << "Head bytes (bits): " << head_bytes.size() << "(" << head_bits.size() << ")" << std::endl;
270+ dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Head (bin): " << head_bits << std::endl;
271+ dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Head (hex): " << hex_encode(head_bytes) << std::endl;
272+
273+ std::string body_bytes;
274+ if (!header_only)
275+ {
276+ body_bytes = body_bits.to_byte_string();
277+
278+ dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Body (bin): " << body_bits << std::endl;
279+ dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Body (hex): " << hex_encode(body_bytes) << std::endl;
280+ dlog.is(DEBUG2, ENCODE) && dlog << "Body bytes (bits): " << body_bytes.size() << "(" << body_bits.size() << ")" << std::endl;
281+
282+ if(!crypto_key_.empty() && !skip_crypto_ids_.count(id(desc)))
283+ encrypt(&body_bytes, head_bytes);
284+
285+ dlog.is(logger::DEBUG3, logger::ENCODE) && dlog << "Encrypted Body (hex): " << hex_encode(body_bytes) << std::endl;
286+ }
287+
288+ dlog.is(DEBUG1, ENCODE) && dlog << "Successfully encoded message of type: " << desc->full_name() << std::endl;
289+ *bytes += head_bytes + body_bytes;
290+}
291
292 unsigned dccl::Codec::id(const std::string& bytes)
293 {
294- unsigned id_min_size = 0, id_max_size = 0;
295- id_codec()->field_min_size(&id_min_size, 0);
296- id_codec()->field_max_size(&id_max_size, 0);
297-
298- if(bytes.length() < (id_min_size / BITS_IN_BYTE))
299- throw(Exception("Bytes passed (hex: " + hex_encode(bytes) + ") is too small to be a valid DCCL message"));
300-
301- Bitset fixed_header_bits;
302- fixed_header_bits.from_byte_string(bytes.substr(0, (size_t)std::ceil(double(id_max_size) / BITS_IN_BYTE)));
303-
304- Bitset these_bits(&fixed_header_bits);
305- these_bits.get_more_bits(id_min_size);
306-
307- boost::any return_value;
308- id_codec()->field_decode(&these_bits, &return_value, 0);
309-
310- return boost::any_cast<uint32>(return_value);
311+ return id(bytes.begin(), bytes.end());
312 }
313
314
315@@ -268,96 +300,7 @@
316
317 void dccl::Codec::decode(const std::string& bytes, google::protobuf::Message* msg, bool header_only /* = false */)
318 {
319- try
320- {
321- unsigned this_id = id(bytes);
322-
323- dlog.is(DEBUG1, DECODE) && dlog << "Began decoding message of id: " << this_id << std::endl;
324-
325- if(!id2desc_.count(this_id))
326- throw(Exception("Message id " + boost::lexical_cast<std::string>(this_id) + " has not been validated. Call validate() before decoding this type."));
327-
328- const Descriptor* desc = msg->GetDescriptor();
329-
330- dlog.is(DEBUG1, DECODE) && dlog << "Type name: " << desc->full_name() << std::endl;
331-
332- boost::shared_ptr<FieldCodecBase> codec = FieldCodecManager::find(desc);
333- boost::shared_ptr<internal::FromProtoCppTypeBase> helper = internal::TypeHelper::find(desc);
334-
335- if(codec)
336- {
337- unsigned head_size_bits;
338- unsigned body_size_bits;
339- codec->base_max_size(&head_size_bits, desc, HEAD);
340- codec->base_max_size(&body_size_bits, desc, BODY);
341- unsigned id_size = 0;
342- id_codec()->field_size(&id_size, this_id, 0);
343- head_size_bits += id_size;
344-
345- unsigned head_size_bytes = ceil_bits2bytes(head_size_bits);
346- unsigned body_size_bytes = ceil_bits2bytes(body_size_bits);
347-
348- dlog.is(DEBUG2, DECODE) && dlog << "Head bytes (bits): " << head_size_bytes << "(" << head_size_bits
349- << "), max body bytes (bits): " << body_size_bytes << "(" << body_size_bits << ")" << std::endl;
350-
351- std::string head_bytes = bytes.substr(0, head_size_bytes);
352- dlog.is(DEBUG3, DECODE) && dlog << "Unencrypted Head (hex): " << hex_encode(head_bytes) << std::endl;
353-
354- Bitset head_bits;
355- head_bits.from_byte_string(head_bytes);
356- dlog.is(DEBUG3, DECODE) && dlog << "Unencrypted Head (bin): " << head_bits << std::endl;
357-
358- // shift off ID bits
359- head_bits >>= id_size;
360-
361- dlog.is(DEBUG3, DECODE) && dlog << "Unencrypted Head after ID bits removal (bin): " << head_bits << std::endl;
362-
363- internal::MessageStack msg_stack;
364- msg_stack.push(msg->GetDescriptor());
365-
366- codec->base_decode(&head_bits, msg, HEAD);
367- dlog.is(DEBUG2, DECODE) && dlog << "after header decode, message is: " << *msg << std::endl;
368-
369-
370- if(header_only)
371- {
372- dlog.is(DEBUG2, DECODE) && dlog << "as requested, skipping decrypting and decoding body." << std::endl;
373- }
374- else
375- {
376- std::string body_bytes = bytes.substr(head_size_bytes);
377- dlog.is(DEBUG3, DECODE) && dlog << "Encrypted Body (hex): " << hex_encode(body_bytes) << std::endl;
378-
379- if(!crypto_key_.empty() && !skip_crypto_ids_.count(this_id))
380- decrypt(&body_bytes, head_bytes);
381-
382- dlog.is(DEBUG3, DECODE) && dlog << "Unencrypted Body (hex): " << hex_encode(body_bytes) << std::endl;
383-
384- Bitset body_bits;
385- body_bits.from_byte_string(body_bytes);
386- dlog.is(DEBUG3, DECODE) && dlog << "Unencrypted Body (bin): " << body_bits << std::endl;
387-
388- codec->base_decode(&body_bits, msg, BODY);
389- dlog.is(DEBUG2, DECODE) && dlog << "after header & body decode, message is: " << *msg << std::endl;
390- }
391- }
392- else
393- {
394- throw(Exception("Failed to find (dccl.msg).codec `" + desc->options().GetExtension(dccl::msg).codec() + "`"));
395- }
396-
397- dlog.is(DEBUG1, DECODE) && dlog << "Successfully decoded message of type: " << desc->full_name() << std::endl;
398- }
399- catch(std::exception& e)
400- {
401- std::stringstream ss;
402-
403- ss << "Message " << hex_encode(bytes) << " failed to decode. Reason: " << e.what() << std::endl;
404-
405- dlog.is(DEBUG1, DECODE) && dlog << ss.str() << std::endl;
406- throw(Exception(ss.str()));
407- }
408-
409+ decode(bytes.begin(), bytes.end(), msg, header_only);
410 }
411
412 // makes sure we can actual encode / decode a message of this descriptor given the loaded FieldCodecs
413
414=== modified file 'src/codec.h'
415--- src/codec.h 2014-10-08 18:19:16 +0000
416+++ src/codec.h 2015-01-22 21:49:07 +0000
417@@ -178,6 +178,10 @@
418 unsigned id(const std::string& bytes);
419
420 /// \brief Provides the DCCL ID given a DCCL type.
421+ template<typename CharIterator>
422+ unsigned id(CharIterator begin, CharIterator end);
423+
424+ /// \brief Provides the DCCL ID given a DCCL type.
425 unsigned id(const google::protobuf::Descriptor* desc) const {
426 return desc->options().GetExtension(dccl::msg).id();
427 }
428@@ -201,6 +205,26 @@
429 /// \throw Exception if message cannot be encoded.
430 void encode(std::string* bytes, const google::protobuf::Message& msg, bool header_only = false);
431
432+ /// \brief Encodes a DCCL message
433+ ///
434+ /// \param bytes Output buffer to store encoded msg
435+ /// \param max_len Maximum size of output buffer
436+ /// \param msg Message to encode (must already have been validated)
437+ /// \param header_only If true, only decode the header (do not try to decrypt (if applicable) and decode the message body)
438+ /// \throw Exception if message cannot be encoded.
439+ /// \return size of encoded message
440+ size_t encode(char* bytes, size_t max_len, const google::protobuf::Message& msg, bool header_only = false);
441+
442+ /// \brief Decode a DCCL message when the type is known at compile time.
443+ ///
444+ /// \param begin Iterator to the first byte of encoded message to decode (must already have been validated)
445+ /// \param end Iterator pointing to the past-the-end character of the message.
446+ /// \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.
447+ /// \param header_only If true, only decode the header (do not try to decrypt (if applicable) and decode the message body)
448+ /// \throw Exception if message cannot be decoded.
449+ template <typename CharIterator>
450+ void decode(CharIterator begin, CharIterator end, google::protobuf::Message* msg, bool header_only = false);
451+
452 /// \brief Decode a DCCL message when the type is known at compile time.
453 ///
454 /// \param bytes encoded message to decode (must already have been validated)
455@@ -266,6 +290,8 @@
456 Codec(const Codec&);
457 Codec& operator= (const Codec&);
458
459+ void encode_internal(const google::protobuf::Message& msg, bool header_only, Bitset& header_bits, Bitset& body_bits);
460+
461 void encrypt(std::string* s, const std::string& nonce);
462 void decrypt(std::string* s, const std::string& nonce);
463
464@@ -326,4 +352,127 @@
465 return msg;
466 }
467
468+template<typename CharIterator>
469+unsigned dccl::Codec::id(CharIterator begin, CharIterator end)
470+{
471+ unsigned id_min_size = 0, id_max_size = 0;
472+ id_codec()->field_min_size(&id_min_size, 0);
473+ id_codec()->field_max_size(&id_max_size, 0);
474+
475+ if(std::distance(begin, end) < (id_min_size / BITS_IN_BYTE))
476+ throw(Exception("Bytes passed (hex: " + hex_encode(begin, end) + ") is too small to be a valid DCCL message"));
477+
478+ Bitset fixed_header_bits;
479+ fixed_header_bits.from_byte_stream(begin, begin+(size_t)std::ceil(double(id_max_size) / BITS_IN_BYTE));
480+
481+ Bitset these_bits(&fixed_header_bits);
482+ these_bits.get_more_bits(id_min_size);
483+
484+ boost::any return_value;
485+ id_codec()->field_decode(&these_bits, &return_value, 0);
486+
487+ return boost::any_cast<uint32>(return_value);
488+}
489+
490+template <typename CharIterator>
491+void dccl::Codec::decode(CharIterator begin, CharIterator end, google::protobuf::Message* msg, bool header_only /*= false*/)
492+{
493+ try
494+ {
495+ unsigned this_id = id(begin, end);
496+
497+ dlog.is(logger::DEBUG1, logger::DECODE) && dlog << "Began decoding message of id: " << this_id << std::endl;
498+
499+ if(!id2desc_.count(this_id))
500+ throw(Exception("Message id " + boost::lexical_cast<std::string>(this_id) + " has not been validated. Call validate() before decoding this type."));
501+
502+ const google::protobuf::Descriptor* desc = msg->GetDescriptor();
503+
504+ dlog.is(logger::DEBUG1, logger::DECODE) && dlog << "Type name: " << desc->full_name() << std::endl;
505+
506+ boost::shared_ptr<FieldCodecBase> codec = FieldCodecManager::find(desc);
507+ boost::shared_ptr<internal::FromProtoCppTypeBase> helper = internal::TypeHelper::find(desc);
508+
509+ if(codec)
510+ {
511+ unsigned head_size_bits;
512+ unsigned body_size_bits;
513+ codec->base_max_size(&head_size_bits, desc, HEAD);
514+ codec->base_max_size(&body_size_bits, desc, BODY);
515+ unsigned id_size = 0;
516+ id_codec()->field_size(&id_size, this_id, 0);
517+ head_size_bits += id_size;
518+
519+ unsigned head_size_bytes = ceil_bits2bytes(head_size_bits);
520+ unsigned body_size_bytes = ceil_bits2bytes(body_size_bits);
521+
522+ dlog.is(logger::DEBUG2, logger::DECODE) && dlog << "Head bytes (bits): " << head_size_bytes << "(" << head_size_bits
523+ << "), max body bytes (bits): " << body_size_bytes << "(" << body_size_bits << ")" << std::endl;
524+
525+ CharIterator head_bytes_end = begin + head_size_bytes;
526+ dlog.is(logger::DEBUG3, logger::DECODE) && dlog << "Unencrypted Head (hex): " << hex_encode(begin, head_bytes_end) << std::endl;
527+
528+ Bitset head_bits;
529+ head_bits.from_byte_stream(begin, head_bytes_end);
530+ dlog.is(logger::DEBUG3, logger::DECODE) && dlog << "Unencrypted Head (bin): " << head_bits << std::endl;
531+
532+ // shift off ID bits
533+ head_bits >>= id_size;
534+
535+ dlog.is(logger::DEBUG3, logger::DECODE) && dlog << "Unencrypted Head after ID bits removal (bin): " << head_bits << std::endl;
536+
537+ internal::MessageStack msg_stack;
538+ msg_stack.push(msg->GetDescriptor());
539+
540+ codec->base_decode(&head_bits, msg, HEAD);
541+ dlog.is(logger::DEBUG2, logger::DECODE) && dlog << "after header decode, message is: " << *msg << std::endl;
542+
543+
544+ if(header_only)
545+ {
546+ dlog.is(logger::DEBUG2, logger::DECODE) && dlog << "as requested, skipping decrypting and decoding body." << std::endl;
547+ }
548+ else
549+ {
550+ dlog.is(logger::DEBUG3, logger::DECODE) && dlog << "Encrypted Body (hex): " << hex_encode(head_bytes_end, end) << std::endl;
551+
552+ Bitset body_bits;
553+ if(!crypto_key_.empty() && !skip_crypto_ids_.count(this_id))
554+ {
555+ std::string head_bytes(begin, head_bytes_end);
556+ std::string body_bytes(head_bytes_end, end);
557+ decrypt(&body_bytes, head_bytes);
558+ dlog.is(logger::DEBUG3, logger::DECODE) && dlog << "Unencrypted Body (hex): " << hex_encode(body_bytes) << std::endl;
559+ body_bits.from_byte_stream(body_bytes.begin(), body_bytes.end());
560+ }
561+ else
562+ {
563+ dlog.is(logger::DEBUG3, logger::DECODE) && dlog << "Unencrypted Body (hex): " << hex_encode(head_bytes_end, end) << std::endl;
564+ body_bits.from_byte_stream(head_bytes_end, end);
565+ }
566+
567+ dlog.is(logger::DEBUG3, logger::DECODE) && dlog << "Unencrypted Body (bin): " << body_bits << std::endl;
568+
569+ codec->base_decode(&body_bits, msg, BODY);
570+ dlog.is(logger::DEBUG2, logger::DECODE) && dlog << "after header & body decode, message is: " << *msg << std::endl;
571+ }
572+ }
573+ else
574+ {
575+ throw(Exception("Failed to find (dccl.msg).codec `" + desc->options().GetExtension(dccl::msg).codec() + "`"));
576+ }
577+
578+ dlog.is(logger::DEBUG1, logger::DECODE) && dlog << "Successfully decoded message of type: " << desc->full_name() << std::endl;
579+ }
580+ catch(std::exception& e)
581+ {
582+ std::stringstream ss;
583+
584+ ss << "Message " << hex_encode(begin, end) << " failed to decode. Reason: " << e.what() << std::endl;
585+
586+ dlog.is(logger::DEBUG1, logger::DECODE) && dlog << ss.str() << std::endl;
587+ throw(Exception(ss.str()));
588+ }
589+}
590+
591 #endif

Subscribers

People subscribed via source and target branches

to all changes: