小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

Protocol Buffer - Richie - 博客園

 ShangShujie 2010-08-22

Protocol Buffer

Protocol buffers是google使用的一種結(jié)構(gòu)化數(shù)據(jù)序列化編碼解碼方式,采用簡單的二進(jìn)制格式,他比XML、JSON格式體積更小,編碼解碼效率更高
下面是項(xiàng)目官方網(wǎng)站與XML對比的描述:
# are 3 to 10 times smaller
# are 20 to 100 times faster
這里有一個(gè).NET環(huán)境下的對比測試:Results of Northwind database rows serialization benchmarks,用的是.NET下面的實(shí)現(xiàn)ProtoBuf.net
protobuf項(xiàng)目(C++),.NET 下的實(shí)現(xiàn)有:protobuf-net、protobuf-csharp-port。 另外一個(gè).NET的項(xiàng)目是Proto#,不過作者似乎沒有維護(hù)了

使用方式簡介
首先定義消息類型:
message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }

  repeated PhoneNumber phone = 4;
}
Field Rules: 屬性規(guī)則,required: 必須的屬性;optional: 可選屬性;repeated: 可重復(fù)多個(gè)的屬性
Field Type: 屬性數(shù)據(jù)類型,標(biāo)量值類型(scalar value types)支持double, float, int32, int64, uint32, uint64, sint32, sint64, fixed32, fixed64, bool, string, bytes等,另外支持枚舉、嵌套/引用的消息類型等
Field Tags: 屬性標(biāo)簽(例如name=1中的1),使用正整數(shù)表示,在序列化的二進(jìn)制中使用這個(gè)標(biāo)簽來標(biāo)記屬性,比使用屬性名稱體積更小
詳細(xì)的語法參考官方網(wǎng)站:Language Guide

消息類型定義在.proto文件中,使用protoc.exe根據(jù).proto文件生成C++、Java、Python等類文件,這些類文件中定義了表示 消息的對象,以及用于編碼、解碼的方法

體積方面,首先從上面消息類型的定義中可以看出,使用屬性標(biāo)簽代替屬性名稱可以減小體積,另外在編碼協(xié)議上對各種數(shù)據(jù)類型的處理,也盡量采用了壓縮的表示 方式以減小體積。速度方面,二進(jìn)制協(xié)議比基于文本的解析更有優(yōu)勢

編碼協(xié)議簡介 - 2.3.0
詳細(xì)的編碼協(xié)議參考官方網(wǎng)站的Encoding
Base 128 Varints

32位整數(shù)使用4字節(jié)存儲,32位的整數(shù)值1同樣要使用4個(gè)字節(jié),比較浪費(fèi)空間。Varint采用變長字節(jié)的方式存儲整數(shù),將高位為0的字節(jié)去掉,節(jié)約空 間
高位為0的字節(jié)去掉以后,用來存儲整數(shù)的每一個(gè)字節(jié),其最高有效位(most significant bit)用作標(biāo)識位,0表示這是整數(shù)的最后一個(gè)字節(jié),1表示不是最后一個(gè)字節(jié);其他7位用于存儲整數(shù)的數(shù)值。字節(jié)序采用little-endian
示例:
整數(shù)1,Varint的二進(jìn)制值為0000 0001。因?yàn)?個(gè)字節(jié)就足夠,所以最高有效位為0,后7位則為1的原碼形式
整數(shù)300,Varint需要2字節(jié)表示,二進(jìn)制值為1010 1100 0000 0010。第一個(gè)字節(jié)最高有效位設(shè)為1,最后一個(gè)字節(jié)最高有效位設(shè)為0。解碼過程如下:
a). 首先每個(gè)字節(jié)去掉最高有效位,得到:010 1100 000 0010
b). 按照little-endian方式處理字節(jié)序,得到:000 0010 010 1100
c). 二進(jìn)制值100101100即為300

