protobuf中 repeated[Ptr]Field的序列化
生活随笔
收集整理的這篇文章主要介紹了
protobuf中 repeated[Ptr]Field的序列化
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
message Test1 {required int32 a = 1;
}Test1 t1; t1.set_a(150); 序列化之后的結(jié)果是
08 96 01。 其中08 >>3 == 1是a的字段序號(hào); 08的低3位(0)是類型varint(int32)96 01 = 1001 0110 0000 0001→ 000 0001 ++ 001 0110 (drop the msb and reverse the groups of 7 bits)→ 10010110→ 2 + 4 + 16 + 128 = 150message Test3 { required Test1 c = 3;}Test3 t3; t3.mutable_c()->set_a(150);
序列化之后的結(jié)果是1a 03 08 96 01
同上。
1a >>3 == 3是c的字段序號(hào); 1a的低3位(2)類型是length-delimited(embeded message)。
第二個(gè)字節(jié)的03 表示后續(xù) 08 96 01的字節(jié)個(gè)數(shù)
08 96 01就是c序列化的結(jié)果。從這里可以很明顯的看出, 在序列化結(jié)構(gòu)體時(shí), 是不包含結(jié)構(gòu)體本身的任何信息的。 那么對(duì)于repeated如何序列化?
pack? 從google看, 需要使用protobuf的cimplementation:protobuf-c, 且只對(duì)primitive有效。見http://code.google.com/p/protobuf-c/wiki/Examples此前,
message Test
{repeated int32 a = 1;
}string s; ....OutputCodedStream ss(...s...);
repeatedPtrField<Test> tt;....//假設(shè)tt.size() == 3
for(int i= 0; i < tt.size(); i++)tt.SerializeToCodedStream(&ss);////多個(gè)Test對(duì)象這樣序列化時(shí), 在stream中是沒有邊界的, 再ParseFrom的時(shí)候就會(huì)出問題了。。。之后從s 反序列化時(shí), 只能得到一個(gè)Test對(duì)象, 且tt中3個(gè)Test中的a數(shù)組的值都放到了這個(gè)唯一的Test對(duì)象中。。。。!!!!因此,我這里不得不使用較原始的方法:
template <class T> bool RepeatedPtrSerialize(const google::protobuf::RepeatedPtrField<T> &tmp, string &s)
{
//特別強(qiáng)調(diào), 在下述語句中, 不能以任何方式訪問s, 否則得到的結(jié)果就不正確。google::protobuf::io::StringOutputStream ss(&s);google::protobuf::io::CodedOutputStream codedOutput(&ss);for(int i = 0 ;i < tmp.size(); i++){ codedOutput.WriteLittleEndian32(tmp.Get(i).ByteSize());tmp.Get(i).SerializeToCodedStream(&codedOutput);} return true;
}template <class T> bool RepeatedPtrParseFrom(google::protobuf::RepeatedPtrField<T> &tmp, const string &s)
{stringstream sss(s);google::protobuf::io::IstreamInputStream ssi(&sss);google::protobuf::io::CodedInputStream codedOutputi(&ssi);while(!codedOutputi.ExpectAtEnd()){unsigned int len = 0;string s;if(!codedOutputi.ReadLittleEndian32(&len)) break;if(!codedOutputi.ReadString(&s, len)) break;T *obj = tmp.Add();obj->ParseFromString(s);從這里再構(gòu)造T。。。}return true;
}https://developers.google.com/protocol-buffers/docs/encoding序列化時(shí), 按<key, value>的形式存儲(chǔ); 多個(gè)字段的話順序存儲(chǔ); 字段間無順序關(guān)系。key: 由字段順序號(hào) + 字段類型。 field_num << 3 | wire_type。 字段類型如下:Type Meaning Used For
0 Varint int32, int64, uint32, uint64, sint32, sint64, bool, enum
1 64-bit fixed64, sfixed64, double
2 Length-delimited string, bytes, embedded messages, packed(only support repeated primitive fields) repeated fields
3 Start group groups (deprecated)
4 End group groups (deprecated)
5 32-bit fixed32, sfixed32, float關(guān)于varint, 使用可變長度編碼上述類型。小于128的無符號(hào), 用1個(gè)字節(jié)就行, 比uint32節(jié)約3個(gè)字節(jié), 比uint64節(jié)約5個(gè)字節(jié)。多個(gè)字節(jié)表示1個(gè)數(shù)時(shí),前面的字節(jié)最高位都為1; 最后一個(gè)字節(jié)的最高位為0,表示是最后一個(gè)字節(jié)了。但是對(duì)于負(fù)數(shù),不太管用,因?yàn)樽罡呶坏姆?hào)位為1。 因此對(duì)于sint32, sint64使用ZigZag壓縮。以int32為例, ZigZag將區(qū)間[-2^31, 2^31 - 1] 映射為[0, 2^32 - 1]. 映射后的值 n 如果是偶數(shù), 則原來的數(shù)就是n/2, 否則就是 -(n+1)/2。Zigzag(n) = (n << 1) ^ (n >> 31), n為sint32時(shí)Zigzag(n) = (n << 1) ^ (n >> 63), n為sint64時(shí)下表是一個(gè)比較直觀的映射表,這樣映射后再進(jìn)行編碼的好處就是絕對(duì)值比較小的負(fù)數(shù)序列化后的結(jié)果占的Bytes數(shù)也會(huì)比較少。
Signed Original Encoded As
0 0
-1 1
1 2
-2 3
2 4
-3 5
… …
2147483647 4294967294
-2147483648 4294967295
length-delimited(wire_type=2)的編碼方式:key+length+content, key的編碼方式是統(tǒng)一的,length采用varints編碼方式,content就是由length指定的長度的Bytes。3)ProtoBuf編解碼中字段順序(Field order)的問題:(a) 編碼/解碼與字段順序無關(guān),這一點(diǎn)由key-value機(jī)制就能保證(b)對(duì)于未知的字段,編碼的時(shí)候會(huì)把它寫在序列化完的已知字段后面。
總結(jié)
以上是生活随笔為你收集整理的protobuf中 repeated[Ptr]Field的序列化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: redis代码 支持的数据结构
- 下一篇: hash_map 桶扩张逻辑