1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
|
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include <zencore/zencore.h>
#include <zencore/enumflags.h>
#include <zencore/guid.h>
#include <zencore/intmath.h>
#include <zencore/iobuffer.h>
#include <zencore/iohash.h>
#include <zencore/memory.h>
#include <zencore/meta.h>
#include <zencore/sharedbuffer.h>
#include <zencore/uid.h>
#include <zencore/varint.h>
#include <functional>
#include <memory>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>
#include <gsl/gsl-lite.hpp>
namespace zen {
class CbObjectView;
class CbArrayView;
class BinaryReader;
class BinaryWriter;
class CompressedBuffer;
class CbValue;
class DateTime
{
public:
explicit DateTime(uint64_t InTicks) : Ticks(InTicks) {}
inline DateTime(int Year, int Month, int Day, int Hours = 0, int Minutes = 0, int Seconds = 0, int MilliSeconds = 0)
{
Set(Year, Month, Day, Hours, Minutes, Seconds, MilliSeconds);
}
inline uint64_t GetTicks() const { return Ticks; }
static uint64_t NowTicks();
static DateTime Now();
int GetYear() const;
int GetMonth() const;
int GetDay() const;
int GetHour() const;
int GetHour12() const;
int GetMinute() const;
int GetSecond() const;
int GetMillisecond() const;
void GetDate(int& Year, int& Month, int& Day) const;
inline bool operator==(const DateTime& Rhs) const { return Ticks == Rhs.Ticks; }
inline auto operator<=>(const DateTime& Rhs) const { return Ticks - Rhs.Ticks; }
std::string ToString(const char* Format) const;
std::string ToIso8601() const;
private:
void Set(int Year, int Month, int Day, int Hours, int Minutes, int Seconds, int MilliSecond);
uint64_t Ticks; // 1 tick == 0.1us == 100ns, epoch == Jan 1st 0001
};
class TimeSpan
{
public:
explicit TimeSpan(uint64_t InTicks) : Ticks(InTicks) {}
inline TimeSpan(int Hours, int Minutes, int Seconds) { Set(0, Hours, Minutes, Seconds, 0); }
inline TimeSpan(int Days, int Hours, int Minutes, int Seconds) { Set(Days, Hours, Minutes, Seconds, 0); }
inline TimeSpan(int Days, int Hours, int Minutes, int Seconds, int Nanos) { Set(Days, Hours, Minutes, Seconds, Nanos); }
inline uint64_t GetTicks() const { return Ticks; }
inline bool operator==(const TimeSpan& Rhs) const { return Ticks == Rhs.Ticks; }
inline auto operator<=>(const TimeSpan& Rhs) const { return Ticks - Rhs.Ticks; }
/**
* Time span related constants.
*/
/** The maximum number of ticks that can be represented in FTimespan. */
static constexpr int64_t MaxTicks = 9223372036854775807;
/** The minimum number of ticks that can be represented in FTimespan. */
static constexpr int64_t MinTicks = -9223372036854775807 - 1;
/** The number of nanoseconds per tick. */
static constexpr int64_t NanosecondsPerTick = 100;
/** The number of timespan ticks per day. */
static constexpr int64_t TicksPerDay = 864000000000;
/** The number of timespan ticks per hour. */
static constexpr int64_t TicksPerHour = 36000000000;
/** The number of timespan ticks per microsecond. */
static constexpr int64_t TicksPerMicrosecond = 10;
/** The number of timespan ticks per millisecond. */
static constexpr int64_t TicksPerMillisecond = 10000;
/** The number of timespan ticks per minute. */
static constexpr int64_t TicksPerMinute = 600000000;
/** The number of timespan ticks per second. */
static constexpr int64_t TicksPerSecond = 10000000;
/** The number of timespan ticks per week. */
static constexpr int64_t TicksPerWeek = 6048000000000;
/** The number of timespan ticks per year (365 days, not accounting for leap years). */
static constexpr int64_t TicksPerYear = 365 * TicksPerDay;
int GetFractionTicks() const { return (int)(Ticks % TicksPerSecond); }
int GetFractionMicro() const { return (int)((Ticks % TicksPerSecond) / TicksPerMicrosecond); }
int GetFractionMilli() const { return (int)((Ticks % TicksPerSecond) / TicksPerMillisecond); }
int GetFractionNano() const { return (int)((Ticks % TicksPerSecond) * NanosecondsPerTick); }
int GetDays() const { return (int)(Ticks / TicksPerDay); }
int GetHours() const { return (int)((Ticks / TicksPerHour) % 24); }
int GetMinutes() const { return (int)((Ticks / TicksPerMinute) % 60); }
int GetSeconds() const { return (int)((Ticks / TicksPerSecond) % 60); }
ZENCORE_API std::string ToString(const char* Format) const;
ZENCORE_API std::string ToString() const;
private:
void Set(int Days, int Hours, int Minutes, int Seconds, int FractionNano);
uint64_t Ticks;
};
//////////////////////////////////////////////////////////////////////////
/**
* Field types and flags for CbField.
*
* This is a private type and is only declared here to enable inline use below.
*
* DO NOT CHANGE THE VALUE OF ANY MEMBERS OF THIS ENUM!
* BACKWARD COMPATIBILITY REQUIRES THAT THESE VALUES BE FIXED!
* SERIALIZATION USES HARD-CODED CONSTANTS BASED ON THESE VALUES!
*/
enum class CbFieldType : uint8_t
{
/** A field type that does not occur in a valid object. */
None = 0x00,
/** Null. Payload is empty. */
Null = 0x01,
/**
* Object is an array of fields with unique non-empty names.
*
* Payload is a VarUInt byte count for the encoded fields followed by the fields.
*/
Object = 0x02,
/**
* UniformObject is an array of fields with the same field types and unique non-empty names.
*
* Payload is a VarUInt byte count for the encoded fields followed by the fields.
*/
UniformObject = 0x03,
/**
* Array is an array of fields with no name that may be of different types.
*
* Payload is a VarUInt byte count, followed by a VarUInt item count, followed by the fields.
*/
Array = 0x04,
/**
* UniformArray is an array of fields with no name and with the same field type.
*
* Payload is a VarUInt byte count, followed by a VarUInt item count, followed by field type,
* followed by the fields without their field type.
*/
UniformArray = 0x05,
/** Binary. Payload is a VarUInt byte count followed by the data. */
Binary = 0x06,
/** String in UTF-8. Payload is a VarUInt byte count then an unterminated UTF-8 string. */
String = 0x07,
/**
* Non-negative integer with the range of a 64-bit unsigned integer.
*
* Payload is the value encoded as a VarUInt.
*/
IntegerPositive = 0x08,
/**
* Negative integer with the range of a 64-bit signed integer.
*
* Payload is the ones' complement of the value encoded as a VarUInt.
*/
IntegerNegative = 0x09,
/** Single precision float. Payload is one big endian IEEE 754 binary32 float. */
Float32 = 0x0a,
/** Double precision float. Payload is one big endian IEEE 754 binary64 float. */
Float64 = 0x0b,
/** Boolean false value. Payload is empty. */
BoolFalse = 0x0c,
/** Boolean true value. Payload is empty. */
BoolTrue = 0x0d,
/**
* ObjectAttachment is a reference to a compact binary attachment stored externally.
*
* Payload is a 160-bit hash digest of the referenced compact binary data.
*/
ObjectAttachment = 0x0e,
/**
* BinaryAttachment is a reference to a binary attachment stored externally.
*
* Payload is a 160-bit hash digest of the referenced binary data.
*/
BinaryAttachment = 0x0f,
/** Hash. Payload is a 160-bit hash digest. */
Hash = 0x10,
/** UUID/GUID. Payload is a 128-bit UUID as defined by RFC 4122. */
Uuid = 0x11,
/**
* Date and time between 0001-01-01 00:00:00.0000000 and 9999-12-31 23:59:59.9999999.
*
* Payload is a big endian int64 count of 100ns ticks since 0001-01-01 00:00:00.0000000.
*/
DateTime = 0x12,
/**
* Difference between two date/time values.
*
* Payload is a big endian int64 count of 100ns ticks in the span, and may be negative.
*/
TimeSpan = 0x13,
/**
* Object ID
*
* Payload is a 12-byte opaque identifier
*/
ObjectId = 0x14,
/**
* CustomById identifies the sub-type of its payload by an integer identifier.
*
* Payload is a VarUInt byte count of the sub-type identifier and the sub-type payload, followed
* by a VarUInt of the sub-type identifier then the payload of the sub-type.
*/
CustomById = 0x1e,
/**
* CustomByType identifies the sub-type of its payload by a string identifier.
*
* Payload is a VarUInt byte count of the sub-type identifier and the sub-type payload, followed
* by a VarUInt byte count of the unterminated sub-type identifier, then the sub-type identifier
* without termination, then the payload of the sub-type.
*/
CustomByName = 0x1f,
/** Reserved for future use as a flag. Do not add types in this range. */
Reserved = 0x20,
/**
* A transient flag which indicates that the object or array containing this field has stored
* the field type before the payload and name. Non-uniform objects and fields will set this.
*
* Note: Since the flag must never be serialized, this bit may be repurposed in the future.
*/
HasFieldType = 0x40,
/** A persisted flag which indicates that the field has a name stored before the payload. */
HasFieldName = 0x80,
};
ENUM_CLASS_FLAGS(CbFieldType);
/** Functions that operate on CbFieldType. */
class CbFieldTypeOps
{
static constexpr CbFieldType SerializedTypeMask = CbFieldType(0b1011'1111);
static constexpr CbFieldType TypeMask = CbFieldType(0b0011'1111);
static constexpr CbFieldType ObjectMask = CbFieldType(0b0011'1110);
static constexpr CbFieldType ObjectBase = CbFieldType(0b0000'0010);
static constexpr CbFieldType ArrayMask = CbFieldType(0b0011'1110);
static constexpr CbFieldType ArrayBase = CbFieldType(0b0000'0100);
static constexpr CbFieldType IntegerMask = CbFieldType(0b0011'1110);
static constexpr CbFieldType IntegerBase = CbFieldType(0b0000'1000);
static constexpr CbFieldType FloatMask = CbFieldType(0b0011'1100);
static constexpr CbFieldType FloatBase = CbFieldType(0b0000'1000);
static constexpr CbFieldType BoolMask = CbFieldType(0b0011'1110);
static constexpr CbFieldType BoolBase = CbFieldType(0b0000'1100);
static constexpr CbFieldType AttachmentMask = CbFieldType(0b0011'1110);
static constexpr CbFieldType AttachmentBase = CbFieldType(0b0000'1110);
static void StaticAssertTypeConstants();
public:
/** The type with flags removed. */
static constexpr inline CbFieldType GetType(CbFieldType Type) { return Type & TypeMask; }
/** The type with transient flags removed. */
static constexpr inline CbFieldType GetSerializedType(CbFieldType Type) { return Type & SerializedTypeMask; }
static constexpr inline bool HasFieldType(CbFieldType Type) { return EnumHasAnyFlags(Type, CbFieldType::HasFieldType); }
static constexpr inline bool HasFieldName(CbFieldType Type) { return EnumHasAnyFlags(Type, CbFieldType::HasFieldName); }
static constexpr inline bool IsNone(CbFieldType Type) { return GetType(Type) == CbFieldType::None; }
static constexpr inline bool IsNull(CbFieldType Type) { return GetType(Type) == CbFieldType::Null; }
static constexpr inline bool IsObject(CbFieldType Type) { return (Type & ObjectMask) == ObjectBase; }
static constexpr inline bool IsArray(CbFieldType Type) { return (Type & ArrayMask) == ArrayBase; }
static constexpr inline bool IsBinary(CbFieldType Type) { return GetType(Type) == CbFieldType::Binary; }
static constexpr inline bool IsString(CbFieldType Type) { return GetType(Type) == CbFieldType::String; }
static constexpr inline bool IsInteger(CbFieldType Type) { return (Type & IntegerMask) == IntegerBase; }
/** Whether the field is a float, or integer due to implicit conversion. */
static constexpr inline bool IsFloat(CbFieldType Type) { return (Type & FloatMask) == FloatBase; }
static constexpr inline bool IsBool(CbFieldType Type) { return (Type & BoolMask) == BoolBase; }
static constexpr inline bool IsObjectAttachment(CbFieldType Type) { return GetType(Type) == CbFieldType::ObjectAttachment; }
static constexpr inline bool IsBinaryAttachment(CbFieldType Type) { return GetType(Type) == CbFieldType::BinaryAttachment; }
static constexpr inline bool IsAttachment(CbFieldType Type) { return (Type & AttachmentMask) == AttachmentBase; }
static constexpr inline bool IsHash(CbFieldType Type)
{
switch (GetType(Type))
{
case CbFieldType::Hash:
case CbFieldType::BinaryAttachment:
case CbFieldType::ObjectAttachment:
return true;
default:
return false;
}
}
static constexpr inline bool IsUuid(CbFieldType Type) { return GetType(Type) == CbFieldType::Uuid; }
static constexpr inline bool IsObjectId(CbFieldType Type) { return GetType(Type) == CbFieldType::ObjectId; }
static constexpr inline bool IsCustomById(CbFieldType Type) { return GetType(Type) == CbFieldType::CustomById; }
static constexpr inline bool IsCustomByName(CbFieldType Type) { return GetType(Type) == CbFieldType::CustomByName; }
static constexpr inline bool IsDateTime(CbFieldType Type) { return GetType(Type) == CbFieldType::DateTime; }
static constexpr inline bool IsTimeSpan(CbFieldType Type) { return GetType(Type) == CbFieldType::TimeSpan; }
/** Whether the type is or may contain fields of any attachment type. */
static constexpr inline bool MayContainAttachments(CbFieldType Type)
{
return int(IsObject(Type) == true) | int(IsArray(Type) == true) | int(IsAttachment(Type) == true);
}
};
/** Errors that can occur when accessing a field. */
enum class CbFieldError : uint8_t
{
/** The field is not in an error state. */
None,
/** The value type does not match the requested type. */
TypeError,
/** The value is out of range for the requested type. */
RangeError,
};
class ICbVisitor
{
public:
virtual void SetName(std::string_view Name) = 0;
virtual void BeginObject() = 0;
virtual void EndObject() = 0;
virtual void BeginArray() = 0;
virtual void EndArray() = 0;
virtual void VisitNull() = 0;
virtual void VisitBinary(SharedBuffer Value) = 0;
virtual void VisitString(std::string_view Value) = 0;
virtual void VisitInteger(int64_t Value) = 0;
virtual void VisitInteger(uint64_t Value) = 0;
virtual void VisitFloat(float Value) = 0;
virtual void VisitDouble(double Value) = 0;
virtual void VisitBool(bool value) = 0;
virtual void VisitCbAttachment(const IoHash& Value) = 0;
virtual void VisitBinaryAttachment(const IoHash& Value) = 0;
virtual void VisitHash(const IoHash& Value) = 0;
virtual void VisitUuid(const Guid& Value) = 0;
virtual void VisitObjectId(const Oid& Value) = 0;
virtual void VisitDateTime(DateTime Value) = 0;
virtual void VisitTimeSpan(TimeSpan Value) = 0;
};
/** A custom compact binary field type with an integer identifier. */
struct CbCustomById
{
/** An identifier for the sub-type of the field. */
uint64_t Id = 0;
/** A view of the value. Lifetime is tied to the field that the value is associated with. */
MemoryView Data;
};
/** A custom compact binary field type with a string identifier. */
struct CbCustomByName
{
/** An identifier for the sub-type of the field. Lifetime is tied to the field that the name is associated with. */
std::u8string_view Name;
/** A view of the value. Lifetime is tied to the field that the value is associated with. */
MemoryView Data;
};
namespace CompactBinaryPrivate {
/** Parameters for converting to an integer. */
struct IntegerParams
{
/** Whether the output type has a sign bit. */
uint32_t IsSigned : 1;
/** Bits of magnitude. (7 for int8) */
uint32_t MagnitudeBits : 31;
};
/** Make integer params for the given integer type. */
template<typename IntType>
static constexpr inline IntegerParams MakeIntegerParams()
{
IntegerParams Params;
Params.IsSigned = IntType(-1) < IntType(0);
Params.MagnitudeBits = 8 * sizeof(IntType) - Params.IsSigned;
return Params;
}
} // namespace CompactBinaryPrivate
/**
* An atom of data in the compact binary format.
*
* Accessing the value of a field is always a safe operation, even if accessed as the wrong type.
* An invalid access will return a default value for the requested type, and set an error code on
* the field that can be checked with GetLastError and HasLastError. A valid access will clear an
* error from a previous invalid access.
*
* A field is encoded in one or more bytes, depending on its type and the type of object or array
* that contains it. A field of an object or array which is non-uniform encodes its field type in
* the first byte, and includes the HasFieldName flag for a field in an object. The field name is
* encoded in a variable-length unsigned integer of its size in bytes, for named fields, followed
* by that many bytes of the UTF-8 encoding of the name with no null terminator. The remainder of
* the field is the payload and is described in the field type enum. Every field must be uniquely
* addressable when encoded, which means a zero-byte field is not permitted, and only arises in a
* uniform array of fields with no payload, where the answer is to encode as a non-uniform array.
*
* This type only provides a view into memory and does not perform any memory management itself.
* Use CbFieldRef to hold a reference to the underlying memory when necessary.
*/
class CbFieldView
{
public:
CbFieldView() = default;
ZENCORE_API CbFieldView(const void* DataPointer, CbFieldType FieldType = CbFieldType::HasFieldType);
/** Construct a field from a value, without access to the name. */
inline explicit CbFieldView(const CbValue& Value);
/** Returns the name of the field if it has a name, otherwise an empty view. */
constexpr inline std::string_view GetName() const { return std::string_view(static_cast<const char*>(Payload) - NameLen, NameLen); }
/** Returns the name of the field if it has a name, otherwise an empty view. */
constexpr inline std::u8string_view GetU8Name() const
{
return std::u8string_view(static_cast<const char8_t*>(Payload) - NameLen, NameLen);
}
/** Returns the value for unchecked access. Prefer the typed accessors below. */
inline CbValue GetValue() const;
ZENCORE_API MemoryView AsBinaryView(MemoryView Default = MemoryView());
ZENCORE_API CbObjectView AsObjectView();
ZENCORE_API CbArrayView AsArrayView();
ZENCORE_API std::string_view AsString(std::string_view Default = std::string_view());
ZENCORE_API std::u8string_view AsU8String(std::u8string_view Default = std::u8string_view());
ZENCORE_API void IterateAttachments(std::function<void(CbFieldView)> Visitor) const;
/** Access the field as an int8. Returns the provided default on error. */
inline int8_t AsInt8(int8_t Default = 0) { return AsInteger<int8_t>(Default); }
/** Access the field as an int16. Returns the provided default on error. */
inline int16_t AsInt16(int16_t Default = 0) { return AsInteger<int16_t>(Default); }
/** Access the field as an int32. Returns the provided default on error. */
inline int32_t AsInt32(int32_t Default = 0) { return AsInteger<int32_t>(Default); }
/** Access the field as an int64. Returns the provided default on error. */
inline int64_t AsInt64(int64_t Default = 0) { return AsInteger<int64_t>(Default); }
/** Access the field as a uint8. Returns the provided default on error. */
inline uint8_t AsUInt8(uint8_t Default = 0) { return AsInteger<uint8_t>(Default); }
/** Access the field as a uint16. Returns the provided default on error. */
inline uint16_t AsUInt16(uint16_t Default = 0) { return AsInteger<uint16_t>(Default); }
/** Access the field as a uint32. Returns the provided default on error. */
inline uint32_t AsUInt32(uint32_t Default = 0) { return AsInteger<uint32_t>(Default); }
/** Access the field as a uint64. Returns the provided default on error. */
inline uint64_t AsUInt64(uint64_t Default = 0) { return AsInteger<uint64_t>(Default); }
/** Access the field as a float. Returns the provided default on error. */
ZENCORE_API float AsFloat(float Default = 0.0f);
/** Access the field as a double. Returns the provided default on error. */
ZENCORE_API double AsDouble(double Default = 0.0);
/** Access the field as a bool. Returns the provided default on error. */
ZENCORE_API bool AsBool(bool bDefault = false);
/** Access the field as a hash referencing a compact binary attachment. Returns the provided default on error. */
ZENCORE_API IoHash AsObjectAttachment(const IoHash& Default = IoHash());
/** Access the field as a hash referencing a binary attachment. Returns the provided default on error. */
ZENCORE_API IoHash AsBinaryAttachment(const IoHash& Default = IoHash());
/** Access the field as a hash referencing an attachment. Returns the provided default on error. */
ZENCORE_API IoHash AsAttachment(const IoHash& Default = IoHash());
/** Access the field as a hash. Returns the provided default on error. */
ZENCORE_API IoHash AsHash(const IoHash& Default = IoHash());
/** Access the field as a UUID. Returns a nil UUID on error. */
ZENCORE_API Guid AsUuid();
/** Access the field as a UUID. Returns the provided default on error. */
ZENCORE_API Guid AsUuid(const Guid& Default);
/** Access the field as an OID. Returns a nil OID on error. */
ZENCORE_API Oid AsObjectId();
/** Access the field as a OID. Returns the provided default on error. */
ZENCORE_API Oid AsObjectId(const Oid& Default);
/** Access the field as a custom sub-type with an integer identifier. Returns the provided default on error. */
ZENCORE_API CbCustomById AsCustomById(CbCustomById Default);
/** Access the field as a custom sub-type with a string identifier. Returns the provided default on error. */
ZENCORE_API CbCustomByName AsCustomByName(CbCustomByName Default);
/** Access the field as a date/time tick count. Returns the provided default on error. */
ZENCORE_API int64_t AsDateTimeTicks(int64_t Default = 0);
/** Access the field as a date/time. Returns a date/time at the epoch on error. */
ZENCORE_API DateTime AsDateTime();
/** Access the field as a date/time. Returns the provided default on error. */
ZENCORE_API DateTime AsDateTime(DateTime Default);
/** Access the field as a timespan tick count. Returns the provided default on error. */
ZENCORE_API int64_t AsTimeSpanTicks(int64_t Default = 0);
/** Access the field as a timespan. Returns an empty timespan on error. */
ZENCORE_API TimeSpan AsTimeSpan();
/** Access the field as a timespan. Returns the provided default on error. */
ZENCORE_API TimeSpan AsTimeSpan(TimeSpan Default);
/** True if the field has a name. */
constexpr inline bool HasName() const { return CbFieldTypeOps::HasFieldName(Type); }
constexpr inline bool IsNull() const { return CbFieldTypeOps::IsNull(Type); }
constexpr inline bool IsObject() const { return CbFieldTypeOps::IsObject(Type); }
constexpr inline bool IsArray() const { return CbFieldTypeOps::IsArray(Type); }
constexpr inline bool IsBinary() const { return CbFieldTypeOps::IsBinary(Type); }
constexpr inline bool IsString() const { return CbFieldTypeOps::IsString(Type); }
/** Whether the field is an integer of unspecified range and sign. */
constexpr inline bool IsInteger() const { return CbFieldTypeOps::IsInteger(Type); }
/** Whether the field is a float, or integer that supports implicit conversion. */
constexpr inline bool IsFloat() const { return CbFieldTypeOps::IsFloat(Type); }
constexpr inline bool IsBool() const { return CbFieldTypeOps::IsBool(Type); }
constexpr inline bool IsObjectAttachment() const { return CbFieldTypeOps::IsObjectAttachment(Type); }
constexpr inline bool IsBinaryAttachment() const { return CbFieldTypeOps::IsBinaryAttachment(Type); }
constexpr inline bool IsAttachment() const { return CbFieldTypeOps::IsAttachment(Type); }
constexpr inline bool IsHash() const { return CbFieldTypeOps::IsHash(Type); }
constexpr inline bool IsUuid() const { return CbFieldTypeOps::IsUuid(Type); }
constexpr inline bool IsObjectId() const { return CbFieldTypeOps::IsObjectId(Type); }
constexpr inline bool IsDateTime() const { return CbFieldTypeOps::IsDateTime(Type); }
constexpr inline bool IsTimeSpan() const { return CbFieldTypeOps::IsTimeSpan(Type); }
/** Whether the field has a value. */
constexpr inline explicit operator bool() const { return HasValue(); }
/**
* Whether the field has a value.
*
* All fields in a valid object or array have a value. A field with no value is returned when
* finding a field by name fails or when accessing an iterator past the end.
*/
constexpr inline bool HasValue() const { return !CbFieldTypeOps::IsNone(Type); };
/** Whether the last field access encountered an error. */
constexpr inline bool HasError() const { return Error != CbFieldError::None; }
/** The type of error that occurred on the last field access, or None. */
constexpr inline CbFieldError GetError() const { return Error; }
/** Returns the size of the field in bytes, including the type and name. */
ZENCORE_API uint64_t GetSize() const;
/** Calculate the hash of the field, including the type and name. */
ZENCORE_API IoHash GetHash() const;
ZENCORE_API void GetHash(IoHashStream& HashStream) const;
/** Feed the field (including type and name) to the stream function */
inline void WriteToStream(auto Hash) const
{
const CbFieldType SerializedType = CbFieldTypeOps::GetSerializedType(Type);
Hash(&SerializedType, sizeof(SerializedType));
auto View = GetViewNoType();
Hash(View.GetData(), View.GetSize());
}
/** Copy the field into a buffer of exactly GetSize() bytes, including the type and name. */
ZENCORE_API void CopyTo(MutableMemoryView Buffer) const;
/** Copy the field into an archive, including its type and name. */
ZENCORE_API void CopyTo(BinaryWriter& Ar) const;
/**
* Whether this field is identical to the other field.
*
* Performs a deep comparison of any contained arrays or objects and their fields. Comparison
* assumes that both fields are valid and are written in the canonical format. Fields must be
* written in the same order in arrays and objects, and name comparison is case sensitive. If
* these assumptions do not hold, this may return false for equivalent inputs. Validation can
* be performed with ValidateCompactBinary, except for field order and field name case.
*/
ZENCORE_API bool Equals(const CbFieldView& Other) const;
/** Returns a view of the field, including the type and name when present. */
ZENCORE_API MemoryView GetView() const;
/**
* Try to get a view of the field as it would be serialized, such as by CopyTo.
*
* A serialized view is not available if the field has an externally-provided type.
* Access the serialized form of such fields using CopyTo or FCbFieldRef::Clone.
*/
inline bool TryGetSerializedView(MemoryView& OutView) const
{
if (CbFieldTypeOps::HasFieldType(Type))
{
OutView = GetView();
return true;
}
return false;
}
protected:
/** Returns a view of the name and value payload, which excludes the type. */
ZENCORE_API MemoryView GetViewNoType() const;
/** Returns a view of the value payload, which excludes the type and name. */
inline MemoryView GetPayloadView() const { return MemoryView(Payload, GetPayloadSize()); }
/** Returns the type of the field including flags. */
constexpr inline CbFieldType GetType() const { return Type; }
/** Returns the start of the value payload. */
constexpr inline const void* GetPayload() const { return Payload; }
/** Returns the end of the value payload. */
inline const void* GetPayloadEnd() const { return static_cast<const uint8_t*>(Payload) + GetPayloadSize(); }
/** Returns the size of the value payload in bytes, which is the field excluding the type and name. */
ZENCORE_API uint64_t GetPayloadSize() const;
/** Assign a field from a pointer to its data and an optional externally-provided type. */
inline void Assign(const void* InData, const CbFieldType InType)
{
static_assert(std::is_trivially_destructible<CbFieldView>::value,
"This optimization requires CbField to be trivially destructible!");
new (this) CbFieldView(InData, InType);
}
private:
/**
* Access the field as the given integer type.
*
* Returns the provided default if the value cannot be represented in the output type.
*/
template<typename IntType>
inline IntType AsInteger(IntType Default)
{
return IntType(AsInteger(uint64_t(Default), CompactBinaryPrivate::MakeIntegerParams<IntType>()));
}
ZENCORE_API uint64_t AsInteger(uint64_t Default, CompactBinaryPrivate::IntegerParams Params);
private:
/** The field type, with the transient HasFieldType flag if the field contains its type. */
CbFieldType Type = CbFieldType::None;
/** The error (if any) that occurred on the last field access. */
CbFieldError Error = CbFieldError::None;
/** The number of bytes for the name stored before the payload. */
uint32_t NameLen = 0;
/** The value payload, which also points to the end of the name. */
const void* Payload = nullptr;
};
template<typename FieldType>
class TCbFieldIterator : public FieldType
{
public:
/** Construct an empty field range. */
constexpr TCbFieldIterator() = default;
inline TCbFieldIterator& operator++()
{
const void* const PayloadEnd = FieldType::GetPayloadEnd();
const int64_t AtEndMask = int64_t(PayloadEnd == FieldsEnd) - 1;
const CbFieldType NextType = CbFieldType(int64_t(FieldType::GetType()) & AtEndMask);
const void* const NextField = reinterpret_cast<const void*>(int64_t(PayloadEnd) & AtEndMask);
const void* const NextFieldsEnd = reinterpret_cast<const void*>(int64_t(FieldsEnd) & AtEndMask);
FieldType::Assign(NextField, NextType);
FieldsEnd = NextFieldsEnd;
return *this;
}
inline TCbFieldIterator operator++(int)
{
TCbFieldIterator It(*this);
++*this;
return It;
}
constexpr inline FieldType& operator*() { return *this; }
constexpr inline FieldType* operator->() { return this; }
/** Reset this to an empty field range. */
inline void Reset() { *this = TCbFieldIterator(); }
/** Returns the size of the fields in the range in bytes. */
ZENCORE_API uint64_t GetRangeSize() const;
/** Calculate the hash of every field in the range. */
ZENCORE_API IoHash GetRangeHash() const;
ZENCORE_API void GetRangeHash(IoHashStream& Hash) const;
using FieldType::Equals;
template<typename OtherFieldType>
constexpr inline bool Equals(const TCbFieldIterator<OtherFieldType>& Other) const
{
return FieldType::GetPayload() == Other.OtherFieldType::GetPayload() && FieldsEnd == Other.FieldsEnd;
}
template<typename OtherFieldType>
constexpr inline bool operator==(const TCbFieldIterator<OtherFieldType>& Other) const
{
return Equals(Other);
}
template<typename OtherFieldType>
constexpr inline bool operator!=(const TCbFieldIterator<OtherFieldType>& Other) const
{
return !Equals(Other);
}
/** Copy the field range into a buffer of exactly GetRangeSize() bytes. */
ZENCORE_API void CopyRangeTo(MutableMemoryView Buffer) const;
/** Invoke the visitor for every attachment in the field range. */
ZENCORE_API void IterateRangeAttachments(std::function<void(CbFieldView)> Visitor) const;
/** Create a view of every field in the range. */
inline MemoryView GetRangeView() const { return MemoryView(FieldType::GetView().GetData(), FieldsEnd); }
/**
* Try to get a view of every field in the range as they would be serialized.
*
* A serialized view is not available if the underlying fields have an externally-provided type.
* Access the serialized form of such ranges using CbFieldRefIterator::CloneRange.
*/
inline bool TryGetSerializedRangeView(MemoryView& OutView) const
{
if (CbFieldTypeOps::HasFieldType(FieldType::GetType()))
{
OutView = GetRangeView();
return true;
}
return false;
}
protected:
/** Construct a field range that contains exactly one field. */
constexpr inline explicit TCbFieldIterator(FieldType InField) : FieldType(std::move(InField)), FieldsEnd(FieldType::GetPayloadEnd()) {}
/**
* Construct a field range from the first field and a pointer to the end of the last field.
*
* @param InField The first field, or the default field if there are no fields.
* @param InFieldsEnd A pointer to the end of the payload of the last field, or null.
*/
constexpr inline TCbFieldIterator(FieldType&& InField, const void* InFieldsEnd) : FieldType(std::move(InField)), FieldsEnd(InFieldsEnd)
{
}
/** Returns the end of the last field, or null for an iterator at the end. */
template<typename OtherFieldType>
static inline const void* GetFieldsEnd(const TCbFieldIterator<OtherFieldType>& It)
{
return It.FieldsEnd;
}
private:
friend inline TCbFieldIterator begin(const TCbFieldIterator& Iterator) { return Iterator; }
friend inline TCbFieldIterator end(const TCbFieldIterator&) { return TCbFieldIterator(); }
private:
template<typename OtherType>
friend class TCbFieldIterator;
friend class CbFieldViewIterator;
friend class CbFieldIterator;
/** Pointer to the first byte past the end of the last field. Set to null at the end. */
const void* FieldsEnd = nullptr;
};
/**
* Iterator for CbField.
*
* @see CbFieldIterator
*/
class CbFieldViewIterator : public TCbFieldIterator<CbFieldView>
{
public:
constexpr CbFieldViewIterator() = default;
/** Construct a field range that contains exactly one field. */
static inline CbFieldViewIterator MakeSingle(const CbFieldView& Field) { return CbFieldViewIterator(Field); }
/**
* Construct a field range from a buffer containing zero or more valid fields.
*
* @param View A buffer containing zero or more valid fields.
* @param Type HasFieldType means that View contains the type. Otherwise, use the given type.
*/
static inline CbFieldViewIterator MakeRange(MemoryView View, CbFieldType Type = CbFieldType::HasFieldType)
{
return !View.IsEmpty() ? TCbFieldIterator(CbFieldView(View.GetData(), Type), View.GetDataEnd()) : CbFieldViewIterator();
}
/** Construct an iterator from another iterator. */
template<typename OtherFieldType>
inline CbFieldViewIterator(const TCbFieldIterator<OtherFieldType>& It)
: TCbFieldIterator(ImplicitConv<CbFieldView>(It), GetFieldsEnd(It))
{
}
private:
using TCbFieldIterator::TCbFieldIterator;
};
/**
* Serialize a compact binary array to JSON.
*/
ZENCORE_API void CompactBinaryToJson(const CbArrayView& Object, StringBuilderBase& Builder);
/**
* Array of CbField that have no names.
*
* Accessing a field of the array requires iteration. Access by index is not provided because the
* cost of accessing an item by index scales linearly with the index.
*
* This type only provides a view into memory and does not perform any memory management itself.
* Use CbArrayRef to hold a reference to the underlying memory when necessary.
*/
class CbArrayView : protected CbFieldView
{
friend class CbFieldView;
public:
/** @see CbField::CbField */
using CbFieldView::CbFieldView;
using CbFieldView::TryGetSerializedView;
/** Construct an array with no fields. */
ZENCORE_API CbArrayView();
/** Returns the number of items in the array. */
ZENCORE_API uint64_t Num() const;
/** Create an iterator for the fields of this array. */
ZENCORE_API CbFieldViewIterator CreateViewIterator() const;
/** Visit the fields of this array. */
ZENCORE_API void VisitFields(ICbVisitor& Visitor);
/** Access the array as an array field. */
inline CbFieldView AsFieldView() const { return static_cast<const CbFieldView&>(*this); }
/** Construct an array from an array field. No type check is performed! */
static inline CbArrayView FromFieldView(const CbFieldView& Field) { return CbArrayView(Field); }
/** Whether the array has any fields. */
inline explicit operator bool() const { return Num() > 0; }
/** Returns the size of the array in bytes if serialized by itself with no name. */
ZENCORE_API uint64_t GetSize() const;
/** Calculate the hash of the array if serialized by itself with no name. */
ZENCORE_API IoHash GetHash() const;
ZENCORE_API void GetHash(IoHashStream& Stream) const;
/**
* Whether this array is identical to the other array.
*
* Performs a deep comparison of any contained arrays or objects and their fields. Comparison
* assumes that both fields are valid and are written in the canonical format. Fields must be
* written in the same order in arrays and objects, and name comparison is case sensitive. If
* these assumptions do not hold, this may return false for equivalent inputs. Validation can
* be done with the All mode to check these assumptions about the format of the inputs.
*/
ZENCORE_API bool Equals(const CbArrayView& Other) const;
/** Copy the array into a buffer of exactly GetSize() bytes, with no name. */
ZENCORE_API void CopyTo(MutableMemoryView Buffer) const;
/** Copy the array into an archive, including its type and name. */
ZENCORE_API void CopyTo(BinaryWriter& Ar) const;
///** Invoke the visitor for every attachment in the array. */
inline void IterateAttachments(std::function<void(CbFieldView)> Visitor) const
{
CreateViewIterator().IterateRangeAttachments(Visitor);
}
/** Returns a view of the array, including the type and name when present. */
using CbFieldView::GetView;
StringBuilderBase& ToJson(StringBuilderBase& Builder) const
{
CompactBinaryToJson(*this, Builder);
return Builder;
}
private:
friend inline CbFieldViewIterator begin(const CbArrayView& Array) { return Array.CreateViewIterator(); }
friend inline CbFieldViewIterator end(const CbArrayView&) { return CbFieldViewIterator(); }
/** Construct an array from an array field. No type check is performed! Use via FromField. */
inline explicit CbArrayView(const CbFieldView& Field) : CbFieldView(Field) {}
};
/**
* Serialize a compact binary object to JSON.
*/
ZENCORE_API void CompactBinaryToJson(const CbObjectView& Object, StringBuilderBase& Builder);
class CbObjectView : protected CbFieldView
{
friend class CbFieldView;
public:
/** @see CbField::CbField */
using CbFieldView::CbFieldView;
using CbFieldView::TryGetSerializedView;
/** Construct an object with no fields. */
ZENCORE_API CbObjectView();
/** Create an iterator for the fields of this object. */
ZENCORE_API CbFieldViewIterator CreateViewIterator() const;
/** Visit the fields of this object. */
ZENCORE_API void VisitFields(ICbVisitor& Visitor);
/**
* Find a field by case-sensitive name comparison.
*
* The cost of this operation scales linearly with the number of fields in the object. Prefer
* to iterate over the fields only once when consuming an object.
*
* @param Name The name of the field.
* @return The matching field if found, otherwise a field with no value.
*/
ZENCORE_API CbFieldView FindView(std::string_view Name) const;
/** Find a field by case-insensitive name comparison. */
ZENCORE_API CbFieldView FindViewIgnoreCase(std::string_view Name) const;
/** Find a field by case-sensitive name comparison. */
inline CbFieldView operator[](std::string_view Name) const { return FindView(Name); }
/** Access the object as an object field. */
inline CbFieldView AsFieldView() const { return static_cast<const CbFieldView&>(*this); }
/** Construct an object from an object field. No type check is performed! */
static inline CbObjectView FromFieldView(const CbFieldView& Field) { return CbObjectView(Field); }
/** Whether the object has any fields. */
ZENCORE_API explicit operator bool() const;
/** Returns the size of the object in bytes if serialized by itself with no name. */
ZENCORE_API uint64_t GetSize() const;
/** Calculate the hash of the object if serialized by itself with no name. */
ZENCORE_API IoHash GetHash() const;
ZENCORE_API void GetHash(IoHashStream& HashStream) const;
/**
* Whether this object is identical to the other object.
*
* Performs a deep comparison of any contained arrays or objects and their fields. Comparison
* assumes that both fields are valid and are written in the canonical format. Fields must be
* written in the same order in arrays and objects, and name comparison is case sensitive. If
* these assumptions do not hold, this may return false for equivalent inputs. Validation can
* be done with the All mode to check these assumptions about the format of the inputs.
*/
ZENCORE_API bool Equals(const CbObjectView& Other) const;
/** Copy the object into a buffer of exactly GetSize() bytes, with no name. */
ZENCORE_API void CopyTo(MutableMemoryView Buffer) const;
/** Copy the field into an archive, including its type and name. */
ZENCORE_API void CopyTo(BinaryWriter& Ar) const;
///** Invoke the visitor for every attachment in the object. */
inline void IterateAttachments(std::function<void(CbFieldView)> Visitor) const
{
CreateViewIterator().IterateRangeAttachments(Visitor);
}
/** Returns a view of the object, including the type and name when present. */
using CbFieldView::GetView;
/** Whether the field has a value. */
using CbFieldView::operator bool;
StringBuilderBase& ToJson(StringBuilderBase& Builder) const
{
CompactBinaryToJson(*this, Builder);
return Builder;
}
private:
friend inline CbFieldViewIterator begin(const CbObjectView& Object) { return Object.CreateViewIterator(); }
friend inline CbFieldViewIterator end(const CbObjectView&) { return CbFieldViewIterator(); }
/** Construct an object from an object field. No type check is performed! Use via FromField. */
inline explicit CbObjectView(const CbFieldView& Field) : CbFieldView(Field) {}
};
//////////////////////////////////////////////////////////////////////////
/** A reference to a function that is used to allocate buffers for compact binary data. */
using BufferAllocator = std::function<UniqueBuffer(uint64_t Size)>;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/** A wrapper that holds a reference to the buffer that contains its compact binary value. */
template<typename BaseType>
class CbBuffer : public BaseType
{
public:
/** Construct a default value. */
CbBuffer() = default;
/**
* Construct a value from a pointer to its data and an optional externally-provided type.
*
* @param ValueBuffer A buffer that exactly contains the value.
* @param Type HasFieldType means that ValueBuffer contains the type. Otherwise, use the given type.
*/
inline explicit CbBuffer(SharedBuffer ValueBuffer, CbFieldType Type = CbFieldType::HasFieldType)
{
if (ValueBuffer)
{
BaseType::operator=(BaseType(ValueBuffer.GetData(), Type));
ZEN_ASSERT(ValueBuffer.GetView().Contains(BaseType::GetView()));
Buffer = std::move(ValueBuffer);
}
}
/** Construct a value that holds a reference to the buffer that contains it. */
inline CbBuffer(const BaseType& Value, SharedBuffer OuterBuffer) : BaseType(Value)
{
if (OuterBuffer)
{
ZEN_ASSERT(OuterBuffer.GetView().Contains(BaseType::GetView()));
Buffer = std::move(OuterBuffer);
}
}
/** Construct a value that holds a reference to the buffer of the outer that contains it. */
template<typename OtherBaseType>
inline CbBuffer(const BaseType& Value, CbBuffer<OtherBaseType> OuterRef) : CbBuffer(Value, std::move(OuterRef.Buffer))
{
}
/** Reset this to a default value and null buffer. */
inline void Reset() { *this = CbBuffer(); }
/** Whether this reference has ownership of the memory in its buffer. */
inline bool IsOwned() const { return Buffer && Buffer.IsOwned(); }
/** Clone the value, if necessary, to a buffer that this reference has ownership of. */
inline void MakeOwned()
{
if (!IsOwned())
{
UniqueBuffer MutableBuffer = UniqueBuffer::Alloc(BaseType::GetSize());
BaseType::CopyTo(MutableBuffer);
BaseType::operator=(BaseType(MutableBuffer.GetData()));
Buffer = std::move(MutableBuffer);
}
}
/** Returns a buffer that exactly contains this value. */
inline SharedBuffer GetBuffer() const
{
const MemoryView View = BaseType::GetView();
const SharedBuffer& OuterBuffer = GetOuterBuffer();
return View == OuterBuffer.GetView() ? OuterBuffer : SharedBuffer::MakeView(View, OuterBuffer);
}
/** Returns the outer buffer (if any) that contains this value. */
inline const SharedBuffer& GetOuterBuffer() const& { return Buffer; }
inline SharedBuffer GetOuterBuffer() && { return std::move(Buffer); }
private:
template<typename OtherType>
friend class CbBuffer;
SharedBuffer Buffer;
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Factory functions for types derived from CbBuffer.
*
* This uses the curiously recurring template pattern to construct the correct type of reference.
* The derived type inherits from CbBufferRef and this type to expose the factory functions.
*/
template<typename RefType, typename BaseType>
class CbBufferFactory
{
public:
/** Construct a value from an owned clone of its memory. */
static inline RefType Clone(const void* const Data) { return Clone(BaseType(Data)); }
/** Construct a value from an owned clone of its memory. */
static inline RefType Clone(const BaseType& Value)
{
RefType Ref = MakeView(Value);
Ref.MakeOwned();
return Ref;
}
/** Construct a value from a read-only view of its memory and its optional outer buffer. */
static inline RefType MakeView(const void* const Data, SharedBuffer OuterBuffer = SharedBuffer())
{
return MakeView(BaseType(Data), std::move(OuterBuffer));
}
/** Construct a value from a read-only view of its memory and its optional outer buffer. */
static inline RefType MakeView(const BaseType& Value, SharedBuffer OuterBuffer = SharedBuffer())
{
return RefType(Value, std::move(OuterBuffer));
}
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class CbArray;
class CbObject;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* A field that can hold a reference to the memory that contains it.
*
* @see CbBufferRef
*/
class CbField : public CbBuffer<CbFieldView>, public CbBufferFactory<CbField, CbFieldView>
{
public:
using CbBuffer::CbBuffer;
/** Access the field as an object. Defaults to an empty object on error. */
inline CbObject AsObject() &;
inline CbObject AsObject() &&;
/** Access the field as an array. Defaults to an empty array on error. */
inline CbArray AsArray() &;
inline CbArray AsArray() &&;
/** Access the field as binary. Returns the provided default on error. */
inline SharedBuffer AsBinary(const SharedBuffer& Default = SharedBuffer()) &;
inline SharedBuffer AsBinary(const SharedBuffer& Default = SharedBuffer()) &&;
};
/**
* Iterator for CbFieldRef.
*
* @see CbFieldIterator
*/
class CbFieldIterator : public TCbFieldIterator<CbField>
{
public:
/** Construct a field range from an owned clone of a range. */
ZENCORE_API static CbFieldIterator CloneRange(const CbFieldViewIterator& It);
/** Construct a field range from an owned clone of a range. */
static inline CbFieldIterator CloneRange(const CbFieldIterator& It) { return CloneRange(CbFieldViewIterator(It)); }
/** Construct a field range that contains exactly one field. */
static inline CbFieldIterator MakeSingle(CbField Field) { return CbFieldIterator(std::move(Field)); }
/**
* Construct a field range from a buffer containing zero or more valid fields.
*
* @param Buffer A buffer containing zero or more valid fields.
* @param Type HasFieldType means that Buffer contains the type. Otherwise, use the given type.
*/
static inline CbFieldIterator MakeRange(SharedBuffer Buffer, CbFieldType Type = CbFieldType::HasFieldType)
{
if (Buffer.GetSize())
{
const void* const DataEnd = Buffer.GetView().GetDataEnd();
return CbFieldIterator(CbField(std::move(Buffer), Type), DataEnd);
}
return CbFieldIterator();
}
/** Construct a field range from an iterator and its optional outer buffer. */
static inline CbFieldIterator MakeRangeView(const CbFieldViewIterator& It, SharedBuffer OuterBuffer = SharedBuffer())
{
return CbFieldIterator(CbField(It, std::move(OuterBuffer)), GetFieldsEnd(It));
}
/** Construct an empty field range. */
constexpr CbFieldIterator() = default;
/** Clone the range, if necessary, to a buffer that this reference has ownership of. */
inline void MakeRangeOwned()
{
if (!IsOwned())
{
*this = CloneRange(*this);
}
}
/** Returns a buffer that exactly contains the field range. */
ZENCORE_API SharedBuffer GetRangeBuffer() const;
private:
using TCbFieldIterator::TCbFieldIterator;
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* An array that can hold a reference to the memory that contains it.
*
* @see CbBuffer
*/
class CbArray : public CbBuffer<CbArrayView>, public CbBufferFactory<CbArray, CbArrayView>
{
public:
using CbBuffer::CbBuffer;
/** Create an iterator for the fields of this array. */
inline CbFieldIterator CreateIterator() const { return CbFieldIterator::MakeRangeView(CreateViewIterator(), GetOuterBuffer()); }
/** Access the array as an array field. */
inline CbField AsField() const& { return CbField(CbArrayView::AsFieldView(), *this); }
/** Access the array as an array field. */
inline CbField AsField() && { return CbField(CbArrayView::AsFieldView(), std::move(*this)); }
private:
friend inline CbFieldIterator begin(const CbArray& Array) { return Array.CreateIterator(); }
friend inline CbFieldIterator end(const CbArray&) { return CbFieldIterator(); }
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* An object that can hold a reference to the memory that contains it.
*
* @see CbBuffer
*/
class CbObject : public CbBuffer<CbObjectView>, public CbBufferFactory<CbObject, CbObjectView>
{
public:
using CbBuffer::CbBuffer;
/** Create an iterator for the fields of this object. */
inline CbFieldIterator CreateIterator() const { return CbFieldIterator::MakeRangeView(CreateViewIterator(), GetOuterBuffer()); }
/** Find a field by case-sensitive name comparison. */
inline CbField Find(std::string_view Name) const
{
if (CbFieldView Field = FindView(Name))
{
return CbField(Field, *this);
}
return CbField();
}
/** Find a field by case-insensitive name comparison. */
inline CbField FindIgnoreCase(std::string_view Name) const
{
if (CbFieldView Field = FindViewIgnoreCase(Name))
{
return CbField(Field, *this);
}
return CbField();
}
/** Find a field by case-sensitive name comparison. */
inline CbFieldView operator[](std::string_view Name) const { return Find(Name); }
/** Access the object as an object field. */
inline CbField AsField() const& { return CbField(CbObjectView::AsFieldView(), *this); }
/** Access the object as an object field. */
inline CbField AsField() && { return CbField(CbObjectView::AsFieldView(), std::move(*this)); }
private:
friend inline CbFieldIterator begin(const CbObject& Object) { return Object.CreateIterator(); }
friend inline CbFieldIterator end(const CbObject&) { return CbFieldIterator(); }
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
inline CbObject
CbField::AsObject() &
{
return IsObject() ? CbObject(AsObjectView(), *this) : CbObject();
}
inline CbObject
CbField::AsObject() &&
{
return IsObject() ? CbObject(AsObjectView(), std::move(*this)) : CbObject();
}
inline CbArray
CbField::AsArray() &
{
return IsArray() ? CbArray(AsArrayView(), *this) : CbArray();
}
inline CbArray
CbField::AsArray() &&
{
return IsArray() ? CbArray(AsArrayView(), std::move(*this)) : CbArray();
}
inline SharedBuffer
CbField::AsBinary(const SharedBuffer& Default) &
{
const MemoryView View = AsBinaryView();
return !HasError() ? SharedBuffer::MakeView(View, GetOuterBuffer()) : Default;
}
inline SharedBuffer
CbField::AsBinary(const SharedBuffer& Default) &&
{
const MemoryView View = AsBinaryView();
return !HasError() ? SharedBuffer::MakeView(View, std::move(*this).GetOuterBuffer()) : Default;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Load a compact binary field from an archive.
*
* The field may be an array or an object, which the caller can convert to by using AsArray or
* AsObject as appropriate. The buffer allocator is called to provide the buffer for the field
* to load into once its size has been determined.
*
* @param Ar Archive to read the field from. An error state is set on failure.
* @param Allocator Allocator for the buffer that the field is loaded into.
* @return A field with a reference to the allocated buffer, or a default field on failure.
*/
ZENCORE_API CbField LoadCompactBinary(BinaryReader& Ar, BufferAllocator Allocator);
ZENCORE_API CbObject LoadCompactBinaryObject(IoBuffer&& Payload);
ZENCORE_API CbObject LoadCompactBinaryObject(const IoBuffer& Payload);
ZENCORE_API CbObject LoadCompactBinaryObject(CompressedBuffer&& Payload);
ZENCORE_API CbObject LoadCompactBinaryObject(const CompressedBuffer& Payload);
/**
* Load a compact binary from JSON.
*/
ZENCORE_API CbFieldIterator LoadCompactBinaryFromJson(std::string_view Json, std::string& Error);
ZENCORE_API CbFieldIterator LoadCompactBinaryFromJson(std::string_view Json);
ZENCORE_API void SaveCompactBinary(BinaryWriter& Ar, const CbFieldView& Field);
ZENCORE_API void SaveCompactBinary(BinaryWriter& Ar, const CbArrayView& Array);
ZENCORE_API void SaveCompactBinary(BinaryWriter& Ar, const CbObjectView& Object);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Determine the size in bytes of the compact binary field at the start of the view.
*
* This may be called on an incomplete or invalid field, in which case the returned size is zero.
* A size can always be extracted from a valid field with no name if a view of at least the first
* 10 bytes is provided, regardless of field size. For fields with names, the size of view needed
* to calculate a size is at most 10 + MaxNameLen + MeasureVarUInt(MaxNameLen).
*
* This function can be used when streaming a field, for example, to determine the size of buffer
* to fill before attempting to construct a field from it.
*
* @param View A memory view that may contain the start of a field.
* @param Type HasFieldType means that View contains the type. Otherwise, use the given type.
*/
ZENCORE_API uint64_t MeasureCompactBinary(MemoryView View, CbFieldType Type = CbFieldType::HasFieldType);
/**
* Try to determine the type and size of the compact binary field at the start of the view.
*
* This may be called on an incomplete or invalid field, in which case it will return false, with
* OutSize being 0 for invalid fields, otherwise the minimum view size necessary to make progress
* in measuring the field on the next call to this function.
*
* @note A return of true from this function does not indicate that the entire field is valid.
*
* @param InView A memory view that may contain the start of a field.
* @param OutType The type (with flags) of the field. None is written until a value is available.
* @param OutSize The total field size for a return of true, 0 for invalid fields, or the size to
* make progress in measuring the field on the next call to this function.
* @param InType HasFieldType means that InView contains the type. Otherwise, use the given type.
* @return true if the size of the field was determined, otherwise false.
*/
ZENCORE_API bool TryMeasureCompactBinary(MemoryView InView,
CbFieldType& OutType,
uint64_t& OutSize,
CbFieldType InType = CbFieldType::HasFieldType);
inline CbFieldViewIterator
begin(CbFieldView& View)
{
if (View.IsArray())
{
return View.AsArrayView().CreateViewIterator();
}
else if (View.IsObject())
{
return View.AsObjectView().CreateViewIterator();
}
return CbFieldViewIterator();
}
inline CbFieldViewIterator
end(CbFieldView&)
{
return CbFieldViewIterator();
}
/**
* Serialize serialized compact binary blob to jaons. It must be 0 to n fields with including type for each field
*/
ZENCORE_API void CompactBinaryToJson(MemoryView Data, StringBuilderBase& InBuilder);
ZENCORE_API std::vector<CbFieldView> ReadCompactBinaryStream(MemoryView Data);
void uson_forcelink(); // internal
} // namespace zen
|