/Users/deen/code/yugabyte-db/src/yb/rocksdb/third-party/fbson/FbsonDocument.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2011-present, Facebook, Inc. |
3 | | * All rights reserved. |
4 | | * |
5 | | * This source code is licensed under the BSD-style license found in the |
6 | | * LICENSE file in the root directory of this source tree. An additional grant |
7 | | * of patent rights can be found in the PATENTS file in the same directory. |
8 | | * |
9 | | * The following only applies to changes made to this file as part of YugaByte development. |
10 | | * |
11 | | * Portions Copyright (c) YugaByte, Inc. |
12 | | * |
13 | | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
14 | | * in compliance with the License. You may obtain a copy of the License at |
15 | | * |
16 | | * http://www.apache.org/licenses/LICENSE-2.0 |
17 | | * |
18 | | * Unless required by applicable law or agreed to in writing, software distributed under the License |
19 | | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
20 | | * or implied. See the License for the specific language governing permissions and limitations |
21 | | * under the License. |
22 | | * |
23 | | * |
24 | | */ |
25 | | |
26 | | /* |
27 | | * This header defines FbsonDocument, FbsonKeyValue, and various value classes |
28 | | * which are derived from FbsonValue, and a forward iterator for container |
29 | | * values - essentially everything that is related to FBSON binary data |
30 | | * structures. |
31 | | * |
32 | | * Implementation notes: |
33 | | * |
34 | | * None of the classes in this header file can be instantiated directly (i.e. |
35 | | * you cannot create a FbsonKeyValue or FbsonValue object - all constructors |
36 | | * are declared non-public). We use the classes as wrappers on the packed FBSON |
37 | | * bytes (serialized), and cast the classes (types) to the underlying packed |
38 | | * byte array. |
39 | | * |
40 | | * For the same reason, we cannot define any FBSON value class to be virtual, |
41 | | * since we never call constructors, and will not instantiate vtbl and vptrs. |
42 | | * |
43 | | * Therefore, the classes are defined as packed structures (i.e. no data |
44 | | * alignment and padding), and the private member variables of the classes are |
45 | | * defined precisely in the same order as the FBSON spec. This ensures we |
46 | | * access the packed FBSON bytes correctly. |
47 | | * |
48 | | * The packed structures are highly optimized for in-place operations with low |
49 | | * overhead. The reads (and in-place writes) are performed directly on packed |
50 | | * bytes. There is no memory allocation at all at runtime. |
51 | | * |
52 | | * For updates/writes of values that will expand the original FBSON size, the |
53 | | * write will fail, and the caller needs to handle buffer increase. |
54 | | * |
55 | | * ** Iterator ** |
56 | | * Both ObjectVal class and ArrayVal class have iterator type that you can use |
57 | | * to declare an iterator on a container object to go through the key-value |
58 | | * pairs or value list. The iterator has both non-const and const types. |
59 | | * |
60 | | * Note: iterators are forward direction only. |
61 | | * |
62 | | * ** Query ** |
63 | | * Querying into containers is through the member functions find (for key/value |
64 | | * pairs) and get (for array elements), and is in streaming style. We don't |
65 | | * need to read/scan the whole FBSON packed bytes in order to return results. |
66 | | * Once the key/index is found, we will stop search. You can use text to query |
67 | | * both objects and array (for array, text will be converted to integer index), |
68 | | * and use index to retrieve from array. Array index is 0-based. |
69 | | * |
70 | | * ** External dictionary ** |
71 | | * During query processing, you can also pass a call-back function, so the |
72 | | * search will first try to check if the key string exists in the dictionary. |
73 | | * If so, search will be based on the id instead of the key string. |
74 | | * |
75 | | * @author Tian Xia <tianx@fb.com> |
76 | | */ |
77 | | |
78 | | #ifndef FBSON_FBSONDOCUMENT_H |
79 | | #define FBSON_FBSONDOCUMENT_H |
80 | | |
81 | | #include <stdlib.h> |
82 | | #include <string.h> |
83 | | #include <assert.h> |
84 | | |
85 | | namespace fbson { |
86 | | |
87 | | #pragma pack(push, 1) |
88 | | |
89 | 352 | #define FBSON_VER 1 |
90 | | |
91 | | // forward declaration |
92 | | class FbsonValue; |
93 | | class ObjectVal; |
94 | | |
95 | | /* |
96 | | * FbsonDocument is the main object that accesses and queries FBSON packed |
97 | | * bytes. NOTE: FbsonDocument only allows object container as the top level |
98 | | * FBSON value. However, you can use the static method "createValue" to get any |
99 | | * FbsonValue object from the packed bytes. |
100 | | * |
101 | | * FbsonDocument object also dereferences to an object container value |
102 | | * (ObjectVal) once FBSON is loaded. |
103 | | * |
104 | | * ** Load ** |
105 | | * FbsonDocument is usable after loading packed bytes (memory location) into |
106 | | * the object. We only need the header and first few bytes of the payload after |
107 | | * header to verify the FBSON. |
108 | | * |
109 | | * Note: creating an FbsonDocument (through createDocument) does not allocate |
110 | | * any memory. The document object is an efficient wrapper on the packed bytes |
111 | | * which is accessed directly. |
112 | | * |
113 | | * ** Query ** |
114 | | * Query is through dereferencing into ObjectVal. |
115 | | */ |
116 | | class FbsonDocument { |
117 | | public: |
118 | | // create an FbsonDocument object from FBSON packed bytes |
119 | | static FbsonDocument* createDocument(const char* pb, uint32_t size); |
120 | | |
121 | | // create an FbsonValue from FBSON packed bytes |
122 | | static FbsonValue* createValue(const char* pb, uint32_t size); |
123 | | |
124 | 0 | uint8_t version() { return header_.ver_; } |
125 | | |
126 | 81 | FbsonValue* getValue() { return ((FbsonValue*)payload_); } |
127 | | |
128 | 0 | ObjectVal* operator->() { return ((ObjectVal*)payload_); } |
129 | | |
130 | 0 | const ObjectVal* operator->() const { return ((const ObjectVal*)payload_); } |
131 | | |
132 | | private: |
133 | | /* |
134 | | * FbsonHeader class defines FBSON header (internal to FbsonDocument). |
135 | | * |
136 | | * Currently it only contains version information (1-byte). We may expand the |
137 | | * header to include checksum of the FBSON binary for more security. |
138 | | */ |
139 | | struct FbsonHeader { |
140 | | uint8_t ver_; |
141 | | } header_; |
142 | | |
143 | | char payload_[1]; |
144 | | |
145 | | FbsonDocument(); |
146 | | |
147 | | FbsonDocument(const FbsonDocument&) = delete; |
148 | | FbsonDocument& operator=(const FbsonDocument&) = delete; |
149 | | }; |
150 | | |
151 | | /* |
152 | | * FbsonFwdIteratorT implements FBSON's iterator template. |
153 | | * |
154 | | * Note: it is an FORWARD iterator only due to the design of FBSON format. |
155 | | */ |
156 | | template <class Iter_Type, class Cont_Type> |
157 | | class FbsonFwdIteratorT { |
158 | | typedef Iter_Type iterator; |
159 | | typedef typename std::iterator_traits<Iter_Type>::pointer pointer; |
160 | | typedef typename std::iterator_traits<Iter_Type>::reference reference; |
161 | | |
162 | | public: |
163 | 263 | explicit FbsonFwdIteratorT(const iterator& i) : current_(i) {} fbson::FbsonFwdIteratorT<fbson::FbsonKeyValue*, fbson::ObjectVal>::FbsonFwdIteratorT(fbson::FbsonKeyValue* const&) Line | Count | Source | 163 | 14 | explicit FbsonFwdIteratorT(const iterator& i) : current_(i) {} |
Unexecuted instantiation: fbson::FbsonFwdIteratorT<fbson::FbsonValue const*, fbson::ArrayVal>::FbsonFwdIteratorT(fbson::FbsonValue const* const&) fbson::FbsonFwdIteratorT<fbson::FbsonKeyValue const*, fbson::ObjectVal>::FbsonFwdIteratorT(fbson::FbsonKeyValue const* const&) Line | Count | Source | 163 | 249 | explicit FbsonFwdIteratorT(const iterator& i) : current_(i) {} |
|
164 | | |
165 | | // allow non-const to const iterator conversion (same container type) |
166 | | template <class Iter_Ty> |
167 | | FbsonFwdIteratorT(const FbsonFwdIteratorT<Iter_Ty, Cont_Type>& rhs) |
168 | | : current_(rhs.base()) {} |
169 | | |
170 | 361 | bool operator==(const FbsonFwdIteratorT& rhs) const { |
171 | 361 | return (current_ == rhs.current_); |
172 | 361 | } fbson::FbsonFwdIteratorT<fbson::FbsonKeyValue const*, fbson::ObjectVal>::operator==(fbson::FbsonFwdIteratorT<fbson::FbsonKeyValue const*, fbson::ObjectVal> const&) const Line | Count | Source | 170 | 340 | bool operator==(const FbsonFwdIteratorT& rhs) const { | 171 | 340 | return (current_ == rhs.current_); | 172 | 340 | } |
fbson::FbsonFwdIteratorT<fbson::FbsonKeyValue*, fbson::ObjectVal>::operator==(fbson::FbsonFwdIteratorT<fbson::FbsonKeyValue*, fbson::ObjectVal> const&) const Line | Count | Source | 170 | 21 | bool operator==(const FbsonFwdIteratorT& rhs) const { | 171 | 21 | return (current_ == rhs.current_); | 172 | 21 | } |
Unexecuted instantiation: fbson::FbsonFwdIteratorT<fbson::FbsonValue const*, fbson::ArrayVal>::operator==(fbson::FbsonFwdIteratorT<fbson::FbsonValue const*, fbson::ArrayVal> const&) const |
173 | | |
174 | 361 | bool operator!=(const FbsonFwdIteratorT& rhs) const { |
175 | 361 | return !operator==(rhs); |
176 | 361 | } fbson::FbsonFwdIteratorT<fbson::FbsonKeyValue const*, fbson::ObjectVal>::operator!=(fbson::FbsonFwdIteratorT<fbson::FbsonKeyValue const*, fbson::ObjectVal> const&) const Line | Count | Source | 174 | 340 | bool operator!=(const FbsonFwdIteratorT& rhs) const { | 175 | 340 | return !operator==(rhs); | 176 | 340 | } |
fbson::FbsonFwdIteratorT<fbson::FbsonKeyValue*, fbson::ObjectVal>::operator!=(fbson::FbsonFwdIteratorT<fbson::FbsonKeyValue*, fbson::ObjectVal> const&) const Line | Count | Source | 174 | 21 | bool operator!=(const FbsonFwdIteratorT& rhs) const { | 175 | 21 | return !operator==(rhs); | 176 | 21 | } |
Unexecuted instantiation: fbson::FbsonFwdIteratorT<fbson::FbsonValue const*, fbson::ArrayVal>::operator!=(fbson::FbsonFwdIteratorT<fbson::FbsonValue const*, fbson::ArrayVal> const&) const |
177 | | |
178 | 0 | bool operator<(const FbsonFwdIteratorT& rhs) const { |
179 | 0 | return (current_ < rhs.current_); |
180 | 0 | } |
181 | | |
182 | | bool operator>(const FbsonFwdIteratorT& rhs) const { return !operator<(rhs); } |
183 | | |
184 | 239 | FbsonFwdIteratorT& operator++() { |
185 | 239 | current_ = (iterator)(((char*)current_) + current_->numPackedBytes()); |
186 | 239 | return *this; |
187 | 239 | } fbson::FbsonFwdIteratorT<fbson::FbsonKeyValue const*, fbson::ObjectVal>::operator++() Line | Count | Source | 184 | 225 | FbsonFwdIteratorT& operator++() { | 185 | 225 | current_ = (iterator)(((char*)current_) + current_->numPackedBytes()); | 186 | 225 | return *this; | 187 | 225 | } |
fbson::FbsonFwdIteratorT<fbson::FbsonKeyValue*, fbson::ObjectVal>::operator++() Line | Count | Source | 184 | 14 | FbsonFwdIteratorT& operator++() { | 185 | 14 | current_ = (iterator)(((char*)current_) + current_->numPackedBytes()); | 186 | 14 | return *this; | 187 | 14 | } |
Unexecuted instantiation: fbson::FbsonFwdIteratorT<fbson::FbsonValue const*, fbson::ArrayVal>::operator++() |
188 | | |
189 | | FbsonFwdIteratorT operator++(int) { |
190 | | auto tmp = *this; |
191 | | current_ = (iterator)(((char*)current_) + current_->numPackedBytes()); |
192 | | return tmp; |
193 | | } |
194 | | |
195 | 0 | explicit operator pointer() { return current_; } |
196 | | |
197 | 105 | reference operator*() const { return *current_; } fbson::FbsonFwdIteratorT<fbson::FbsonKeyValue const*, fbson::ObjectVal>::operator*() const Line | Count | Source | 197 | 88 | reference operator*() const { return *current_; } |
fbson::FbsonFwdIteratorT<fbson::FbsonKeyValue*, fbson::ObjectVal>::operator*() const Line | Count | Source | 197 | 17 | reference operator*() const { return *current_; } |
|
198 | | |
199 | 468 | pointer operator->() const { return current_; } |
200 | | |
201 | | iterator base() const { return current_; } |
202 | | |
203 | | private: |
204 | | iterator current_; |
205 | | }; |
206 | | |
207 | | typedef int (*hDictInsert)(const char* key, unsigned len); |
208 | | typedef int (*hDictFind)(const char* key, unsigned len); |
209 | | |
210 | | /* |
211 | | * FbsonType defines 10 primitive types and 2 container types, as described |
212 | | * below. |
213 | | * |
214 | | * primitive_value ::= |
215 | | * 0x00 //null value (0 byte) |
216 | | * | 0x01 //boolean true (0 byte) |
217 | | * | 0x02 //boolean false (0 byte) |
218 | | * | 0x03 int8 //char/int8 (1 byte) |
219 | | * | 0x04 int16 //int16 (2 bytes) |
220 | | * | 0x05 int32 //int32 (4 bytes) |
221 | | * | 0x06 int64 //int64 (8 bytes) |
222 | | * | 0x07 double //floating point (8 bytes) |
223 | | * | 0x08 string //variable length string |
224 | | * | 0x09 binary //variable length binary |
225 | | * |
226 | | * container ::= |
227 | | * 0x0A int32 key_value_list //object, int32 is the total bytes of the object |
228 | | * | 0x0B int32 value_list //array, int32 is the total bytes of the array |
229 | | */ |
230 | | enum class FbsonType : char { |
231 | | T_Null = 0x00, |
232 | | T_True = 0x01, |
233 | | T_False = 0x02, |
234 | | T_Int8 = 0x03, |
235 | | T_Int16 = 0x04, |
236 | | T_Int32 = 0x05, |
237 | | T_Int64 = 0x06, |
238 | | T_Double = 0x07, |
239 | | T_String = 0x08, |
240 | | T_Binary = 0x09, |
241 | | T_Object = 0x0A, |
242 | | T_Array = 0x0B, |
243 | | NUM_TYPES, |
244 | | }; |
245 | | |
246 | | typedef std::underlying_type<FbsonType>::type FbsonTypeUnder; |
247 | | |
248 | | /* |
249 | | * FbsonKeyValue class defines FBSON key type, as described below. |
250 | | * |
251 | | * key ::= |
252 | | * 0x00 int8 //1-byte dictionary id |
253 | | * | int8 (byte*) //int8 (>0) is the size of the key string |
254 | | * |
255 | | * value ::= primitive_value | container |
256 | | * |
257 | | * FbsonKeyValue can be either an id mapping to the key string in an external |
258 | | * dictionary, or it is the original key string. Whether to read an id or a |
259 | | * string is decided by the first byte (size_). |
260 | | * |
261 | | * Note: a key object must be followed by a value object. Therefore, a key |
262 | | * object implicitly refers to a key-value pair, and you can get the value |
263 | | * object right after the key object. The function numPackedBytes hence |
264 | | * indicates the total size of the key-value pair, so that we will be able go |
265 | | * to next pair from the key. |
266 | | * |
267 | | * ** Dictionary size ** |
268 | | * By default, the dictionary size is 255 (1-byte). Users can define |
269 | | * "USE_LARGE_DICT" to increase the dictionary size to 655535 (2-byte). |
270 | | */ |
271 | | class FbsonKeyValue { |
272 | | public: |
273 | | #ifdef USE_LARGE_DICT |
274 | | static const int sMaxKeyId = 65535; |
275 | | typedef uint16_t keyid_type; |
276 | | #else |
277 | | static const int sMaxKeyId = 255; |
278 | | typedef uint8_t keyid_type; |
279 | | #endif // #ifdef USE_LARGE_DICT |
280 | | |
281 | | static const uint8_t sMaxKeyLen = 64; |
282 | | |
283 | | // size of the key. 0 indicates it is stored as id |
284 | 2.18k | uint8_t klen() const { return size_; } |
285 | | |
286 | | // get the key string. Note the string may not be null terminated. |
287 | 1.44k | const char* getKeyStr() const { return key_.str_; } |
288 | | |
289 | 0 | keyid_type getKeyId() const { return key_.id_; } |
290 | | |
291 | 2.42k | unsigned int keyPackedBytes() const { |
292 | 2.42k | return size_ ? (sizeof(size_) + size_) |
293 | 2.42k | : (sizeof(size_) + sizeof(keyid_type))0 ; |
294 | 2.42k | } |
295 | | |
296 | 1.01k | FbsonValue* value() const { |
297 | 1.01k | return (FbsonValue*)(((char*)this) + keyPackedBytes()); |
298 | 1.01k | } |
299 | | |
300 | | // size of the total packed bytes (key+value) |
301 | | unsigned int numPackedBytes() const; |
302 | | |
303 | | private: |
304 | | uint8_t size_; |
305 | | |
306 | | union key_ { |
307 | | keyid_type id_; |
308 | | char str_[1]; |
309 | | } key_; |
310 | | |
311 | | FbsonKeyValue(); |
312 | | }; |
313 | | |
314 | | /* |
315 | | * FbsonValue is the base class of all FBSON types. It contains only one member |
316 | | * variable - type info, which can be retrieved by member functions is[Type]() |
317 | | * or type(). |
318 | | */ |
319 | | class FbsonValue { |
320 | | public: |
321 | | static const uint32_t sMaxValueLen = 1 << 24; // 16M |
322 | | |
323 | 356 | bool isNull() const { return (type_ == FbsonType::T_Null); } |
324 | 20 | bool isTrue() const { return (type_ == FbsonType::T_True); } |
325 | 7 | bool isFalse() const { return (type_ == FbsonType::T_False); } |
326 | 449 | bool isInt8() const { return (type_ == FbsonType::T_Int8); } |
327 | 102 | bool isInt16() const { return (type_ == FbsonType::T_Int16); } |
328 | 99 | bool isInt32() const { return (type_ == FbsonType::T_Int32); } |
329 | 94 | bool isInt64() const { return (type_ == FbsonType::T_Int64); } |
330 | 132 | bool isDouble() const { return (type_ == FbsonType::T_Double); } |
331 | 268 | bool isString() const { return (type_ == FbsonType::T_String); } |
332 | 0 | bool isBinary() const { return (type_ == FbsonType::T_Binary); } |
333 | 1.51k | bool isObject() const { return (type_ == FbsonType::T_Object); } |
334 | 254 | bool isArray() const { return (type_ == FbsonType::T_Array); } |
335 | | |
336 | 1.60k | FbsonType type() const { return type_; } |
337 | | |
338 | | // size of the total packed bytes |
339 | | unsigned int numPackedBytes() const; |
340 | | |
341 | | // size of the value in bytes |
342 | | unsigned int size() const; |
343 | | |
344 | | // get the raw byte array of the value |
345 | | const char* getValuePtr() const; |
346 | | |
347 | | // find the FBSON value by a key path string (null terminated) |
348 | | FbsonValue* findPath(const char* key_path, |
349 | | const char* delim = ".", |
350 | 0 | hDictFind handler = nullptr) { |
351 | 0 | return findPath(key_path, (unsigned int)strlen(key_path), delim, handler); |
352 | 0 | } |
353 | | |
354 | | // find the FBSON value by a key path string (with length) |
355 | | FbsonValue* findPath(const char* key_path, |
356 | | unsigned int len, |
357 | | const char* delim, |
358 | | hDictFind handler); |
359 | | |
360 | | protected: |
361 | | FbsonType type_; // type info |
362 | | |
363 | | FbsonValue(); |
364 | | }; |
365 | | |
366 | | /* |
367 | | * NumerValT is the template class (derived from FbsonValue) of all number |
368 | | * types (integers and double). |
369 | | */ |
370 | | template <class T> |
371 | | class NumberValT : public FbsonValue { |
372 | | public: |
373 | 485 | T val() const { return num_; } fbson::NumberValT<signed char>::val() const Line | Count | Source | 373 | 306 | T val() const { return num_; } |
fbson::NumberValT<short>::val() const Line | Count | Source | 373 | 1 | T val() const { return num_; } |
fbson::NumberValT<int>::val() const Line | Count | Source | 373 | 3 | T val() const { return num_; } |
fbson::NumberValT<long long>::val() const Line | Count | Source | 373 | 54 | T val() const { return num_; } |
fbson::NumberValT<double>::val() const Line | Count | Source | 373 | 121 | T val() const { return num_; } |
|
374 | | |
375 | | unsigned int numPackedBytes() const { return sizeof(FbsonValue) + sizeof(T); } |
376 | | |
377 | | // catch all unknow specialization of the template class |
378 | | bool setVal(T value) { return false; } |
379 | | |
380 | | private: |
381 | | T num_; |
382 | | |
383 | | NumberValT(); |
384 | | }; |
385 | | |
386 | | typedef NumberValT<int8_t> Int8Val; |
387 | | |
388 | | // override setVal for Int8Val |
389 | | template <> |
390 | 0 | inline bool Int8Val::setVal(int8_t value) { |
391 | 0 | if (!isInt8()) { |
392 | 0 | return false; |
393 | 0 | } |
394 | 0 |
|
395 | 0 | num_ = value; |
396 | 0 | return true; |
397 | 0 | } |
398 | | |
399 | | typedef NumberValT<int16_t> Int16Val; |
400 | | |
401 | | // override setVal for Int16Val |
402 | | template <> |
403 | 0 | inline bool Int16Val::setVal(int16_t value) { |
404 | 0 | if (!isInt16()) { |
405 | 0 | return false; |
406 | 0 | } |
407 | 0 |
|
408 | 0 | num_ = value; |
409 | 0 | return true; |
410 | 0 | } |
411 | | |
412 | | typedef NumberValT<int32_t> Int32Val; |
413 | | |
414 | | // override setVal for Int32Val |
415 | | template <> |
416 | 0 | inline bool Int32Val::setVal(int32_t value) { |
417 | 0 | if (!isInt32()) { |
418 | 0 | return false; |
419 | 0 | } |
420 | 0 |
|
421 | 0 | num_ = value; |
422 | 0 | return true; |
423 | 0 | } |
424 | | |
425 | | typedef NumberValT<int64_t> Int64Val; |
426 | | |
427 | | // override setVal for Int64Val |
428 | | template <> |
429 | 0 | inline bool Int64Val::setVal(int64_t value) { |
430 | 0 | if (!isInt64()) { |
431 | 0 | return false; |
432 | 0 | } |
433 | 0 |
|
434 | 0 | num_ = value; |
435 | 0 | return true; |
436 | 0 | } |
437 | | |
438 | | typedef NumberValT<double> DoubleVal; |
439 | | |
440 | | // override setVal for DoubleVal |
441 | | template <> |
442 | 0 | inline bool DoubleVal::setVal(double value) { |
443 | 0 | if (!isDouble()) { |
444 | 0 | return false; |
445 | 0 | } |
446 | 0 |
|
447 | 0 | num_ = value; |
448 | 0 | return true; |
449 | 0 | } |
450 | | |
451 | | /* |
452 | | * BlobVal is the base class (derived from FbsonValue) for string and binary |
453 | | * types. The size_ indicates the total bytes of the payload_. |
454 | | */ |
455 | | class BlobVal : public FbsonValue { |
456 | | public: |
457 | | // size of the blob payload only |
458 | 208 | unsigned int getBlobLen() const { return size_; } |
459 | | |
460 | | // return the blob as byte array |
461 | 208 | const char* getBlob() const { return payload_; } |
462 | | |
463 | | // size of the total packed bytes |
464 | 602 | unsigned int numPackedBytes() const { |
465 | 602 | return sizeof(FbsonValue) + sizeof(size_) + size_; |
466 | 602 | } |
467 | | |
468 | | protected: |
469 | | uint32_t size_; |
470 | | char payload_[1]; |
471 | | |
472 | | // set new blob bytes |
473 | 0 | bool internalSetVal(const char* blob, uint32_t blobSize) { |
474 | 0 | // if we cannot fit the new blob, fail the operation |
475 | 0 | if (blobSize > size_) { |
476 | 0 | return false; |
477 | 0 | } |
478 | 0 |
|
479 | 0 | memcpy(payload_, blob, blobSize); |
480 | 0 |
|
481 | 0 | // Set the reset of the bytes to 0. Note we cannot change the size_ of the |
482 | 0 | // current payload, as all values are packed. |
483 | 0 | memset(payload_ + blobSize, 0, size_ - blobSize); |
484 | 0 |
|
485 | 0 | return true; |
486 | 0 | } |
487 | | |
488 | | BlobVal(); |
489 | | |
490 | | private: |
491 | | // Disable as this class can only be allocated dynamically |
492 | | BlobVal(const BlobVal&) = delete; |
493 | | BlobVal& operator=(const BlobVal&) = delete; |
494 | | }; |
495 | | |
496 | | /* |
497 | | * Binary type |
498 | | */ |
499 | | class BinaryVal : public BlobVal { |
500 | | public: |
501 | 0 | bool setVal(const char* blob, uint32_t blobSize) { |
502 | 0 | if (!isBinary()) { |
503 | 0 | return false; |
504 | 0 | } |
505 | 0 |
|
506 | 0 | return internalSetVal(blob, blobSize); |
507 | 0 | } |
508 | | |
509 | | private: |
510 | | BinaryVal(); |
511 | | }; |
512 | | |
513 | | /* |
514 | | * String type |
515 | | * Note: FBSON string may not be a c-string (NULL-terminated) |
516 | | */ |
517 | | class StringVal : public BlobVal { |
518 | | public: |
519 | 0 | bool setVal(const char* str, uint32_t blobSize) { |
520 | 0 | if (!isString()) { |
521 | 0 | return false; |
522 | 0 | } |
523 | 0 |
|
524 | 0 | return internalSetVal(str, blobSize); |
525 | 0 | } |
526 | | |
527 | | private: |
528 | | StringVal(); |
529 | | }; |
530 | | |
531 | | /* |
532 | | * ContainerVal is the base class (derived from FbsonValue) for object and |
533 | | * array types. The size_ indicates the total bytes of the payload_. |
534 | | */ |
535 | | class ContainerVal : public FbsonValue { |
536 | | public: |
537 | | // size of the container payload only |
538 | 0 | unsigned int getContainerSize() const { return size_; } |
539 | | |
540 | | // return the container payload as byte array |
541 | 0 | const char* getPayload() const { return payload_; } |
542 | | |
543 | | // size of the total packed bytes |
544 | 821 | unsigned int numPackedBytes() const { |
545 | 821 | return sizeof(FbsonValue) + sizeof(size_) + size_; |
546 | 821 | } |
547 | | |
548 | | protected: |
549 | | uint32_t size_; |
550 | | char payload_[1]; |
551 | | |
552 | | ContainerVal(); |
553 | | |
554 | | ContainerVal(const ContainerVal&) = delete; |
555 | | ContainerVal& operator=(const ContainerVal&) = delete; |
556 | | }; |
557 | | |
558 | | /* |
559 | | * Object type |
560 | | */ |
561 | | class ObjectVal : public ContainerVal { |
562 | | public: |
563 | | // find the FBSON value by a key string (null terminated) |
564 | 867 | FbsonValue* find(const char* key, hDictFind handler = nullptr) const { |
565 | 867 | if (!key) |
566 | 0 | return nullptr; |
567 | | |
568 | 867 | return find(key, (unsigned int)strlen(key), handler); |
569 | 867 | } |
570 | | |
571 | | // find the FBSON value by a key string (with length) |
572 | | FbsonValue* find(const char* key, |
573 | | unsigned int klen, |
574 | 867 | hDictFind handler = nullptr) const { |
575 | 867 | if (!key || !klen) |
576 | 0 | return nullptr; |
577 | | |
578 | 867 | int key_id = -1; |
579 | 867 | if (handler && (key_id = handler(key, klen)) >= 00 ) { |
580 | 0 | return find(key_id); |
581 | 0 | } |
582 | | |
583 | 867 | return internalFind(key, klen); |
584 | 867 | } |
585 | | |
586 | | // find the FBSON value by a key dictionary ID |
587 | 0 | FbsonValue* find(int key_id) const { |
588 | 0 | if (key_id < 0 || key_id > FbsonKeyValue::sMaxKeyId) |
589 | 0 | return nullptr; |
590 | | |
591 | 0 | const char* pch = payload_; |
592 | 0 | const char* fence = payload_ + size_; |
593 | |
|
594 | 0 | while (pch < fence) { |
595 | 0 | FbsonKeyValue* pkey = (FbsonKeyValue*)(pch); |
596 | 0 | if (!pkey->klen() && key_id == pkey->getKeyId()) { |
597 | 0 | return pkey->value(); |
598 | 0 | } |
599 | 0 | pch += pkey->numPackedBytes(); |
600 | 0 | } |
601 | | |
602 | 0 | assert(pch == fence); |
603 | | |
604 | 0 | return nullptr; |
605 | 0 | } |
606 | | |
607 | | typedef FbsonKeyValue value_type; |
608 | | typedef value_type* pointer; |
609 | | typedef const value_type* const_pointer; |
610 | | typedef FbsonFwdIteratorT<pointer, ObjectVal> iterator; |
611 | | typedef FbsonFwdIteratorT<const_pointer, ObjectVal> const_iterator; |
612 | | |
613 | 7 | iterator begin() { return iterator((pointer)payload_); } |
614 | | |
615 | 134 | const_iterator begin() const { return const_iterator((pointer)payload_); } |
616 | | |
617 | 7 | iterator end() { return iterator((pointer)(payload_ + size_)); } |
618 | | |
619 | 115 | const_iterator end() const { |
620 | 115 | return const_iterator((pointer)(payload_ + size_)); |
621 | 115 | } |
622 | | |
623 | | private: |
624 | 867 | FbsonValue* internalFind(const char* key, unsigned int klen) const { |
625 | 867 | const char* pch = payload_; |
626 | 867 | const char* fence = payload_ + size_; |
627 | | |
628 | 2.04k | while (pch < fence) { |
629 | 2.01k | FbsonKeyValue* pkey = (FbsonKeyValue*)(pch); |
630 | 2.01k | if (klen == pkey->klen() && strncmp(key, pkey->getKeyStr(), klen) == 01.27k ) { |
631 | 837 | return pkey->value(); |
632 | 837 | } |
633 | 1.17k | pch += pkey->numPackedBytes(); |
634 | 1.17k | } |
635 | | |
636 | 30 | assert(pch == fence); |
637 | | |
638 | 0 | return nullptr; |
639 | 867 | } |
640 | | |
641 | | private: |
642 | | ObjectVal(); |
643 | | }; |
644 | | |
645 | | /* |
646 | | * Array type |
647 | | */ |
648 | | class ArrayVal : public ContainerVal { |
649 | | public: |
650 | | // get the FBSON value at index |
651 | 160 | FbsonValue* get(int idx) const { |
652 | 160 | if (idx < 0) |
653 | 0 | return nullptr; |
654 | | |
655 | 160 | const char* pch = payload_; |
656 | 160 | const char* fence = payload_ + size_; |
657 | | |
658 | 216 | while (pch < fence && idx-- > 0) |
659 | 56 | pch += ((FbsonValue*)pch)->numPackedBytes(); |
660 | | |
661 | 160 | if (idx == -1) |
662 | 160 | return (FbsonValue*)pch; |
663 | 0 | else { |
664 | 0 | assert(pch == fence); |
665 | 0 | return nullptr; |
666 | 0 | } |
667 | 160 | } |
668 | | |
669 | | // Get number of elements in array |
670 | 142 | unsigned int numElem() const { |
671 | 142 | const char* pch = payload_; |
672 | 142 | const char* fence = payload_ + size_; |
673 | | |
674 | 142 | unsigned int num = 0; |
675 | 343 | while (pch < fence) { |
676 | 201 | ++num; |
677 | 201 | pch += ((FbsonValue*)pch)->numPackedBytes(); |
678 | 201 | } |
679 | | |
680 | 142 | assert(pch == fence); |
681 | | |
682 | 0 | return num; |
683 | 142 | } |
684 | | |
685 | | typedef FbsonValue value_type; |
686 | | typedef value_type* pointer; |
687 | | typedef const value_type* const_pointer; |
688 | | typedef FbsonFwdIteratorT<pointer, ArrayVal> iterator; |
689 | | typedef FbsonFwdIteratorT<const_pointer, ArrayVal> const_iterator; |
690 | | |
691 | 0 | iterator begin() { return iterator((pointer)payload_); } |
692 | | |
693 | 0 | const_iterator begin() const { return const_iterator((pointer)payload_); } |
694 | | |
695 | 0 | iterator end() { return iterator((pointer)(payload_ + size_)); } |
696 | | |
697 | 0 | const_iterator end() const { |
698 | 0 | return const_iterator((pointer)(payload_ + size_)); |
699 | 0 | } |
700 | | |
701 | | private: |
702 | | ArrayVal(); |
703 | | }; |
704 | | |
705 | | inline FbsonDocument* FbsonDocument::createDocument(const char* pb, |
706 | 0 | uint32_t size) { |
707 | 0 | if (!pb || size < sizeof(FbsonHeader) + sizeof(FbsonValue)) { |
708 | 0 | return nullptr; |
709 | 0 | } |
710 | 0 |
|
711 | 0 | FbsonDocument* doc = (FbsonDocument*)pb; |
712 | 0 | if (doc->header_.ver_ != FBSON_VER) { |
713 | 0 | return nullptr; |
714 | 0 | } |
715 | 0 |
|
716 | 0 | FbsonValue* val = (FbsonValue*)doc->payload_; |
717 | 0 | if (!val->isObject() || size != sizeof(FbsonHeader) + val->numPackedBytes()) { |
718 | 0 | return nullptr; |
719 | 0 | } |
720 | 0 |
|
721 | 0 | return doc; |
722 | 0 | } |
723 | | |
724 | 171 | inline FbsonValue* FbsonDocument::createValue(const char* pb, uint32_t size) { |
725 | 171 | if (!pb || size < sizeof(FbsonHeader) + sizeof(FbsonValue)) { |
726 | 0 | return nullptr; |
727 | 0 | } |
728 | | |
729 | 171 | FbsonDocument* doc = (FbsonDocument*)pb; |
730 | 171 | if (doc->header_.ver_ != FBSON_VER) { |
731 | 0 | return nullptr; |
732 | 0 | } |
733 | | |
734 | 171 | FbsonValue* val = (FbsonValue*)doc->payload_; |
735 | 171 | if (size != sizeof(FbsonHeader) + val->numPackedBytes()) { |
736 | 1 | return nullptr; |
737 | 1 | } |
738 | | |
739 | 170 | return val; |
740 | 171 | } |
741 | | |
742 | 1.41k | inline unsigned int FbsonKeyValue::numPackedBytes() const { |
743 | 1.41k | unsigned int ks = keyPackedBytes(); |
744 | 1.41k | FbsonValue* val = (FbsonValue*)(((char*)this) + ks); |
745 | 1.41k | return ks + val->numPackedBytes(); |
746 | 1.41k | } |
747 | | |
748 | | // Poor man's "virtual" function FbsonValue::numPackedBytes |
749 | 2.33k | inline unsigned int FbsonValue::numPackedBytes() const { |
750 | 2.33k | switch (type_) { |
751 | 64 | case FbsonType::T_Null: |
752 | 73 | case FbsonType::T_True: |
753 | 79 | case FbsonType::T_False: { |
754 | 79 | return sizeof(type_); |
755 | 73 | } |
756 | | |
757 | 557 | case FbsonType::T_Int8: { |
758 | 557 | return sizeof(type_) + sizeof(int8_t); |
759 | 73 | } |
760 | 0 | case FbsonType::T_Int16: { |
761 | 0 | return sizeof(type_) + sizeof(int16_t); |
762 | 73 | } |
763 | 2 | case FbsonType::T_Int32: { |
764 | 2 | return sizeof(type_) + sizeof(int32_t); |
765 | 73 | } |
766 | 184 | case FbsonType::T_Int64: { |
767 | 184 | return sizeof(type_) + sizeof(int64_t); |
768 | 73 | } |
769 | 85 | case FbsonType::T_Double: { |
770 | 85 | return sizeof(type_) + sizeof(double); |
771 | 73 | } |
772 | 602 | case FbsonType::T_String: |
773 | 602 | case FbsonType::T_Binary: { |
774 | 602 | return ((BlobVal*)(this))->numPackedBytes(); |
775 | 602 | } |
776 | | |
777 | 707 | case FbsonType::T_Object: |
778 | 821 | case FbsonType::T_Array: { |
779 | 821 | return ((ContainerVal*)(this))->numPackedBytes(); |
780 | 707 | } |
781 | 0 | default: |
782 | 0 | return 0; |
783 | 2.33k | } |
784 | 2.33k | } |
785 | | |
786 | 0 | inline unsigned int FbsonValue::size() const { |
787 | 0 | switch (type_) { |
788 | 0 | case FbsonType::T_Int8: { |
789 | 0 | return sizeof(int8_t); |
790 | 0 | } |
791 | 0 | case FbsonType::T_Int16: { |
792 | 0 | return sizeof(int16_t); |
793 | 0 | } |
794 | 0 | case FbsonType::T_Int32: { |
795 | 0 | return sizeof(int32_t); |
796 | 0 | } |
797 | 0 | case FbsonType::T_Int64: { |
798 | 0 | return sizeof(int64_t); |
799 | 0 | } |
800 | 0 | case FbsonType::T_Double: { |
801 | 0 | return sizeof(double); |
802 | 0 | } |
803 | 0 | case FbsonType::T_String: |
804 | 0 | case FbsonType::T_Binary: { |
805 | 0 | return ((BlobVal*)(this))->getBlobLen(); |
806 | 0 | } |
807 | 0 |
|
808 | 0 | case FbsonType::T_Object: |
809 | 0 | case FbsonType::T_Array: { |
810 | 0 | return ((ContainerVal*)(this))->getContainerSize(); |
811 | 0 | } |
812 | 0 | case FbsonType::T_Null: |
813 | 0 | case FbsonType::T_True: |
814 | 0 | case FbsonType::T_False: |
815 | 0 | default: |
816 | 0 | return 0; |
817 | 0 | } |
818 | 0 | } |
819 | | |
820 | 0 | inline const char* FbsonValue::getValuePtr() const { |
821 | 0 | switch (type_) { |
822 | 0 | case FbsonType::T_Int8: |
823 | 0 | case FbsonType::T_Int16: |
824 | 0 | case FbsonType::T_Int32: |
825 | 0 | case FbsonType::T_Int64: |
826 | 0 | case FbsonType::T_Double: |
827 | 0 | return ((char*)this) + sizeof(FbsonType); |
828 | 0 |
|
829 | 0 | case FbsonType::T_String: |
830 | 0 | case FbsonType::T_Binary: |
831 | 0 | return ((BlobVal*)(this))->getBlob(); |
832 | 0 |
|
833 | 0 | case FbsonType::T_Object: |
834 | 0 | case FbsonType::T_Array: |
835 | 0 | return ((ContainerVal*)(this))->getPayload(); |
836 | 0 |
|
837 | 0 | case FbsonType::T_Null: |
838 | 0 | case FbsonType::T_True: |
839 | 0 | case FbsonType::T_False: |
840 | 0 | default: |
841 | 0 | return nullptr; |
842 | 0 | } |
843 | 0 | } |
844 | | |
845 | | inline FbsonValue* FbsonValue::findPath(const char* key_path, |
846 | | unsigned int kp_len, |
847 | | const char* delim = ".", |
848 | 0 | hDictFind handler = nullptr) { |
849 | 0 | if (!key_path || !kp_len) |
850 | 0 | return nullptr; |
851 | 0 |
|
852 | 0 | if (!delim) |
853 | 0 | delim = "."; // default delimiter |
854 | 0 |
|
855 | 0 | FbsonValue* pval = this; |
856 | 0 | const char* fence = key_path + kp_len; |
857 | 0 | char idx_buf[21]; // buffer to parse array index (integer value) |
858 | 0 |
|
859 | 0 | while (pval && key_path < fence) { |
860 | 0 | const char* key = key_path; |
861 | 0 | unsigned int klen = 0; |
862 | 0 | // find the current key |
863 | 0 | for (; key_path != fence && *key_path != *delim; ++key_path, ++klen) |
864 | 0 | ; |
865 | 0 |
|
866 | 0 | if (!klen) |
867 | 0 | return nullptr; |
868 | 0 |
|
869 | 0 | switch (pval->type_) { |
870 | 0 | case FbsonType::T_Object: { |
871 | 0 | pval = ((ObjectVal*)pval)->find(key, klen, handler); |
872 | 0 | break; |
873 | 0 | } |
874 | 0 |
|
875 | 0 | case FbsonType::T_Array: { |
876 | 0 | // parse string into an integer (array index) |
877 | 0 | if (klen >= sizeof(idx_buf)) |
878 | 0 | return nullptr; |
879 | 0 |
|
880 | 0 | memcpy(idx_buf, key, klen); |
881 | 0 | idx_buf[klen] = 0; |
882 | 0 |
|
883 | 0 | char* end = nullptr; |
884 | 0 | int index = (int)strtol(idx_buf, &end, 10); |
885 | 0 | if (end && !*end) |
886 | 0 | pval = ((fbson::ArrayVal*)pval)->get(index); |
887 | 0 | else |
888 | 0 | // incorrect index string |
889 | 0 | return nullptr; |
890 | 0 | break; |
891 | 0 | } |
892 | 0 |
|
893 | 0 | default: |
894 | 0 | return nullptr; |
895 | 0 | } |
896 | 0 |
|
897 | 0 | // skip the delimiter |
898 | 0 | if (key_path < fence) { |
899 | 0 | ++key_path; |
900 | 0 | if (key_path == fence) |
901 | 0 | // we have a trailing delimiter at the end |
902 | 0 | return nullptr; |
903 | 0 | } |
904 | 0 | } |
905 | 0 |
|
906 | 0 | return pval; |
907 | 0 | } |
908 | | |
909 | | #pragma pack(pop) |
910 | | |
911 | | } // namespace fbson |
912 | | |
913 | | #endif // FBSON_FBSONDOCUMENT_H |