26 #include "dccl/codec.h"
29 #if CRYPTOPP_PATH_USES_PLUS_SIGN
30 #include <crypto++/filters.h>
31 #include <crypto++/sha.h>
32 #include <crypto++/modes.h>
33 #include <crypto++/aes.h>
35 #include <cryptopp/filters.h>
36 #include <cryptopp/sha.h>
37 #include <cryptopp/modes.h>
38 #include <cryptopp/aes.h>
39 #endif // CRYPTOPP_PATH_USES_PLUS_SIGN
40 #endif // HAS_CRYPTOPP
42 #include "dccl/codecs2/field_codec_default.h"
43 #include "dccl/codecs3/field_codec_default.h"
44 #include "dccl/codecs3/field_codec_var_bytes.h"
45 #include "dccl/codecs3/field_codec_presence.h"
46 #include "dccl/field_codec_id.h"
48 #include "dccl/option_extensions.pb.h"
55 using namespace dccl::logger;
57 using google::protobuf::FieldDescriptor;
58 using google::protobuf::Descriptor;
59 using google::protobuf::Reflection;
61 const unsigned full_width = 60;
69 : strict_(false), id_codec_(dccl_id_codec)
72 FieldCodecManager::add<DefaultIdentifierCodec>(default_id_codec_name());
74 if(!library_path.empty())
82 for(std::vector<void *>::iterator it = dl_handles_.begin(),
83 n = dl_handles_.end(); it != n; ++it)
91 void dccl::Codec::set_default_codecs()
94 static bool defaults_loaded =
false;
98 using google::protobuf::FieldDescriptor;
101 FieldCodecManager::add<v2::DefaultNumericFieldCodec<double> >(default_codec_name());
102 FieldCodecManager::add<v2::DefaultNumericFieldCodec<float> >(default_codec_name());
103 FieldCodecManager::add<v2::DefaultBoolCodec>(default_codec_name());
104 FieldCodecManager::add<v2::DefaultNumericFieldCodec<int32> >(default_codec_name());
105 FieldCodecManager::add<v2::DefaultNumericFieldCodec<int64> >(default_codec_name());
106 FieldCodecManager::add<v2::DefaultNumericFieldCodec<uint32> >(default_codec_name());
107 FieldCodecManager::add<v2::DefaultNumericFieldCodec<uint64> >(default_codec_name());
108 FieldCodecManager::add<v2::DefaultStringCodec, FieldDescriptor::TYPE_STRING>(default_codec_name());
109 FieldCodecManager::add<v2::DefaultBytesCodec, FieldDescriptor::TYPE_BYTES>(default_codec_name());
110 FieldCodecManager::add<v2::DefaultEnumCodec >(default_codec_name());
111 FieldCodecManager::add<v2::DefaultMessageCodec, FieldDescriptor::TYPE_MESSAGE>(default_codec_name());
113 FieldCodecManager::add<v2::TimeCodec<uint64> >(
"dccl.time2");
114 FieldCodecManager::add<v2::TimeCodec<int64> >(
"dccl.time2");
115 FieldCodecManager::add<v2::TimeCodec<double> >(
"dccl.time2");
117 FieldCodecManager::add<v2::StaticCodec<std::string> >(
"dccl.static2");
118 FieldCodecManager::add<v2::StaticCodec<double> >(
"dccl.static2");
119 FieldCodecManager::add<v2::StaticCodec<float> >(
"dccl.static2");
120 FieldCodecManager::add<v2::StaticCodec<int32> >(
"dccl.static2");
121 FieldCodecManager::add<v2::StaticCodec<int64> >(
"dccl.static2");
122 FieldCodecManager::add<v2::StaticCodec<uint32> >(
"dccl.static2");
123 FieldCodecManager::add<v2::StaticCodec<uint64> >(
"dccl.static2");
126 FieldCodecManager::add<v3::DefaultNumericFieldCodec<double> >(default_codec_name(3));
127 FieldCodecManager::add<v3::DefaultNumericFieldCodec<float> >(default_codec_name(3));
128 FieldCodecManager::add<v3::DefaultBoolCodec>(default_codec_name(3));
129 FieldCodecManager::add<v3::DefaultNumericFieldCodec<int32> >(default_codec_name(3));
130 FieldCodecManager::add<v3::DefaultNumericFieldCodec<int64> >(default_codec_name(3));
131 FieldCodecManager::add<v3::DefaultNumericFieldCodec<uint32> >(default_codec_name(3));
132 FieldCodecManager::add<v3::DefaultNumericFieldCodec<uint64> >(default_codec_name(3));
133 FieldCodecManager::add<v3::DefaultStringCodec, FieldDescriptor::TYPE_STRING>(default_codec_name(3));
134 FieldCodecManager::add<v3::DefaultBytesCodec, FieldDescriptor::TYPE_BYTES>(default_codec_name(3));
135 FieldCodecManager::add<v3::DefaultEnumCodec >(default_codec_name(3));
136 FieldCodecManager::add<v3::DefaultMessageCodec, FieldDescriptor::TYPE_MESSAGE>(default_codec_name(3));
139 FieldCodecManager::add<v3::PresenceBitCodec<v3::DefaultNumericFieldCodec<double> > >(
"dccl.presence");
140 FieldCodecManager::add<v3::PresenceBitCodec<v3::DefaultNumericFieldCodec<float> > >(
"dccl.presence");
141 FieldCodecManager::add<v3::PresenceBitCodec<v3::DefaultNumericFieldCodec<int32> > >(
"dccl.presence");
142 FieldCodecManager::add<v3::PresenceBitCodec<v3::DefaultNumericFieldCodec<int64> > >(
"dccl.presence");
143 FieldCodecManager::add<v3::PresenceBitCodec<v3::DefaultNumericFieldCodec<uint32> > >(
"dccl.presence");
144 FieldCodecManager::add<v3::PresenceBitCodec<v3::DefaultNumericFieldCodec<uint64> > >(
"dccl.presence");
145 FieldCodecManager::add<v3::PresenceBitCodec<v3::DefaultEnumCodec > >(
"dccl.presence");
148 FieldCodecManager::add<v3::VarBytesCodec, FieldDescriptor::TYPE_BYTES>(
"dccl.var_bytes");
151 FieldCodecManager::add<v2::TimeCodec<uint64> >(
"_time");
152 FieldCodecManager::add<v2::TimeCodec<int64> >(
"_time");
153 FieldCodecManager::add<v2::TimeCodec<double> >(
"_time");
155 FieldCodecManager::add<v2::StaticCodec<std::string> >(
"_static");
156 FieldCodecManager::add<v2::StaticCodec<double> >(
"_static");
157 FieldCodecManager::add<v2::StaticCodec<float> >(
"_static");
158 FieldCodecManager::add<v2::StaticCodec<int32> >(
"_static");
159 FieldCodecManager::add<v2::StaticCodec<int64> >(
"_static");
160 FieldCodecManager::add<v2::StaticCodec<uint32> >(
"_static");
161 FieldCodecManager::add<v2::StaticCodec<uint64> >(
"_static");
164 defaults_loaded =
true;
170 const Descriptor* desc = msg.GetDescriptor();
172 dlog.
is(DEBUG1, ENCODE) && dlog <<
"Began encoding message of type: " << desc->full_name() << std::endl;
176 unsigned dccl_id = (user_id < 0) ?
id(desc) : user_id;
177 size_t head_byte_size = 0;
179 if(!msg.IsInitialized() && !header_only)
180 throw(
Exception(
"Message is not properly initialized. All `required` fields must be set."));
182 if(!id2desc_.count(dccl_id))
183 throw(
Exception(
"Message id " + boost::lexical_cast<std::string>(dccl_id) +
" has not been loaded. Call load() before encoding this type."));
188 boost::shared_ptr<internal::FromProtoCppTypeBase> helper = internal::TypeHelper::find(desc);
193 id_codec()->field_encode(&head_bits, dccl_id, 0);
196 msg_stack.push(msg.GetDescriptor());
197 codec->base_encode(&head_bits, msg, HEAD, strict_);
200 head_byte_size = ceil_bits2bytes(head_bits.size());
201 head_bits.resize(head_byte_size * BITS_IN_BYTE);
205 dlog.
is(DEBUG2, ENCODE) && dlog <<
"as requested, skipping encoding and encrypting body." << std::endl;
209 codec->base_encode(&body_bits, msg, BODY, strict_);
214 throw(
Exception(
"Failed to find (dccl.msg).codec `" + desc->options().GetExtension(dccl::msg).codec() +
"`"));
220 dlog.
is(DEBUG1, ENCODE) && dlog <<
"Message " << desc->full_name() <<
" failed to encode because a field was out of bounds and strict == true: " << e.what() << std::endl;
223 catch(std::exception& e)
225 std::stringstream ss;
227 ss <<
"Message " << desc->full_name() <<
" failed to encode. Reason: " << e.what();
229 dlog.
is(DEBUG1, ENCODE) && dlog << ss.str() << std::endl;
236 const Descriptor* desc = msg.GetDescriptor();
239 encode_internal(msg, header_only, head_bits, body_bits, user_id);
241 size_t head_byte_size = ceil_bits2bytes(head_bits.size());
242 if (max_len < head_byte_size)
244 throw std::length_error(
"max_len must be >= head_byte_size");
248 dlog.
is(DEBUG2, ENCODE) && dlog <<
"Head bytes (bits): " << head_byte_size <<
"(" << head_bits.size() <<
")" << std::endl;
249 dlog.
is(DEBUG3, ENCODE) && dlog <<
"Unencrypted Head (bin): " << head_bits << std::endl;
250 dlog.
is(DEBUG3, ENCODE) && dlog <<
"Unencrypted Head (hex): " <<
hex_encode(bytes, bytes+head_byte_size) << std::endl;
252 size_t body_byte_size = 0;
255 body_byte_size = ceil_bits2bytes(body_bits.size());
256 if (max_len < (head_byte_size + body_byte_size))
258 throw std::length_error(
"max_len must be >= (head_byte_size + body_byte_size)");
260 body_bits.
to_byte_string(bytes+head_byte_size, max_len-head_byte_size);
262 dlog.
is(DEBUG3, ENCODE) && dlog <<
"Unencrypted Body (bin): " << body_bits << std::endl;
263 dlog.
is(DEBUG3, ENCODE) && dlog <<
"Unencrypted Body (hex): " <<
hex_encode(bytes+head_byte_size, bytes+head_byte_size+body_byte_size) << std::endl;
264 dlog.
is(DEBUG2, ENCODE) && dlog <<
"Body bytes (bits): " << body_byte_size <<
"(" << body_bits.size() <<
")" << std::endl;
266 unsigned dccl_id = (user_id < 0) ?
id(desc) : user_id;
267 if(!crypto_key_.empty() && !skip_crypto_ids_.count(dccl_id)) {
268 std::string head_bytes(bytes, bytes+head_byte_size);
269 std::string body_bytes(bytes+head_byte_size, bytes+head_byte_size+body_byte_size);
270 encrypt(&body_bytes, head_bytes);
271 std::memcpy(bytes+head_byte_size, body_bytes.data(), body_bytes.size());
274 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;
277 dlog.
is(DEBUG1, ENCODE) && dlog <<
"Successfully encoded message of type: " << desc->full_name() << std::endl;
279 return head_byte_size + body_byte_size;
285 const Descriptor* desc = msg.GetDescriptor();
288 encode_internal(msg, header_only, head_bits, body_bits, user_id);
292 dlog.
is(DEBUG2, ENCODE) && dlog <<
"Head bytes (bits): " << head_bytes.size() <<
"(" << head_bits.size() <<
")" << std::endl;
293 dlog.
is(DEBUG3, ENCODE) && dlog <<
"Unencrypted Head (bin): " << head_bits << std::endl;
294 dlog.
is(DEBUG3, ENCODE) && dlog <<
"Unencrypted Head (hex): " <<
hex_encode(head_bytes) << std::endl;
296 std::string body_bytes;
301 dlog.
is(DEBUG3, ENCODE) && dlog <<
"Unencrypted Body (bin): " << body_bits << std::endl;
302 dlog.
is(DEBUG3, ENCODE) && dlog <<
"Unencrypted Body (hex): " <<
hex_encode(body_bytes) << std::endl;
303 dlog.
is(DEBUG2, ENCODE) && dlog <<
"Body bytes (bits): " << body_bytes.size() <<
"(" << body_bits.size() <<
")" << std::endl;
305 unsigned dccl_id = (user_id < 0) ?
id(desc) : user_id;
306 if(!crypto_key_.empty() && !skip_crypto_ids_.count(dccl_id))
307 encrypt(&body_bytes, head_bytes);
309 dlog.
is(logger::DEBUG3, logger::ENCODE) && dlog <<
"Encrypted Body (hex): " <<
hex_encode(body_bytes) << std::endl;
312 dlog.
is(DEBUG1, ENCODE) && dlog <<
"Successfully encoded message of type: " << desc->full_name() << std::endl;
313 *bytes += head_bytes + body_bytes;
318 return id(bytes.begin(), bytes.end());
325 unsigned last_size = size(*msg);
326 bytes->erase(0, last_size);
331 decode(bytes.begin(), bytes.end(), msg, header_only);
340 if(user_id <0 && !desc->options().GetExtension(dccl::msg).has_id())
341 throw(
Exception(
"Missing message option `(dccl.msg).id`. Specify a unique id (e.g. 3) in the body of your .proto message using \"option (dccl.msg).id = 3\""));
342 if(!desc->options().GetExtension(dccl::msg).has_max_bytes())
343 throw(
Exception(
"Missing message option `(dccl.msg).max_bytes`. Specify a maximum (encoded) message size in bytes (e.g. 32) in the body of your .proto message using \"option (dccl.msg).max_bytes = 32\""));
345 if(!desc->options().GetExtension(dccl::msg).has_codec_version())
346 dlog.
is(WARN) && dlog <<
"** NOTE: No (dccl.msg).codec_version set for DCCL Message '" << desc->full_name() <<
"'. Unless you need backwards compatibility with Goby 2.0 (DCCL2), we highly recommend setting 'option (dccl.msg).codec_version = 3' in the message definition for " << desc->full_name() <<
" to use the default DCCL3 codecs. If you need compatibility with Goby 2.0, ignore this warning, or set 'option (dccl.msg).codec_version = 2' to remove this warning. **" << std::endl;
350 unsigned dccl_id = (user_id < 0) ?
id(desc) : user_id;
351 unsigned head_size_bits, body_size_bits;
352 codec->base_max_size(&head_size_bits, desc, HEAD);
353 codec->base_max_size(&body_size_bits, desc, BODY);
355 unsigned id_bits = 0;
356 id_codec()->field_size(&id_bits, dccl_id, 0);
357 head_size_bits += id_bits;
359 const unsigned byte_size = ceil_bits2bytes(head_size_bits) + ceil_bits2bytes(body_size_bits);
361 if(byte_size > desc->options().GetExtension(dccl::msg).max_bytes())
362 throw(
Exception(
"Actual maximum size of message exceeds allowed maximum (dccl.max_bytes). Tighten bounds, remove fields, improve codecs, or increase the allowed dccl.max_bytes"));
364 codec->base_validate(desc, HEAD);
365 codec->base_validate(desc, BODY);
367 if(id2desc_.count(dccl_id) && desc != id2desc_.find(dccl_id)->second)
368 throw(
Exception(
"`dccl id` " + boost::lexical_cast<std::string>(dccl_id) +
" is already in use by Message " + id2desc_.find(dccl_id)->second->full_name() +
": " + boost::lexical_cast<std::string>(id2desc_.find(dccl_id)->second)));
370 id2desc_.insert(std::make_pair(dccl_id, desc));
372 dlog.
is(DEBUG1) && dlog <<
"Successfully validated message of type: " << desc->full_name() << std::endl;
384 dlog.
is(DEBUG1) && dlog <<
"Message " << desc->full_name() <<
": " << desc <<
" failed validation. Reason: "
386 <<
"If possible, information about the Message are printed above. " << std::endl;
395 unsigned int erased = 0;
396 for (std::map<int32, const google::protobuf::Descriptor*>::iterator it = id2desc_.begin(); it != id2desc_.end();)
398 if (it->second == desc)
401 id2desc_.erase(it++);
410 dlog.
is(DEBUG1) && dlog <<
"Message " << desc->full_name() <<
": is not loaded. Ignoring unload request." << std::endl;
417 if(id2desc_.count(dccl_id))
419 id2desc_.erase(dccl_id);
423 dlog.
is(DEBUG1) && dlog <<
"Message with id " << dccl_id <<
": is not loaded. Ignoring unload request." << std::endl;
431 const Descriptor* desc = msg.GetDescriptor();
435 unsigned dccl_id = (user_id < 0) ?
id(desc) : user_id;
436 unsigned head_size_bits;
437 codec->base_size(&head_size_bits, msg, HEAD);
439 unsigned id_bits = 0;
440 id_codec()->field_size(&id_bits, dccl_id, 0);
441 head_size_bits += id_bits;
443 unsigned body_size_bits;
444 codec->base_size(&body_size_bits, msg, BODY);
446 const unsigned head_size_bytes = ceil_bits2bytes(head_size_bits);
447 const unsigned body_size_bytes = ceil_bits2bytes(body_size_bits);
448 return head_size_bytes + body_size_bytes;
456 unsigned head_size_bits;
457 codec->base_max_size(&head_size_bits, desc, HEAD);
459 unsigned id_bits = 0;
460 id_codec()->field_max_size(&id_bits, 0);
461 head_size_bits += id_bits;
463 unsigned body_size_bits;
464 codec->base_max_size(&body_size_bits, desc, BODY);
466 const unsigned head_size_bytes = ceil_bits2bytes(head_size_bits);
467 const unsigned body_size_bytes = ceil_bits2bytes(body_size_bits);
468 return head_size_bytes + body_size_bytes;
475 unsigned head_size_bits;
476 codec->base_min_size(&head_size_bits, desc, HEAD);
478 unsigned id_bits = 0;
479 id_codec()->field_min_size(&id_bits, 0);
480 head_size_bits += id_bits;
482 unsigned body_size_bits;
483 codec->base_min_size(&body_size_bits, desc, BODY);
485 const unsigned head_size_bytes = ceil_bits2bytes(head_size_bits);
486 const unsigned body_size_bytes = ceil_bits2bytes(body_size_bits);
487 return head_size_bytes + body_size_bytes;
492 void dccl::Codec::info(
const google::protobuf::Descriptor* desc, std::ostream* param_os ,
int user_id )
const
494 std::ostream* os = (param_os) ? param_os : &dlog;
496 if(param_os || dlog.
is(INFO))
502 unsigned config_head_bit_size, body_bit_size;
503 codec->base_max_size(&config_head_bit_size, desc, HEAD);
504 codec->base_max_size(&body_bit_size, desc, BODY);
506 unsigned dccl_id = (user_id < 0) ?
id(desc) : user_id;
507 unsigned id_bit_size = 0;
508 id_codec()->field_size(&id_bit_size, dccl_id, 0);
510 const unsigned bit_size = id_bit_size + config_head_bit_size + body_bit_size;
513 const unsigned byte_size = ceil_bits2bytes(config_head_bit_size + id_bit_size) + ceil_bits2bytes(body_bit_size);
515 const unsigned allowed_byte_size = desc->options().GetExtension(dccl::msg).max_bytes();
516 const unsigned allowed_bit_size = allowed_byte_size * BITS_IN_BYTE;
518 std::string message_name = boost::lexical_cast<std::string>(dccl_id) +
": " + desc->full_name();
519 std::string guard = std::string((full_width-message_name.size())/2,
'=');
521 std::string bits_dccl_head_str =
"dccl.id head";
522 std::string bits_user_head_str =
"user head";
523 std::string bits_body_str =
"body";
524 std::string bits_padding_str =
"padding to full byte";
526 const int bits_width = 40;
527 const int spaces = 8;
528 std::string indent = std::string(spaces,
' ');
530 *os << guard <<
" " << message_name <<
" " << guard <<
"\n"
531 <<
"Actual maximum size of message: " << byte_size <<
" bytes / "
532 << byte_size*BITS_IN_BYTE <<
" bits\n"
533 << indent << bits_dccl_head_str << std::setfill(
'.') << std::setw(bits_width-bits_dccl_head_str.size()) << id_bit_size <<
"\n"
534 << indent << bits_user_head_str << std::setfill(
'.') << std::setw(bits_width-bits_user_head_str.size()) << config_head_bit_size <<
"\n"
535 << indent << bits_body_str << std::setfill(
'.') << std::setw(bits_width-bits_body_str.size()) << body_bit_size <<
"\n"
536 << indent << bits_padding_str << std::setfill(
'.') << std::setw(bits_width-bits_padding_str.size()) << byte_size * BITS_IN_BYTE - bit_size <<
"\n"
537 <<
"Allowed maximum size of message: " << allowed_byte_size <<
" bytes / "
538 << allowed_bit_size <<
" bits\n";
540 std::string header_str =
"Header";
541 std::string header_guard = std::string((full_width-header_str.size())/2,
'-');
542 *os << header_guard <<
" " << header_str <<
" " << header_guard << std::endl;
543 *os << bits_dccl_head_str << std::setfill(
'.') << std::setw(bits_width-bits_dccl_head_str.size()+spaces) << id_bit_size <<
" {" << id_codec()->name() <<
"}\n";
544 codec->base_info(os, desc, HEAD);
547 std::string body_str =
"Body";
548 std::string body_guard = std::string((full_width-body_str.size())/2,
'-');
549 *os << body_guard <<
" " << body_str <<
" " << body_guard << std::endl;
550 codec->base_info(os, desc, BODY);
557 dlog.
is(DEBUG1) && dlog <<
"Message " << desc->full_name() <<
" cannot provide information due to invalid configuration. Reason: " << e.what() << std::endl;
567 void dccl::Codec::encrypt(std::string* s,
const std::string& nonce )
569 #if DCCL_HAS_CRYPTOPP
570 using namespace CryptoPP;
574 StringSource unused(nonce,
true,
new HashFilter(hash,
new StringSink(iv)));
576 CTR_Mode<AES>::Encryption encryptor;
577 encryptor.SetKeyWithIV((
byte*)crypto_key_.c_str(), crypto_key_.size(), (
byte*)iv.c_str());
580 StreamTransformationFilter in(encryptor,
new StringSink(cipher));
581 in.Put((
byte*)s->c_str(), s->size());
587 void dccl::Codec::decrypt(std::string* s,
const std::string& nonce)
589 #if DCCL_HAS_CRYPTOPP
590 using namespace CryptoPP;
594 StringSource unused(nonce,
true,
new HashFilter(hash,
new StringSink(iv)));
596 CTR_Mode<AES>::Decryption decryptor;
597 decryptor.SetKeyWithIV((
byte*)crypto_key_.c_str(), crypto_key_.size(), (
byte*)iv.c_str());
599 std::string recovered;
601 StreamTransformationFilter out(decryptor,
new StringSink(recovered));
602 out.Put((
byte*)s->c_str(), s->size());
610 void* handle = dlopen(library_path.c_str(), RTLD_LAZY);
612 dl_handles_.push_back(handle);
613 load_library(handle);
619 throw(
Exception(
"Null shared library handle passed to load_library"));
623 dccl_load_ptr = (void (*)(
dccl::Codec*)) dlsym(dl_handle,
"dccl3_load");
625 (*dccl_load_ptr)(
this);
631 throw(
Exception(
"Null shared library handle passed to unload_library"));
635 dccl_unload_ptr = (void (*)(
dccl::Codec*)) dlsym(dl_handle,
"dccl3_unload");
637 (*dccl_unload_ptr)(
this);
643 if(!crypto_key_.empty())
645 skip_crypto_ids_.clear();
647 #if DCCL_HAS_CRYPTOPP
648 using namespace CryptoPP;
651 StringSource unused(passphrase,
true,
new HashFilter(hash,
new StringSink(crypto_key_)));
653 dlog.
is(DEBUG1) && dlog <<
"Cryptography enabled with given passphrase" << std::endl;
655 dlog.
is(DEBUG1) && dlog <<
"Cryptography disabled because DCCL was compiled without support of Crypto++. Install Crypto++ and recompile to enable cryptography." << std::endl;
658 skip_crypto_ids_ = do_not_encrypt_ids_;
663 std::ostream* os = (param_os) ? param_os : &dlog;
665 if(param_os || dlog.
is(INFO))
667 std::string codec_str =
"Dynamic Compact Control Language (DCCL) Codec";
668 std::string codec_guard = std::string((full_width-codec_str.size())/2,
'|');
669 *os << codec_guard <<
" " << codec_str <<
" " << codec_guard << std::endl;
671 *os << id2desc_.size() <<
" messages loaded.\n";
672 *os <<
"Field sizes are in bits unless otherwise noted." << std::endl;
674 for(std::map<int32, const google::protobuf::Descriptor*>::const_iterator it = id2desc_.begin(), n = id2desc_.end(); it != n; ++it)
675 info(it->second, os, it->first);
687 id_codec_ = id_codec_name;