- 浏览: 341417 次
- 性别:
- 来自: 福建福州
文章分类
最新评论
-
jw72jw:
最后这个是打表求值
LUA源码分析三:table分析(1) -
dyllove98:
"一些非常重要的问题,涉及面少。那这个时候,我更崇尚 ...
乱写:团队里的独裁和民主一点看法 -
jvmlover:
被踩10次了,什么思想感情啊。
LUA源码分析三:table分析(1) -
chenchenfly99:
chenchenfly99 写道
MMO游戏终极内测开服一周,问题记录 -
chenchenfly99:
...
MMO游戏终极内测开服一周,问题记录
(为啥在可视化编辑里的字都好好的,出来后就忽大忽小的,尤其在代码和文字混排的时候)
本篇主要是对“序列化.写入”所做的代码跟踪,会稍微提到点其他的。采取的例子是自带的addressbook
对我目前而言,主要关心这么几个点,对字段的管理,对协议的管理。
其中在一些代码分析的思路上是这样的:因为是对这套代码的整个需求不是太了解,所以采取的方式是,会先分析各个模块是干吗的,然后根据它们的行为开始推导。最后再将所有的模块串联起来。
要注意的是,作为一个阅读者,这套代码产生的环境、需求、历史都不太了解,所以在一些细节上的东西会稍微忽略,会有自己的疑问,但是不对里面的方法进行比较和评价,做到原原本本的展现出来。
流程图就不画了,我太懒了。反正也不是什么正规的,就是一个流水的记录。
1.字段管理.普通字段
对每个成员变量,都会有这几类的接口,一些set的接口还可能有若干的重载以
message Person { required string name = 1; }
为例,分别有以下接口
inline bool has_name() const; inline void clear_name(); inline const ::std::string& name() const; inline void set_name(const ::std::string& value); inline void set_name(const char* value); inline void set_name(const char* value, size_t size);
以及三个标志位的接口
inline bool Person::has_name() const { return (_has_bits_[0] & 0x00000001u) != 0; } inline void Person::set_has_name() { _has_bits_[0] |= 0x00000001u; } inline void Person::clear_has_name() { _has_bits_[0] &= ~0x00000001u; }
标志位类型如下
::google::protobuf::uint32 _has_bits_[(4 + 31) / 32];
在set_name(),clear_name()中,分别会调用相应的标志位接口。
因为值是和xxx=tag,中的tag绑定的, 所以在向后或向前兼容上,tag不能够重复的使用
bool SerializeToFileDescriptor(int file_descriptor) const; bool SerializePartialToFileDescriptor(int file_descriptor) const; bool SerializeToOstream(ostream* output) const; bool SerializePartialToOstream(ostream* output) const;
bool Message::SerializeToOstream(ostream* output) const { { io::OstreamOutputStream zero_copy_output(output); if (!SerializeToZeroCopyStream(&zero_copy_output)) return false; } return output->good(); } bool MessageLite::SerializeToZeroCopyStream( io::ZeroCopyOutputStream* output) const { io::CodedOutputStream encoder(output); return SerializeToCodedStream(&encoder); } bool MessageLite::SerializeToCodedStream(io::CodedOutputStream* output) const { GOOGLE_DCHECK(IsInitialized()) << InitializationErrorMessage("serialize", *this); return SerializePartialToCodedStream(output); }有些函数有Partial之分,最终都会调用到SerializePartialToCodedStream,因此整个类大体的调用层次如下:
bool MessageLite::SerializePartialToCodedStream( io::CodedOutputStream* output) const { const int size = ByteSize(); // Force size to be cached. uint8* buffer = output->GetDirectBufferForNBytesAndAdvance(size); if (buffer != NULL) { uint8* end = SerializeWithCachedSizesToArray(buffer); if (end - buffer != size) { ByteSizeConsistencyError(size, ByteSize(), end - buffer); } return true; } else { int original_byte_count = output->ByteCount(); SerializeWithCachedSizes(output); if (output->HadError()) { return false; } int final_byte_count = output->ByteCount(); if (final_byte_count - original_byte_count != size) { ByteSizeConsistencyError(size, ByteSize(), final_byte_count - original_byte_count); } return true; } }1).有两种写入的方式,SerializeWithCachedSizesToArray和SerializeWithCachedSizes
class LIBPROTOBUF_EXPORT CodedOutputStream { public: // Create an CodedOutputStream that writes to the given ZeroCopyOutputStream. explicit CodedOutputStream(ZeroCopyOutputStream* output); // Skips a number of bytes, leaving the bytes unmodified in the underlying // buffer. Returns false if an underlying write error occurs. This is // mainly useful with GetDirectBufferPointer(). bool Skip(int count); // Sets *data to point directly at the unwritten part of the // CodedOutputStream's underlying buffer, and *size to the size of that // buffer, but does not advance the stream's current position. This will // always either produce a non-empty buffer or return false. If the caller // writes any data to this buffer, it should then call Skip() to skip over // the consumed bytes. This may be useful for implementing external fast // serialization routines for types of data not covered by the // CodedOutputStream interface. bool GetDirectBufferPointer(void** data, int* size); // If there are at least "size" bytes available in the current buffer, // returns a pointer directly into the buffer and advances over these bytes. // The caller may then write directly into this buffer (e.g. using the // *ToArray static methods) rather than go through CodedOutputStream. If // there are not enough bytes available, returns NULL. The return pointer is // invalidated as soon as any other non-const method of CodedOutputStream // is called. inline uint8* GetDirectBufferForNBytesAndAdvance(int size); // Write raw bytes, copying them from the given buffer. void WriteRaw(const void* buffer, int size); // Like WriteRaw() but writing directly to the target array. // This is _not_ inlined, as the compiler often optimizes memcpy into inline // copy loops. Since this gets called by every field with string or bytes // type, inlining may lead to a significant amount of code bloat, with only a // minor performance gain. static uint8* WriteRawToArray(const void* buffer, int size, uint8* target); // Equivalent to WriteRaw(str.data(), str.size()). void WriteString(const string& str); // Like WriteString() but writing directly to the target array. static uint8* WriteStringToArray(const string& str, uint8* target); // Write a 32-bit little-endian integer. void WriteLittleEndian32(uint32 value); // Returns the total number of bytes written since this object was created. inline int ByteCount() const; // Returns true if there was an underlying I/O error since this object was // created. bool HadError() const { return had_error_; } private: GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CodedOutputStream); ZeroCopyOutputStream* output_; uint8* buffer_; int buffer_size_; int total_bytes_; // Sum of sizes of all buffers seen so far. bool had_error_; // Whether an error occurred during output. // Advance the buffer by a given number of bytes. void Advance(int amount); // Called when the buffer runs out to request more data. Implies an // Advance(buffer_size_). bool Refresh(); };
这个类干了这么几件事
1) 维护一个ZeroCopyOutputStream
2) 维护一个uint8* buffer_,各种write函数都是和它绑定,这也是他希望的意识形态
3) uint8* buffer_和ZeroCopyOutputStream通过Refresh()转换
4) Refresh()的转换调用buffer_和ZeroCopyOutputStream通过Refresh::Next函数。而且Next必然是个虚函数
在XXXOutputStream结构类如下,以OstreamOutputStream为例,源码简化如下:
class LIBPROTOBUF_EXPORT OstreamOutputStream : public ZeroCopyOutputStream { public: // Creates a stream that writes to the given C++ ostream. // If a block_size is given, it specifies the size of the buffers // that should be returned by Next(). Otherwise, a reasonable default // is used. explicit OstreamOutputStream(ostream* stream, int block_size = -1); ~OstreamOutputStream(); // implements ZeroCopyOutputStream --------------------------------- bool Next(void** data, int* size); void BackUp(int count); int64 ByteCount() const; private: class LIBPROTOBUF_EXPORT CopyingOstreamOutputStream : public CopyingOutputStream { public: CopyingOstreamOutputStream(ostream* output); ~CopyingOstreamOutputStream(); // implements CopyingOutputStream -------------------------------- bool Write(const void* buffer, int size); private: // The stream. ostream* output_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CopyingOstreamOutputStream); }; CopyingOstreamOutputStream copying_output_; CopyingOutputStreamAdaptor impl_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(OstreamOutputStream); };
1) OstreamOutputStream本身继承ZeroCopyOutputStream
2) 有个内置类Copying...,继承CopyingOutputStream
3) 及成员变量copying_output_和一个impl_
我们先看看OstreamOutputStream和copying_output_、impl_是怎么交互的。
// implements ZeroCopyOutputStream --------------------------------- bool Next(void** data, int* size); void BackUp(int count); int64 ByteCount() const; bool OstreamOutputStream::Next(void** data, int* size) { return impl_.Next(data, size); } void OstreamOutputStream::BackUp(int count) { impl_.BackUp(count); } int64 OstreamOutputStream::ByteCount() const { return impl_.ByteCount(); }
而copying_output_只是给impl_构造用
OstreamOutputStream::OstreamOutputStream(ostream* output, int block_size) : copying_output_(output), impl_(©ing_output_, block_size) { }
可以看到,OstreamOutputStream,copying_output_都继承了ZeroCopyOutputStream,但实现都是在copying_output_中,OstreamOutputStream只是起到接口约束。
继续跟调CopyingOutputStreamAdaptor。
1).维护scoped_array<uint8> buffer_; CopyingOutputStream* copying_stream_;
2).围绕buffer_做了很多事,主要是字段,位置,写入等等
3).buffer_和copying_stream_交互主要通过一个Write的虚函数,比如
if (copying_stream_->Write(buffer_.get(), buffer_used_)) {
4).buffer_是一个连续的空间,大小由外部传入
至此,几个大模块功能都差不多过了一遍,现在把他们串起来。
自定义协议继承google::protobuf::Message,当你要把协议体序列化到某个介质的时候,如下:
std::fstream output(filename.c_str(), ios::out | ios::trunc | ios::binary);
addressbook.SerializeToOstream(&output);
SerializeToXXX,XXX可以是用户的自定义格式
进行一个IO流的封装,可以叫FileOutputStream,也可以叫OstreamOutputStream,以后者为例,都继承自一个叫ZeroCopyOutputStream接口类,需要实现以下三个函数
bool Next(void** data, int* size);
void BackUp(int count);
int64 ByteCount() const;
为了重写这3个接口的方便和统一,只要求用户在数据的导出上做一个重写。于是抽象出
CopyingOutputStream类,这个类里面只有一个bool Write(const void* buffer, int size);函数,也就是把第三方的数据源导入到buffer里面。
Next,BackUp,ByteCount自然可以起到一个重用的机制,于是抽象出叫CopyingOutputStreamAdaptor。
其继承自ZeroCopyOutputStream,主要是为了Next,BackUp,ByteCount接口约束。在父类OstreamOutputStream里的Next,BackUp,ByteCount,只是对CopyingOutputStreamAdaptor封装调用
(一开始对OstreamOutputStream,CopyingOutputStream,CopyingOutputStreamAdaptor有点迷惑,理清关系后,发现层次挺清晰的)
CopyingOutputStreamAdaptor维护着scoped_array<uint8> buffer_;会调用CopyingOutputStream的接口Write导入数据
OK,那现在OstreamOutputStream已经有数据了,进行CodedOutputStream
CodedOutputStream是为两者提供服务,一个是 ZeroCopyOutputStream* output_;也就是我们前文中转换后的OstreamOutputStream;一个是静态数据,供第三方直接调用.
CodedOutputStream提供了一个uint8* buffer_;指针,其实是直接从ZeroCopyOutputStream* output_读取指针值的,这也是为什么叫ZeroCopyOutputStream。
最后调用MessageLite::SerializePartialToCodedStream函数,里面会判断调用虚函数SerializeWithCachedSizesToArray,
SerializeWithCachedSizes。(前者最后还是会调用SerializeWithCachedSizes)
在虚函数SerializeWithCachedSizesToArray里,参数是一个uint8* buffer_,把协议里的值和tag号顺序的写入入。tag|长度|值
4.序列化.读出
代码架构和写入的一样,主要关注最终的MergePartialFromCodedStream函数。
发表评论
-
【创业】找味道相同程序员
2013-10-29 12:06 30我觉得写招聘,就是在找味道一样的人。但是团队正起步中,没有 ... -
0到新浪微博综搜榜第一
2012-12-27 09:38 19720到新浪微博综搜榜第一 原谅我标题了一点,这两天 ... -
看事儿网:下一代社交内容分析和demo展示
2012-10-31 11:26 1703兄弟我最近用业余时间学了点WEB的东西,做了个自己设想中 ... -
总结第一个项目"种粉网"的失败
2012-08-07 10:45 1277第一个项目“种粉网 ... -
当年朦胧的程序员我,在游戏上对聊天玩法新的包装想法。。
2011-10-13 22:48 1500下班回家的路上,在 ... -
2010 ECUG大会,第二天记录。
2010-10-17 21:35 1125第二天 早上爬了几次才起来。一路小跑,买完包子后在 ... -
2010 ECUG大会,第一天记录。
2010-10-16 22:29 1213ECUG:http://ecug.org/2010:home ... -
缓存的力量,windows下的slub分配器
2010-07-13 14:58 2285上张图先: 该CPU L2=3MB 首先谈谈对象分 ... -
IOCP分层二,一个复杂度为1的对象池定位
2010-04-16 11:42 2009前篇 IOCP的分层一 在对象池的使用上,我采 ... -
IOCP的分层一
2010-04-13 16:09 1357写网络代码时,总希望把逻辑和网络层分开。而在IOC ... -
~也来共享共享我的程序学习方法~
2010-03-03 22:23 849一家之言,如果你觉得天天看书无效、纠结语法的时候可以 ... -
(00XX系列)搞搞有限状态机
2009-11-12 17:02 1414需要修改游戏中的一些AI,自己做了个小模型。一 ... -
万恶的继承
2009-10-28 23:36 816(1) 关于人物继承: cla ... -
一篇以前写过的文章:C++之一个编程入门者的感悟(能引起你的共鸣,是我最值得的事)
2009-03-11 13:29 874今日再看到这篇文章实 ... -
我怎么就想个留个电话,该留个邮箱啊。
2009-01-06 11:18 1049跟客户交流完问题后,一些问题还没得到解决。客户拿了张纸叫我把一 ... -
说出“对不起”的感觉真好
2008-12-30 21:38 1488早上,在聚精会神看书中。一同事突然问我:“XX,关于。。。。” ... -
观察和留意细节
2008-12-06 12:33 921周五下午回公司开会。 在会议室门外,已经坐着几个同组的同事。 ... -
就算说话很随便,关系再好的人,也要注意用词用语
2008-12-03 22:54 1607如下: 中午,组长因为少了个椅子,来我旁边搬走张椅子(我是用 ... -
今日反思:对简单程序的不重视
2008-11-24 23:04 922一个很简单的UDP/TCP/服务器连接。 不怎么重视,很随意的 ... -
内网呆了一周,一些奇怪的问题记录下。。
2008-09-05 22:17 1251内网呆了一周,一些奇怪的问题记录下。。 这个是很久以前的 协 ...
相关推荐
- **Protocol Buffer**是一种用于数据序列化的高效工具,支持多种编程语言(如Java、C++、Python等),能够实现数据的有效存储和传输。 #### 二、概念与工作原理 1. **概念介绍** - **Protocol Buffer**:一种...
Protocol Buffer是一种由Google开发的数据序列化协议,它旨在简化数据的存储和通信。Protocol Buffer与平台无关,是一种高效的二进制格式,用于结构化数据的串行化(序列化)。它通常用于替代传统文本格式,如XML或...
Protocol Buffer是Google开发的一种数据序列化协议,它允许开发者定义数据结构,并将其转换为二进制格式进行存储或网络传输。Protocol Buffer 3.0 (简称protobuf) 是该技术的第三个主要版本,提供了更强大的功能和...
Protocol Buffer是Google开发的一种数据序列化协议,常用于结构化数据的存储和交换。它提供了一种高效、灵活且跨语言的方式,使得不同系统之间能够方便地共享数据。标题中的"Protocol Buffer java版本"指的是...
Google Protocol Buffer 提供了一种高效的数据序列化方式,使得开发者能够在不同的语言和平台上轻松地交换数据。通过定义清晰的数据结构并在不同端之间共享这些定义,可以极大地简化数据交换的过程。此外,它的灵活...
Google Protocol Buffer(简称Protobuf)是一种高效、轻便的结构化数据序列化工具,广泛应用于数据存储、RPC(远程过程调用)数据交换格式,尤其在通信协议和数据存储领域。它具有语言无关、平台无关以及可扩展性,...
Protocol_Buffer的工作流程始于定义一个`.proto`文件,在该文件中描述了需要序列化的数据结构。每个Protocol_Buffer消息本质上是由一系列键值对组成的逻辑记录。例如,下面是一个简单的`.proto`文件,用于定义个人...
Protocol Buffer是Google开发的一种数据序列化协议,类似于XML或JSON,但更小巧、快速、灵活。它定义了一种结构化数据的格式,可以用于网络通信、存储或任何需要数据交换的场合。Protocol Buffer支持多种语言,包括...
Protocol Buffer是Google开发的一种高效、灵活且跨平台的数据序列化协议,用于结构化数据的序列化,类似于XML、JSON,但更小、更快、更简单。它能够将结构化的数据序列化,可用于数据存储、通信协议等方面。Protocol...
Google Protocol Buffer(Protobuf)是一种高效、语言无关、平台无关的结构化数据序列化机制,由Google开发并广泛应用于数据存储和RPC(远程过程调用)系统。它的主要作用是将复杂的数据结构转化为二进制流,便于在...
标题中的“一个栗子-序列化工具protobuf”指的是使用Google的Protocol Buffers(简称protobuf)进行数据序列化的实例。protobuf是一种高效、灵活且跨平台的数据序列化库,它允许开发者定义数据结构,然后生成能够在...
这个缓冲区可能被设计用于处理二进制数据,比如 Protocol Buffers(protobuf)——一个高效的数据序列化协议,常用于网络通信和数据存储。 缓冲区管理主要包括以下几个方面: 1. **分配**:根据描述,pb_buffer ...
而Protobuf(Protocol Buffers)是Google开发的一种数据序列化协议,常用于数据存储和跨平台通信,因其高效、紧凑的二进制格式而受到欢迎。 在ROS2中使用protobuf的主要目的是为了优化数据传输效率和减少内存占用。...
Protocol Buffers(简称Protobuf)是谷歌推出的一种高效的数据序列化协议,它提供了一种语言无关、平台无关、可扩展的序列化方法,与XML类似,但在效率和简洁性上更胜一筹。通过使用Protobuf,开发者可以定义数据...
在上述配置中,我们使用了String序列化器来序列化和反序列化消息的键和值。如果需要自定义序列化器,可以创建自己的序列化器类,并在配置中指定。 **自定义序列化器示例:** ```java public class ...
除了Hadoop内置的Writable机制,还有其他序列化框架,如Avro、Thrift和Protocol Buffer。Avro提供了一种二进制序列化方式,便于高效处理大量数据,并且对动态语言友好。Thrift是一个跨语言服务开发框架,通过Thrift ...
Protocol Buffers是Google推出的一种数据序列化协议,它允许开发者定义数据结构,然后生成相应的代码,以便在各种数据类型和平台之间高效地存储和交换这些数据。 ### PBC库的核心特性 1. **无代码生成**:与标准的...
- **`pickle.dump(obj, file[, protocol])`**:该函数用于将对象序列化,并将序列化后的数据写入到指定的文件对象`file`中。`protocol`参数用来指定序列化的协议版本,支持0、1、2三个版本,默认为0(ASCII协议)。...
密钥 Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存...