ZigZag編碼
Varint對于無符號整數(shù)有效,對負(fù)數(shù)無法進(jìn)行壓縮,protocol buffer對有符號整數(shù)采用ZigZag編碼后,再以varint形式存儲
對32位有符號數(shù),ZigZag編碼算法為 (n << 1) ^ (n >> 31),對64位有符號數(shù)的算法為(n << 1) ^ (n >> 63)
注意:32位有符號數(shù)右移31位后,對于正數(shù)所有位為0,對于負(fù)數(shù)所有位為1
編碼后的效果是0=>0, -1=>1, 1=>2, -2=>3, 2=>4……,即將無符號數(shù)編碼為有符號數(shù)表示,這樣就能有效發(fā)揮varint的優(yōu)勢了

Protocol buffer用32位表示float和fixed32,用64位表示double和fixed64
String, bytes, 嵌入式消息等數(shù)據(jù)均采用定長數(shù)據(jù)類型(length-delimited)表示,這類數(shù)據(jù)在開始位置使用一個(gè)varint表示數(shù)據(jù)的字節(jié)長度,后面接著是 數(shù)據(jù)值

消息結(jié)構(gòu)
消息的所有屬性都序列化為key-value pair(鍵-值對)的字節(jié)流形式,字節(jié)流中不包含屬性的名稱和聲明的類型,這些信息必須從定義的消息類型中獲取
key里面包含2個(gè)東西,一個(gè)是在消息類型里面為該屬性指定的field tag,另一個(gè)是protocol buffer協(xié)議的封裝類型(wire type)。這2個(gè)部分都是正整數(shù),使用 (field_tag << 3) | wire_type 方式生成一個(gè)正整數(shù),然后使用base 128 varint方式表示。key后面跟著是屬性的值
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 repeated fields

3

Start group

groups (deprecated)

4

End group

groups (deprecated)

5

32-bit

fixed32, sfixed32, float


示例:
消息類型如下
message Test1 {
  required int32 attr = 1;
}
創(chuàng)建一個(gè)Test1的對象,將其屬性attr的值設(shè)置為150,則對該對象編碼過程如下
屬性數(shù)據(jù)類型為int32,其wire type為0,所以key值為
(1 << 3 ) | 0 => 0000 1000
屬性值150采用Varint編碼
   150
   => 10010110 //二進(jìn)制
   => 000 0001 001 0110 //7位一組分開
   => 001 0110 000 0001 //little-endian字節(jié)序
   => 1001 0110 0000 0001 //設(shè)置最高標(biāo)識位
   => 96 01 //16進(jìn)制
所以這個(gè)Test1對象編碼后的16進(jìn)制值為:08 96 01

如果有嵌入式消息類型定義如下
message Test3 {
  required Test1 c = 3;
}
編碼后的16進(jìn)制值形如:1A 03 08 96 01,其中08 96 01就是上面示例的Test1對象,在Test3的屬性中他與字符串的處理方式一樣,前面的03就是表示其長度的varint

protobuf- csharp-port的使用方式
protobuf-csharp-port跟protobuf的使用方式一樣,即在開發(fā)過程中使用protoc.exe、ProtoGen.exe生成用 于序列化、反序列化時(shí)的消息對象,在運(yùn)行時(shí)通過這些對象進(jìn)行編碼解碼
GitHub下 載項(xiàng)目源代碼(目前還沒有發(fā)布包),項(xiàng)目中帶有示例AddressBook
生成消息通訊用的C#類分2個(gè)步驟
步驟1:使用lib目錄下的protoc.exe生成二進(jìn)制表示
protoc --descriptor_set_out=addressbook.protobin --proto_path=..\protos --include_imports ..\protos\tutorial\addressbook.proto
步驟2:使用編譯生成的ProtoGen.exe從二進(jìn)制表示生成C#類
ProtoGen.exe addressbook.protobin
會生成幾個(gè).cs文件,其中包括AddressBookProtos.cs,這個(gè)就是在addressbook.proto中定義的消息類型
運(yùn)行時(shí)的項(xiàng)目需要引用編譯生成的Google.ProtocolBuffers.dll,使用AddressBookProtos.cs完成編碼解碼操 作,詳細(xì)用法查看示例項(xiàng)目AddressBook
運(yùn)行AddressBook.exe如下圖:
  
