Protobuf ZeroCopyStream
1.ZeroCopyStream
protobuf在io接口上有一个叫做ZeroCopyStream,对于IO的接口设计,pb提供了相关序列化与反序列化接口,如:
代码语言:javascript复制// Read a protocol buffer from the given zero-copy input stream. If
// successful, the entire input will be consumed.
bool ParseFromZeroCopyStream(io::ZeroCopyInputStream* input);
// Write the message to the given zero-copy output stream. All required
// fields must be set.
bool SerializeToZeroCopyStream(io::ZeroCopyOutputStream* output) const;
ZeroCopyStream设计的初衷是最小化buffer的拷贝次数,即省略掉stream内部数据拷贝到用户buffer。因此,stream可以返回一个缓冲区,该缓冲区实际上直接指向要存储字节的最终数据结构,并且调用者可以直接与该缓冲区交互,从而消除了中间复制操作。
例如:经典的IO stream:
代码语言:javascript复制char buffer[[]BUFFER_SIZE];
input->Read(buffer, BUFFER_SIZE);
DoSomething(buffer, BUFFER_SIZE);
然后,stream基本上只是调用memcpy()将数据从pb复制到用户buffer中。如果使用ZeroCopyInputStream,我们只需要:
代码语言:javascript复制const void* buffer;
int size;
input->Next(&buffer, &size);
DoSomething(buffer, size);
这里不执行拷贝,调用者最终直接从buffer中读取。
ZeroCopyStream提供了两个基类:ZeroCopyOutputStream/ZeroCopyInputStream。
以InputStream为例,通常我们可以通过继承的方式自定义自己的ZerCopyStream,需要实现下面四个接口。
代码语言:javascript复制// implements ZeroCopyInputStream ----------------------------------
bool Next(const void** data, int* size);
void BackUp(int count);
bool Skip(int count);
int64 ByteCount() const;
一些rpc框架基本都自定义自己的Stream,例如:sofa-pbrpc
https://github.com/baidu/sofa-pbrpc/blob/master/src/sofa/pbrpc/buffer.h
2.Demo
定义一个pb协议,例如:授权验证:
代码语言:javascript复制syntax = "proto3";
message BasicAuth {
string username = 1;
string password = 2;
}
随后编写序列化与反序列化:
- 序列化
BasicAuth auth_message;
auth_message.set_username("user123");
auth_message.set_password("password");
StringOutputStream output_stream(&buf);
auth_message.SerializeToZeroCopyStream(&output_stream);
- 反序列化
ArrayInputStream input_stream(buf.data(), buf.size());
BasicAuth auth_message;
if (!auth_message.ParseFromZeroCopyStream(&input_stream)) {
std::cerr << "Failed to parse data." << std::endl;
}
std::cout << "Username: " << auth_message.username() << std::endl;
std::cout << "Password: " << auth_message.password() << std::endl;
随后我们便可以调用它:
代码语言:javascript复制int main() {
std::string buf;
buf.reserve(512);
ser(buf);
deser(buf);
return 0;
}
输出:
代码语言:javascript复制Username: user123
Password: password