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 DataTypeIpv4Address = 0x6F,
167 DataTypeIpv6Address = 0x70,
168 DataTypeNetworkAddress = 0x71,
169 DataTypePixelAspect = 0x72,
170 DataTypeTranscript = 0x73,
171 DataTypeTranscriptWord = 0x74,
172 DataTypeMdnsServiceType = 0x75,
173 DataTypeMdnsTxtRecord = 0x76,
174 DataTypeMdnsServiceInstance = 0x77,
175};
176
178inline constexpr DataTypeID DataTypeUserBegin = static_cast<DataTypeID>(0x4000);
180inline constexpr DataTypeID DataTypeUserEnd = static_cast<DataTypeID>(0xFFFF);
181
196void registerBuiltinDataTypes();
197
247class DataType {
248 public:
285 struct Ops {
286 void (*defaultConstruct)(void *p) = nullptr;
287 void (*copyConstruct)(void *dst, const void *src) = nullptr;
288 void (*moveConstruct)(void *dst, void *src) = nullptr;
289 void (*destroy)(void *p) = nullptr;
290 bool (*equal)(const void *a, const void *b) = nullptr;
291 String (*toString)(const void *p, Error *err) = nullptr;
292 bool (*fromString)(const String &s, void *out, Error *err) = nullptr;
293 int64_t (*toInt)(const void *p, Error *err) = nullptr;
294 bool (*fromInt)(int64_t v, void *out, Error *err) = nullptr;
295 double (*toFloat)(const void *p, Error *err) = nullptr;
296 bool (*fromFloat)(double v, void *out, Error *err) = nullptr;
297 JsonObject (*toJson)(const void *p, Error *err) = nullptr;
298 bool (*fromJson)(const JsonObject &j, void *out, Error *err) = nullptr;
299 void (*writeStream)(DataStream &s, const void *p) = nullptr;
300 void (*readStream)(DataStream &s, void *p) = nullptr;
301 };
302
310 struct Data {
311 DataTypeID id = DataTypeInvalid;
312 const char *name = "Invalid";
313 uint32_t version = 0;
314 size_t size = 0;
315 size_t align = 1;
316 std::type_index cppType = std::type_index(typeid(void));
317 Ops ops;
318 };
319
321 DataType() = default;
322
324 explicit DataType(const Data *d) : _data(d) { return; }
325
336 DataType(DataTypeID id);
337
339 bool isValid() const { return _data != nullptr; }
340
342 DataTypeID id() const { return _data != nullptr ? _data->id : DataTypeInvalid; }
343
345 const char *name() const { return _data != nullptr ? _data->name : "Invalid"; }
346
348 uint32_t version() const { return _data != nullptr ? _data->version : 0; }
349
351 size_t size() const { return _data != nullptr ? _data->size : 0; }
352
354 size_t alignment() const { return _data != nullptr ? _data->align : 1; }
355
357 std::type_index cppType() const {
358 return _data != nullptr ? _data->cppType : std::type_index(typeid(void));
359 }
360
368 const Ops &ops() const;
369
371 const Data *data() const { return _data; }
372
374 bool operator==(const DataType &o) const { return _data == o._data; }
375 bool operator!=(const DataType &o) const { return _data != o._data; }
376
399 static DataType registerType(DataTypeID id, const char *name, uint32_t version,
400 std::type_index ti, size_t size, size_t align, Ops ops);
401
403 static List<DataTypeID> registeredIds();
404
406 static DataType byId(DataTypeID id);
407
409 static DataType byName(const char *name);
410
428 template <typename T> static DataType of();
429
431 static DataType byCppType(std::type_index ti);
432
433 private:
434 const Data *_data = nullptr;
435};
436
438namespace Detail {
439
440template <typename T>
441concept HasEqualityOp = requires(const T &a, const T &b) {
442 { a == b } -> std::convertible_to<bool>;
443};
444
445template <typename T>
446concept HasMemberToString = requires(const T &t) {
447 { t.toString() } -> std::convertible_to<String>;
448};
449
456template <typename T>
457concept HasResultFromString = requires(const String &s) {
458 { T::fromString(s) } -> std::convertible_to<Result<T>>;
459};
460
467template <typename T>
468concept HasMemberValueInt = requires(const T &t) {
469 { t.value() } -> std::integral;
470};
471
478template <typename T>
479concept HasMemberIdInt = (!HasMemberValueInt<T>) && requires(const T &t) {
480 requires std::integral<decltype(t.id())> || std::is_enum_v<decltype(t.id())>;
481};
482
491template <typename T>
492concept HasMemberToDouble = requires(const T &t) {
493 { t.toDouble() } -> std::convertible_to<double>;
494};
495
503template <typename T>
504concept HasResultFromDouble = requires(double d) {
505 { T::fromDouble(d) } -> std::convertible_to<Result<T>>;
506};
507
518template <typename T>
519concept HasMemberToJson = requires(const T &t) {
520 t.toJson();
521};
522
531template <typename T>
532concept HasResultFromJson = requires(JsonObject *jp) {
533 T::fromJson(*jp);
534};
535
543template <typename T>
544concept HasIdCtor = requires { typename T::ID; } && std::is_constructible_v<T, typename T::ID>;
545
553template <typename T>
554concept HasInt64Ctor = std::is_constructible_v<T, int64_t> && !HasIdCtor<T>;
555
560template <typename T>
561concept HasPromekiDataType = requires {
562 { T::promekiDataType::id } -> std::convertible_to<DataTypeID>;
563 { T::promekiDataType::name } -> std::convertible_to<const char *>;
564 { T::promekiDataType::version } -> std::convertible_to<uint32_t>;
565};
566
567// HasMemberWriteToStream / HasMemberReadFromStream live in
568// @c datastream.h's bottom half — they reference @c DataStream which
569// must be complete at concept instantiation time, and putting them
570// here would force every consumer of @c datatype.h to also pull in
571// @c datastream.h.
572
587template <typename T> struct HasFreeDataStreamWrite : std::false_type {};
588
590template <typename T> struct HasFreeDataStreamRead : std::false_type {};
591
599template <typename T, uint32_t MaxV, uint32_t CurV = MaxV>
600struct VersionedReader {
601 static Result<T> read(DataStream &s, uint32_t version) {
602 if (version == CurV) return T::template readFromStream<CurV>(s);
603 if constexpr (CurV > 1) return VersionedReader<T, MaxV, CurV - 1>::read(s, version);
604 else return makeError<T>(Error::CorruptData);
605 }
606};
607
608// Primary templates for the "T has an exact-match operator<< / >>
609// member on DataStream" detectors. Partial specializations live in
610// @c datastream.h once the @ref DataStream class is complete — they
611// dereference member function pointers on @ref DataStream and so
612// need its full declaration. Forward-declaring the primary here
613// (without pulling in @c datastream.h) keeps the cycle broken.
614template <typename T, typename = void>
615struct ExactDataStreamWrite : std::false_type {};
616
617template <typename T, typename = void>
618struct ExactDataStreamRead : std::false_type {};
619
620template <typename T>
621inline constexpr bool ExactDataStreamWriteV = ExactDataStreamWrite<T>::value;
622
623template <typename T>
624inline constexpr bool ExactDataStreamReadV = ExactDataStreamRead<T>::value;
625
627template <typename T>
628inline constexpr bool HasDataStreamWriteV = ExactDataStreamWriteV<T> || HasFreeDataStreamWrite<T>::value;
629
631template <typename T>
632inline constexpr bool HasDataStreamReadV = ExactDataStreamReadV<T> || HasFreeDataStreamRead<T>::value;
633
638template <typename T>
639DataType::Ops makeDefaultOps();
640
653template <typename T> void registerAutoConverters(const DataType &dt);
654
655} // namespace Detail
656
676template <typename T> DataType registerDataType();
677
691template <typename T> DataType registerDataType(DataTypeID id, const char *name, uint32_t version = 1);
692
701template <typename T> DataType DataType::of() {
702 registerBuiltinDataTypes();
703 if constexpr (Detail::HasPromekiDataType<T>) {
704 static const DataType once = registerDataType<T>();
705 return once;
706 } else {
707 return DataType::byCppType(std::type_index(typeid(T)));
708 }
709}
710
711template <typename T> DataType registerDataType() {
712 static_assert(Detail::HasPromekiDataType<T>,
713 "registerDataType<T>() with no arguments requires PROMEKI_DATATYPE on T; "
714 "use registerDataType<T>(id, name, version) for primitives and template "
715 "instantiations that cannot host the macro.");
716 DataType existing = DataType::byCppType(std::type_index(typeid(T)));
717 if (existing.isValid()) return existing;
718 DataType dt = DataType::registerType(
719 T::promekiDataType::id, T::promekiDataType::name, T::promekiDataType::version,
720 std::type_index(typeid(T)), sizeof(T), alignof(T),
721 Detail::makeDefaultOps<T>());
722 if (dt.isValid()) Detail::registerAutoConverters<T>(dt);
723 return dt;
724}
725
726template <typename T> DataType registerDataType(DataTypeID id, const char *name, uint32_t version) {
727 DataType existing = DataType::byCppType(std::type_index(typeid(T)));
728 if (existing.isValid()) return existing;
729 DataType dt = DataType::registerType(
730 id, name, version, std::type_index(typeid(T)), sizeof(T), alignof(T),
731 Detail::makeDefaultOps<T>());
732 if (dt.isValid()) Detail::registerAutoConverters<T>(dt);
733 return dt;
734}
735
736PROMEKI_NAMESPACE_END
737
772#define PROMEKI_DATATYPE(TYPE, ID, VERSION) \
773 struct promekiDataType { \
774 using Self = TYPE; \
775 static constexpr ::promeki::DataTypeID id = static_cast<::promeki::DataTypeID>(ID); \
776 static constexpr const char *name = #TYPE; \
777 static constexpr uint32_t version = (VERSION); \
778 static ::promeki::Result<Self> dispatchRead(::promeki::DataStream &s, uint32_t v) { \
779 return ::promeki::Detail::VersionedReader<Self, (VERSION)>::read(s, v); \
780 } \
781 };
782
783// Concept detection of the new member API needs DataStream complete,
784// so it lives in @c datastream.h's bottom half (where it can rely on
785// the class being fully declared without pulling @c datastream.h
786// transitively from here — which would form an unmanageable cycle
787// when @ref PROMEKI_DATATYPE is used inside types that get included
788// from @c datastream.h itself).
789//
790// Users that need the generic @c operator<< / @c operator>>
791// templates, or @c makeDefaultOps for explicit registration, include
792// @c <promeki/datastream.h>.
793
794#endif // PROMEKI_ENABLE_CORE