輸入的對象序列化為二進(jìn)制后,默認(rèn)保存在addressbook.data文件中,可以使用ProtoDump.exe讀取這個(gè)二進(jìn)制文件:
  

protobuf-net的使用方式 - r282
protobuf-net的使用與Google的protobuf完全不一樣,他采用.NET的編程方式,可以非常方便的在.NET的序列化場景下使用, 支持WCF的DataContact,WCF程序幾乎不需要什么修改就能使用protobuf-net
下載protobuf-net,項(xiàng)目引用protobuf-net.dll,測試對象定義如下:
01 [ProtoContract]
02 public class TestObject
03 {
04     [ProtoMember(1)]
05     public string StringAttr1 { get; set; }
06     [ProtoMember(2)]
07     public string StringAttr2 { get; set; }
08     [ProtoMember(3)]
09     public int IntAttr { get; set; }
10     [ProtoMember(4)]
11     public long LongAttr { get; set; }
12     [ProtoMember(5)]
13     public decimal DecimalAttr { get; set; }
14     [ProtoMember(6)]
15     public float FloatAttr { get; set; }
16     [ProtoMember(7)]
17     public int[] ArrayAttr { get; set; }
18     [ProtoMember(8)]
19     public IList<string> ListAttr { get; set; }
20     [ProtoMember(9)]
21     public InnerObject EmbeddedAttr { get; set; }
22     public override string ToString()
23     {
24         StringBuilder sb = new StringBuilder()
25             .Append("TestObject {\r\n")
26             .Append("   StringAttr1: \"").Append(this.StringAttr1).Append("\",\r\n")
27             .Append("   StringAttr2: \"").Append(this.StringAttr2).Append("\",\r\n")
28             .Append("   IntAttr: ").Append(this.IntAttr).Append(",\r\n")
29             .Append("   LongAttr: ").Append(this.LongAttr).Append(",\r\n")
30             .Append("   DecimalAttr: ").Append(this.DecimalAttr).Append(",\r\n")
31             .Append("   FloatAttr: ").Append(this.FloatAttr).Append(",\r\n");
32         if (this.ArrayAttr != null)
33         {
34             sb.Append("   ArrayAttr: [ ");
35             foreach (int i in this.ArrayAttr) sb.Append(i).Append(", ");
36             sb.Remove(sb.Length - 2, 2);
37             sb.Append(" ],\r\n");
38         }
39         if (this.ListAttr != null)
40         {
41             sb.Append("   ListAttr: [ ");
42             foreach (string s in this.ListAttr) sb.Append('"').Append(s).Append("\", ");
43             sb.Remove(sb.Length - 2, 2);
44             sb.Append(" ],\r\n");
45         }
46         if (this.EmbeddedAttr != null)
47             sb.Append("   EmbeddedAttr: ").Append(this.EmbeddedAttr.ToString()).Append("\r\n");
48         return sb.Append("}").ToString();
49     }
50 }
51 [ProtoContract]
52 public class InnerObject
53 {
54     [ProtoMember(1)]
55     public string Attr1 { get; set; }
56     [ProtoMember(2)]
57     public DateTime Attr2 { get; set; }
58     [ProtoMember(3)]
59     public bool Attr3 { get; set; }
60     [ProtoMember(6)]
61     public byte Attr4 { get; set; }
62     [ProtoMember(9)]
63     public sbyte Attr5 { get; set; }
64     public override string ToString()
65     {
66         return new StringBuilder()
67             .Append("{\r\n")
68             .Append("      Attr1: \"").Append(this.Attr1).Append("\",\r\n")
69             .Append("      Attr2: \"").Append(this.Attr2.ToString("yyyy-MM-dd")).Append("\",\r\n")
70             .Append("      Attr3: ").Append(this.Attr3).Append(",\r\n")
71             .Append("      Attr4: ").Append(this.Attr4).Append(",\r\n")
72             .Append("      Attr5: ").Append(this.Attr5).Append("\r\n")
73             .Append("   }").ToString();
74     }
75 }
測試代碼如下:
01 using (MemoryStream ms = new MemoryStream())
02 {
03     TestObject obj = new TestObject()
04     {
05         StringAttr1 = "string 1",
06         StringAttr2 = "string 2",
07         IntAttr = 300,
08         LongAttr = 1,
09         DecimalAttr = 34.10091M,
10         FloatAttr = 12.3f,
11         ArrayAttr = new int[] { 600, -9, 0 },
12         ListAttr = new List<string> { "string 3", "string 5" },
13         EmbeddedAttr = new InnerObject()
14         {
15             Attr1 = "string 6",
16             Attr2 = new DateTime(2010, 2, 1),
17             Attr3 = false,
18             Attr4 = 8,
19             Attr5 = -63
20         }
21     };
22     Serializer.Serialize<TestObject>(ms, obj);
23     ms.Flush();
24     ms.Position = 0;
25     TestObject obj2 = Serializer.Deserialize<TestObject>(ms);
26     Console.WriteLine(obj2);
27     Console.ReadKey();
28 }
運(yùn)行結(jié)果:
  

