/src/brpc/src/mcpack2pb/serializer.h
Line | Count | Source |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | |
18 | | // mcpack2pb - Make protobuf be front-end of mcpack/compack |
19 | | |
20 | | // Date: Mon Oct 19 17:17:36 CST 2015 |
21 | | |
22 | | #ifndef MCPACK2PB_MCPACK_SERIALIZER_H |
23 | | #define MCPACK2PB_MCPACK_SERIALIZER_H |
24 | | |
25 | | #include <limits> |
26 | | #include <vector> |
27 | | #include <google/protobuf/io/zero_copy_stream.h> |
28 | | #include "butil/logging.h" |
29 | | #include "butil/strings/string_piece.h" |
30 | | #include "mcpack2pb/field_type.h" |
31 | | |
32 | | // CAUTION: Methods in this header is not intended to be public to users of |
33 | | // brpc, and subject to change at any future time. |
34 | | |
35 | | namespace mcpack2pb { |
36 | | |
37 | | // Send bytes into ZeroCopyOutputStream |
38 | | class OutputStream { |
39 | | public: |
40 | | class Area { |
41 | | public: |
42 | 0 | Area() : _addr1(NULL) |
43 | 0 | , _addr2(NULL) |
44 | 0 | , _size1(0) |
45 | 0 | , _size2(0) |
46 | 0 | , _addional_area(NULL) {} |
47 | 2 | Area(const butil::LinkerInitialized&) {} |
48 | | Area(const Area& rhs); |
49 | | Area& operator=(const Area& rhs); |
50 | | ~Area(); |
51 | | void add(void* data, size_t n); |
52 | | void assign(const void* data) const; |
53 | | private: |
54 | | void* _addr1; |
55 | | void* _addr2; |
56 | | unsigned _size1; |
57 | | unsigned _size2; |
58 | | std::vector<butil::StringPiece>* _addional_area; |
59 | | }; |
60 | | |
61 | | // TODO(gejun): The zero-copy stream MUST return permanent memory blocks |
62 | | // to support reserve(). E.g. StringOutputStream can't be used because |
63 | | // the string inside invalidates previous memory blocks after resizing. |
64 | | OutputStream(google::protobuf::io::ZeroCopyOutputStream* stream) |
65 | 0 | : _good(true) |
66 | 0 | , _fullsize(0) |
67 | 0 | , _size(0) |
68 | 0 | , _data(NULL) |
69 | 0 | , _zc_stream(stream) |
70 | 0 | , _pushed_bytes(0) |
71 | 0 | {} |
72 | | |
73 | 0 | ~OutputStream() { done(); } |
74 | | |
75 | | // Append n bytes. |
76 | | void append(const void* data, int n); |
77 | | |
78 | | // Append a pod. |
79 | | template <typename T> void append_packed_pod(const T& packed_pod); |
80 | | |
81 | | template <typename T> T* append_packed_pod(); |
82 | | |
83 | | // Append a byte. |
84 | | void push_back(char c); |
85 | | |
86 | | // If next n bytes in the zero-copy stream is continuous, consume it |
87 | | // and return the begining address. NULL otherwise. |
88 | | void* skip_continuous(int n); |
89 | | |
90 | | // Consume n bytes from the stream and return an object representing the |
91 | | // consumed area. |
92 | | Area reserve(int n); |
93 | | |
94 | | // Change data at the area. `data' must be as long as the reserved area. |
95 | | void assign(const Area&, const void* data); |
96 | | |
97 | | // Go back for n bytes. |
98 | | void backup(int n); |
99 | | |
100 | | // Returns bytes pushed and cut since creation of this stream. |
101 | 0 | size_t pushed_bytes() const { return _pushed_bytes; } |
102 | | |
103 | | // Returns false if error occurred during serialization. |
104 | 0 | bool good() { return _good; } |
105 | | |
106 | 0 | void set_bad() { _good = false; } |
107 | | |
108 | | // Optionally called to backup buffered bytes to zero-copy stream. |
109 | | void done(); |
110 | | |
111 | | private: |
112 | | bool _good; |
113 | | int _fullsize; |
114 | | int _size; |
115 | | void* _data; |
116 | | google::protobuf::io::ZeroCopyOutputStream* _zc_stream; |
117 | | size_t _pushed_bytes; |
118 | | }; |
119 | | |
120 | | // This class is different from butil::StringPiece that it only can be |
121 | | // constructed from std::string or const char* which are both ended with |
122 | | // zero, so that in later serialization of the name, we can simply copy one |
123 | | // extra byte to end the name with zero(required by compack) rather than |
124 | | // appending the zero in a separate function call which is less efficient. |
125 | | class StringWrapper { |
126 | | public: |
127 | | StringWrapper(const std::string& str) |
128 | 0 | : _data(str.c_str()), _size(str.size()) {} |
129 | 0 | StringWrapper(const char* str) { |
130 | 0 | if (str) { |
131 | 0 | _data = str; |
132 | 0 | _size = strlen(str); |
133 | 0 | } else { |
134 | 0 | _data = ""; |
135 | 0 | _size = 0; |
136 | 0 | } |
137 | 0 | } |
138 | 0 | ~StringWrapper() { } |
139 | 0 | const char* data() const { return _data; } |
140 | 0 | size_t size() const { return _size; } |
141 | 0 | bool empty() const { return !_size; } |
142 | | private: |
143 | | const char* _data; |
144 | | size_t _size; |
145 | | }; |
146 | 0 | inline std::ostream& operator<<(std::ostream& os, const StringWrapper& sw) { |
147 | 0 | return os << butil::StringPiece(sw.data(), sw.size()); |
148 | 0 | } |
149 | | |
150 | | class Serializer { |
151 | | public: |
152 | | // Serialize into `output'. |
153 | | explicit Serializer(OutputStream* stream); |
154 | | ~Serializer(); |
155 | | |
156 | 0 | bool good() const { return _stream->good(); } |
157 | 0 | void set_bad() { _stream->set_bad(); } |
158 | 0 | size_t pushed_bytes() const { return _stream->pushed_bytes(); } |
159 | | |
160 | | // WARNING: Names to all methods cannot be longer that 254 bytes. |
161 | | |
162 | | // Append a primitive type with name. |
163 | | // Used between begin_object() and end_object(). |
164 | | void add_int8(const StringWrapper& name, int8_t value); |
165 | | void add_int16(const StringWrapper& name, int16_t value); |
166 | | void add_int32(const StringWrapper& name, int32_t value); |
167 | | void add_int64(const StringWrapper& name, int64_t value); |
168 | | void add_uint8(const StringWrapper& name, uint8_t value); |
169 | | void add_uint16(const StringWrapper& name, uint16_t value); |
170 | | void add_uint32(const StringWrapper& name, uint32_t value); |
171 | | void add_uint64(const StringWrapper& name, uint64_t value); |
172 | | void add_bool(const StringWrapper& name, bool value); |
173 | | void add_float(const StringWrapper& name, float value); |
174 | | void add_double(const StringWrapper& name, double value); |
175 | | |
176 | | // Add a primitive type without name. |
177 | | // Used between begin_xxx_array() and end_xxx_array(). |
178 | | void add_int8(int8_t value); |
179 | | void add_int16(int16_t value); |
180 | | void add_int32(int32_t value); |
181 | | void add_int64(int64_t value); |
182 | | void add_uint8(uint8_t value); |
183 | | void add_uint16(uint16_t value); |
184 | | void add_uint32(uint32_t value); |
185 | | void add_uint64(uint64_t value); |
186 | | void add_bool(bool value); |
187 | | void add_float(float value); |
188 | | void add_double(double value); |
189 | | |
190 | | // Add multiple primitive types in one call. |
191 | | void add_multiple_int8(const int8_t* values, size_t count); |
192 | | void add_multiple_int8(const uint8_t* values, size_t count); |
193 | | void add_multiple_int16(const int16_t* values, size_t count); |
194 | | void add_multiple_int16(const uint16_t* values, size_t count); |
195 | | void add_multiple_int32(const int32_t* values, size_t count); |
196 | | void add_multiple_int32(const uint32_t* values, size_t count); |
197 | | void add_multiple_int64(const int64_t* values, size_t count); |
198 | | void add_multiple_int64(const uint64_t* values, size_t count); |
199 | | void add_multiple_uint8(const uint8_t* values, size_t count); |
200 | | void add_multiple_uint8(const int8_t* values, size_t count); |
201 | | void add_multiple_uint16(const uint16_t* values, size_t count); |
202 | | void add_multiple_uint16(const int16_t* values, size_t count); |
203 | | void add_multiple_uint32(const uint32_t* values, size_t count); |
204 | | void add_multiple_uint32(const int32_t* values, size_t count); |
205 | | void add_multiple_uint64(const uint64_t* values, size_t count); |
206 | | void add_multiple_uint64(const int64_t* values, size_t count); |
207 | | void add_multiple_bool(const bool* values, size_t count); |
208 | | void add_multiple_float(const float* values, size_t count); |
209 | | void add_multiple_double(const double* values, size_t count); |
210 | | |
211 | | // Append a string. |
212 | | // The serialized value ends with 0 and the length counts 0. |
213 | | void add_string(const StringWrapper& name, const StringWrapper& str); |
214 | | void add_string(const StringWrapper& str); |
215 | | |
216 | | // Append binary data. |
217 | | void add_binary(const StringWrapper& name, const std::string& data); |
218 | | void add_binary(const StringWrapper& name, const void* data, size_t n); |
219 | | void add_binary(const std::string& data); |
220 | | void add_binary(const void* data, size_t n); |
221 | | |
222 | | // Append a null. |
223 | | void add_null(const StringWrapper& name); |
224 | | void add_null(); |
225 | | |
226 | | // Append an empty array. |
227 | | void add_empty_array(const StringWrapper& name); |
228 | | void add_empty_array(); |
229 | | |
230 | | // Begin/end an array. |
231 | | // All items inside the array must be of same type. This is not a |
232 | | // restriction of mcpack, but we want to align storage model with protobuf |
233 | | // as much as possible. |
234 | | // If too many levels of array/object reached, this function fails and |
235 | | // leaves the output unchanged. |
236 | | void begin_mcpack_array(const StringWrapper& name, FieldType item_type); |
237 | | void begin_mcpack_array(FieldType item_type); |
238 | | void begin_compack_array(const StringWrapper& name, FieldType item_type); |
239 | | void begin_compack_array(FieldType item_type); |
240 | | // End any kind of array. |
241 | | void end_array(); |
242 | | |
243 | | // Begin/end an object. |
244 | | // If too many levels of array/object reached, this function fails and |
245 | | // leaves the output unchanged. |
246 | | void begin_object(const StringWrapper& name); |
247 | | void begin_object(); |
248 | | void end_object(); |
249 | | // TODO(gejun): move to begin_object |
250 | | void end_object_iso(); |
251 | | |
252 | | public: |
253 | | struct GroupInfo { |
254 | | uint32_t item_count; |
255 | | bool isomorphic; |
256 | | uint8_t item_type; |
257 | | uint8_t type; |
258 | | uint8_t name_size; |
259 | | size_t output_offset; |
260 | | int pending_null_count; |
261 | | OutputStream::Area head_area; |
262 | | OutputStream::Area items_head_area; |
263 | | |
264 | | void print(std::ostream&) const; |
265 | | }; |
266 | | |
267 | | private: |
268 | | DISALLOW_COPY_AND_ASSIGN(Serializer); |
269 | | |
270 | | GroupInfo* push_group_info(); |
271 | | GroupInfo & peek_group_info(); |
272 | | void begin_object_internal(const StringWrapper& name); |
273 | | void begin_object_internal(); |
274 | | void end_object_internal(bool objectisoarray); |
275 | | void begin_array_internal(FieldType item_type, bool compack); |
276 | | void begin_array_internal(const StringWrapper& name, |
277 | | FieldType item_type, bool compack); |
278 | | |
279 | | OutputStream* _stream; |
280 | | int _ndepth; |
281 | | GroupInfo _group_info_fast[15]; |
282 | | GroupInfo *_group_info_more; |
283 | | }; |
284 | | |
285 | | } // namespace mcpack2pb |
286 | | |
287 | | #include "mcpack2pb/serializer-inl.h" |
288 | | |
289 | | #endif // MCPACK2PB_MCPACK_SERIALIZER_H |