YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/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