libpromeki 1.0.0-alpha
PROfessional MEdia toolKIt
 
Loading...
Searching...
No Matches
datatype.h
Go to the documentation of this file.
1
8#pragma once
9
10
11#include <promeki/config.h>
12#if PROMEKI_ENABLE_CORE
13#include <cstddef>
14#include <cstdint>
15#include <new>
16#include <type_traits>
17#include <typeindex>
18#include <typeinfo>
19#include <utility>
20#include <promeki/namespace.h>
21#include <promeki/string.h>
22#include <promeki/error.h>
23#include <promeki/result.h>
24#include <promeki/list.h>
25
26PROMEKI_NAMESPACE_BEGIN
27
28class DataStream;
29class JsonObject;
30
50enum DataTypeID : uint16_t {
51 DataTypeInvalid = 0x00,
52
53 // Primitives ---------------------------------------------
54 DataTypeInt8 = 0x01,
55 DataTypeUInt8 = 0x02,
56 DataTypeInt16 = 0x03,
57 DataTypeUInt16 = 0x04,
58 DataTypeInt32 = 0x05,
59 DataTypeUInt32 = 0x06,
60 DataTypeInt64 = 0x07,
61 DataTypeUInt64 = 0x08,
62 DataTypeFloat = 0x09,
63 DataTypeDouble = 0x0A,
64 DataTypeBool = 0x0B,
65 DataTypeString = 0x0C,
66 DataTypeBuffer = 0x0D,
67 DataTypeNoValue = 0x0E,
68
69 // Data objects -------------------------------------------
70 DataTypeUUID = 0x10,
71 DataTypeDateTime = 0x11,
72 DataTypeTimeStamp = 0x12,
73 DataTypeSize2D = 0x13,
74 DataTypeRational = 0x14,
75 DataTypeFrameRate = 0x15,
76 DataTypeTimecode = 0x16,
77 DataTypeColor = 0x17,
78 DataTypeColorModel = 0x18,
79 DataTypeMemSpace = 0x19,
80 DataTypePixelMemLayout = 0x1A,
81 DataTypePixelFormat = 0x1B,
82 DataTypeEnum = 0x1C,
83 DataTypeStringList = 0x1D,
84 DataTypeRect = 0x1E,
85 DataTypePoint = 0x1F,
86
87 // Containers --------------------------------------------
88 DataTypeList = 0x20,
89 DataTypeMap = 0x21,
90 DataTypeSet = 0x22,
91 DataTypeHashMap = 0x23,
92 DataTypeHashSet = 0x24,
93
94 // Shareable types ---------------------------------------
95 DataTypeJsonObject = 0x30,
96 DataTypeJsonArray = 0x31,
97 DataTypeXYZColor = 0x32,
98 DataTypeAudioDesc = 0x33,
99 DataTypeImageDesc = 0x34,
100 DataTypeMediaDesc = 0x35,
101 DataTypeUMID = 0x36,
102 DataTypeEnumList = 0x37,
103 DataTypeMediaTimeStamp = 0x38,
104 DataTypeMacAddress = 0x39,
105 DataTypeEUI64 = 0x3A,
106 DataTypeMediaPipelineStage = 0x3B,
107 DataTypeMediaPipelineRoute = 0x3C,
108 DataTypeMediaPipelineConfig = 0x3D,
109 DataTypeMediaPipelineStats = 0x3E,
110 DataTypeVideoFormat = 0x3F,
111
112 // HDR color metadata ------------------------------------
113 DataTypeMasteringDisplay = 0x40,
114 DataTypeContentLightLevel = 0x41,
115
116 // MediaIO introspection ---------------------------------
117 DataTypeMediaIODescription = 0x42,
118
119 // Frame timeline types ----------------------------------
120 DataTypeFrameNumber = 0x43,
121 DataTypeFrameCount = 0x44,
122 DataTypeMediaDuration = 0x45,
123 DataTypeUrl = 0x46,
124 DataTypeAudioFormat = 0x47,
125 DataTypeMediaPayload = 0x48,
126 DataTypeDuration = 0x49,
127 DataTypeSocketAddress = 0x4A,
128 DataTypeSdpSession = 0x4B,
129 DataTypeVideoCodec = 0x4C,
130 DataTypeAudioCodec = 0x4D,
131 DataTypeAudioChannelMap = 0x4E,
132 DataTypeAudioStreamDesc = 0x4F,
133 DataTypeWindowedStat = 0x50,
134 DataTypeWindowedStatsBundle = 0x51,
135 DataTypeAudioMarkerList = 0x52,
136 DataTypeVariantList = 0x53,
137 DataTypeVariantMap = 0x54,
138 DataTypeXmlDocument = 0x55,
139 DataTypeXmlElement = 0x56,
140 DataTypeSslContext = 0x57,
141 DataTypeAncFormat = 0x58,
142 DataTypeAncPacket = 0x59,
143 DataTypeAncDesc = 0x5A,
144 DataTypeCea708Cdp = 0x5B,
145 DataTypeSubtitle = 0x5C,
146 DataTypeSubtitleSpan = 0x5D,
147 DataTypeScc = 0x5E,
148 DataTypeCea608 = 0x5F,
149 DataTypeCea708Service = 0x60,
150 DataTypeCea708DtvccPacket = 0x61,
151 DataTypeHdrStaticMetadata = 0x62,
152 DataTypeHdrDynamic2094_40 = 0x63,
153
154 // Generic video signal carriers --------------------------
155 DataTypeVideoPortRef = 0x64,
156 DataTypeSdiSignalConfig = 0x65,
157 DataTypeHdmiSignalConfig = 0x66,
158 DataTypeVideoReferenceConfig = 0x67,
159 DataTypeSdiOutputFanoutConfig = 0x68,
160 DataTypeSdiVpid = 0x69,
161 DataTypeAncAtc = 0x6A,
162 DataTypeAncAfd = 0x6B,
163 DataTypeAncOp47Sdp = 0x6C,
164 DataTypeSt2020Audio = 0x6D,
165 DataTypeTimecodeUserbits = 0x6E,
166};
167
169inline constexpr DataTypeID DataTypeUserBegin = static_cast<DataTypeID>(0x4000);
171inline constexpr DataTypeID DataTypeUserEnd = static_cast<DataTypeID>(0xFFFF);
172
187void registerBuiltinDataTypes();
188
238class DataType {
239 public:
276 struct Ops {
277 void (*defaultConstruct)(void *p) = nullptr;
278 void (*copyConstruct)(void *dst, const void *src) = nullptr;
279 void (*moveConstruct)(void *dst, void *src) = nullptr;
280 void (*destroy)(void *p) = nullptr;
281 bool (*equal)(const void *a, const void *b) = nullptr;
282 String (*toString)(const void *p, Error *err) = nullptr;
283 bool (*fromString)(const String &s, void *out, Error *err) = nullptr;
284 int64_t (*toInt)(const void *p, Error *err) = nullptr;
285 bool (*fromInt)(int64_t v, void *out, Error *err) = nullptr;
286 double (*toFloat)(const void *p, Error *err) = nullptr;
287 bool (*fromFloat)(double v, void *out, Error *err) = nullptr;
288 JsonObject (*toJson)(const void *p, Error *err) = nullptr;
289 bool (*fromJson)(const JsonObject &j, void *out, Error *err) = nullptr;
290 void (*writeStream)(DataStream &s, const void *p) = nullptr;
291 void (*readStream)(DataStream &s, void *p) = nullptr;
292 };
293
301 struct Data {
302 DataTypeID id = DataTypeInvalid;
303 const char *name = "Invalid";
304 uint32_t version = 0;
305 size_t size = 0;
306 size_t align = 1;
307 std::type_index cppType = std::type_index(typeid(void));
308 Ops ops;
309 };
310
312 DataType() = default;
313
315 explicit DataType(const Data *d) : _data(d) { return; }
316
327 DataType(DataTypeID id);
328
330 bool isValid() const { return _data != nullptr; }
331
333 DataTypeID id() const { return _data != nullptr ? _data->id : DataTypeInvalid; }
334
336 const char *name() const { return _data != nullptr ? _data->name : "Invalid"; }
337
339 uint32_t version() const { return _data != nullptr ? _data->version : 0; }
340
342 size_t size() const { return _data != nullptr ? _data->size : 0; }
343
345 size_t alignment() const { return _data != nullptr ? _data->align : 1; }
346
348 std::type_index cppType() const {
349 return _data != nullptr ? _data->cppType : std::type_index(typeid(void));
350 }
351
359 const Ops &ops() const;
360
362 const Data *data() const { return _data; }
363
365 bool operator==(const DataType &o) const { return _data == o._data; }
366 bool operator!=(const DataType &o) const { return _data != o._data; }
367
390 static DataType registerType(DataTypeID id, const char *name, uint32_t version,
391 std::type_index ti, size_t size, size_t align, Ops ops);
392
394 static List<DataTypeID> registeredIds();
395
397 static DataType byId(DataTypeID id);
398
400 static DataType byName(const char *name);
401
419 template <typename T> static DataType of();
420
422 static DataType byCppType(std::type_index ti);
423
424 private:
425 const Data *_data = nullptr;
426};
427
429namespace Detail {
430
431template <typename T>
432concept HasEqualityOp = requires(const T &a, const T &b) {
433 { a == b } -> std::convertible_to<bool>;
434};
435
436template <typename T>
437concept HasMemberToString = requires(const T &t) {
438 { t.toString() } -> std::convertible_to<String>;
439};
440
447template <typename T>
448concept HasResultFromString = requires(const String &s) {
449 { T::fromString(s) } -> std::convertible_to<Result<T>>;
450};
451
458template <typename T>
459concept HasMemberValueInt = requires(const T &t) {
460 { t.value() } -> std::integral;
461};
462
469template <typename T>
470concept HasMemberIdInt = (!HasMemberValueInt<T>) && requires(const T &t) {
471 requires std::integral<decltype(t.id())> || std::is_enum_v<decltype(t.id())>;
472};
473
482template <typename T>
483concept HasMemberToDouble = requires(const T &t) {
484 { t.toDouble() } -> std::convertible_to<double>;
485};
486
494template <typename T>
495concept HasResultFromDouble = requires(double d) {
496 { T::fromDouble(d) } -> std::convertible_to<Result<T>>;
497};
498
509template <typename T>
510concept HasMemberToJson = requires(const T &t) {
511 t.toJson();
512};
513
522template <typename T>
523concept HasResultFromJson = requires(JsonObject *jp) {
524 T::fromJson(*jp);
525};
526
534template <typename T>
535concept HasIdCtor = requires { typename T::ID; } && std::is_constructible_v<T, typename T::ID>;
536
544template <typename T>
545concept HasInt64Ctor = std::is_constructible_v<T, int64_t> && !HasIdCtor<T>;
546
551template <typename T>
552concept HasPromekiDataType = requires {
553 { T::promekiDataType::id } -> std::convertible_to<DataTypeID>;
554 { T::promekiDataType::name } -> std::convertible_to<const char *>;
555 { T::promekiDataType::version } -> std::convertible_to<uint32_t>;
556};
557
558// HasMemberWriteToStream / HasMemberReadFromStream live in
559// @c datastream.h's bottom half — they reference @c DataStream which
560// must be complete at concept instantiation time, and putting them
561// here would force every consumer of @c datatype.h to also pull in
562// @c datastream.h.
563
578template <typename T> struct HasFreeDataStreamWrite : std::false_type {};
579
581template <typename T> struct HasFreeDataStreamRead : std::false_type {};
582
590template <typename T, uint32_t MaxV, uint32_t CurV = MaxV>
591struct VersionedReader {
592 static Result<T> read(DataStream &s, uint32_t version) {
593 if (version == CurV) return T::template readFromStream<CurV>(s);
594 if constexpr (CurV > 1) return VersionedReader<T, MaxV, CurV - 1>::read(s, version);
595 else return makeError<T>(Error::CorruptData);
596 }
597};
598
599// Primary templates for the "T has an exact-match operator<< / >>
600// member on DataStream" detectors. Partial specializations live in
601// @c datastream.h once the @ref DataStream class is complete — they
602// dereference member function pointers on @ref DataStream and so
603// need its full declaration. Forward-declaring the primary here
604// (without pulling in @c datastream.h) keeps the cycle broken.
605template <typename T, typename = void>
606struct ExactDataStreamWrite : std::false_type {};
607
608template <typename T, typename = void>
609struct ExactDataStreamRead : std::false_type {};
610
611template <typename T>
612inline constexpr bool ExactDataStreamWriteV = ExactDataStreamWrite<T>::value;
613
614template <typename T>
615inline constexpr bool ExactDataStreamReadV = ExactDataStreamRead<T>::value;
616
618template <typename T>
619inline constexpr bool HasDataStreamWriteV = ExactDataStreamWriteV<T> || HasFreeDataStreamWrite<T>::value;
620
622template <typename T>
623inline constexpr bool HasDataStreamReadV = ExactDataStreamReadV<T> || HasFreeDataStreamRead<T>::value;
624
629template <typename T>
630DataType::Ops makeDefaultOps();
631
644template <typename T> void registerAutoConverters(const DataType &dt);
645
646} // namespace Detail
647
667template <typename T> DataType registerDataType();
668
682template <typename T> DataType registerDataType(DataTypeID id, const char *name, uint32_t version = 1);
683
692template <typename T> DataType DataType::of() {
693 registerBuiltinDataTypes();
694 if constexpr (Detail::HasPromekiDataType<T>) {
695 static const DataType once = registerDataType<T>();
696 return once;
697 } else {
698 return DataType::byCppType(std::type_index(typeid(T)));
699 }
700}
701
702template <typename T> DataType registerDataType() {
703 static_assert(Detail::HasPromekiDataType<T>,
704 "registerDataType<T>() with no arguments requires PROMEKI_DATATYPE on T; "
705 "use registerDataType<T>(id, name, version) for primitives and template "
706 "instantiations that cannot host the macro.");
707 DataType existing = DataType::byCppType(std::type_index(typeid(T)));
708 if (existing.isValid()) return existing;
709 DataType dt = DataType::registerType(
710 T::promekiDataType::id, T::promekiDataType::name, T::promekiDataType::version,
711 std::type_index(typeid(T)), sizeof(T), alignof(T),
712 Detail::makeDefaultOps<T>());
713 if (dt.isValid()) Detail::registerAutoConverters<T>(dt);
714 return dt;
715}
716
717template <typename T> DataType registerDataType(DataTypeID id, const char *name, uint32_t version) {
718 DataType existing = DataType::byCppType(std::type_index(typeid(T)));
719 if (existing.isValid()) return existing;
720 DataType dt = DataType::registerType(
721 id, name, version, std::type_index(typeid(T)), sizeof(T), alignof(T),
722 Detail::makeDefaultOps<T>());
723 if (dt.isValid()) Detail::registerAutoConverters<T>(dt);
724 return dt;
725}
726
727PROMEKI_NAMESPACE_END
728
763#define PROMEKI_DATATYPE(TYPE, ID, VERSION) \
764 struct promekiDataType { \
765 using Self = TYPE; \
766 static constexpr ::promeki::DataTypeID id = static_cast<::promeki::DataTypeID>(ID); \
767 static constexpr const char *name = #TYPE; \
768 static constexpr uint32_t version = (VERSION); \
769 static ::promeki::Result<Self> dispatchRead(::promeki::DataStream &s, uint32_t v) { \
770 return ::promeki::Detail::VersionedReader<Self, (VERSION)>::read(s, v); \
771 } \
772 };
773
774// Concept detection of the new member API needs DataStream complete,
775// so it lives in @c datastream.h's bottom half (where it can rely on
776// the class being fully declared without pulling @c datastream.h
777// transitively from here — which would form an unmanageable cycle
778// when @ref PROMEKI_DATATYPE is used inside types that get included
779// from @c datastream.h itself).
780//
781// Users that need the generic @c operator<< / @c operator>>
782// templates, or @c makeDefaultOps for explicit registration, include
783// @c <promeki/datastream.h>.
784
785#endif // PROMEKI_ENABLE_CORE