附錄
原碼、反碼、補(bǔ)碼
對有符號數(shù),最高位是符號位。正數(shù)的原碼反碼和補(bǔ)碼都是一樣的,就是本身。負(fù)數(shù)的反碼是原碼求反,補(bǔ)碼是反碼加1。例如-1的原碼是1000 0001,反碼是1111 1110,補(bǔ)碼是1111 1111。負(fù)數(shù)都是用補(bǔ)碼表示,從正數(shù)的原碼推負(fù)數(shù)的二進(jìn)制表示(補(bǔ)碼)時(shí),只須將正數(shù)各個(gè)位(包括符合位)取反加1
補(bǔ)碼有2種,即one's complement (1's complement,1的補(bǔ)碼) 和 two's complement (2's complement,2的補(bǔ)碼) 。按照定義,one's complement就是對各個(gè)位取反,two's complement是對各個(gè)位取反后加1。例如在8位處理器情況下,9的二進(jìn)制是0000 1001,one's complement是1111 0110,two's complement是1111 0111
采用one's complement表示負(fù)數(shù)時(shí)存在正0 (0x00)和負(fù)0 (0xff),并且有符號數(shù)相加必須采用end-around carry(循環(huán)進(jìn)位)處理,例如
   
相加之后發(fā)生溢出,則必須將溢出位加到最低位上,這樣導(dǎo)致有符號數(shù)相加和無符號數(shù)相加算法不一致,而采用two's complement表示時(shí)不存在這些問題
關(guān)于2的補(bǔ)碼表示可以參考阮一峰的關(guān)于2的補(bǔ) 碼一文,更專業(yè)的說明可以參考wikipedia上的Method of complements:二進(jìn)制的基數(shù)補(bǔ)碼(radix complement)叫做2的補(bǔ)碼,二進(jìn)制的基數(shù)減一補(bǔ)碼(diminished radix complement)叫做1的補(bǔ)碼;十進(jìn)制的基數(shù)補(bǔ)碼叫做10的補(bǔ)碼,基數(shù)減一補(bǔ)碼叫做9的補(bǔ)碼

Big-endian, little-endian可以參考wikipedia上的Endianness, 講解的很詳細(xì)
Tag標(biāo)簽: Serialization

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多