Advertisement
Guest User

Untitled

a guest
Mar 23rd, 2018
49
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 82.14 KB | None | 0 0
  1. #include <algorithm>
  2. #include <cassert>
  3. #include <cctype>
  4. #include <cstring>
  5. #include <optional>
  6. #include <string>
  7. #include <string_view>
  8. #include <stdexcept>
  9. #include <type_traits>
  10. #include <vector>
  11.  
  12. #include "adsData.h"
  13.  
  14. using namespace std::literals::string_literals;
  15. using namespace std::literals::string_view_literals;
  16.  
  17. #define ASSERT_THROW(cond) ASSERT_THROW_(cond)
  18. #ifdef ADS_DATA_DEBUG_THROW
  19. #define ASSERT_THROW_(cond) \
  20. { \
  21.     assert(cond); \
  22.     if (!(cond)) \
  23.         throw std::runtime_error(#cond); \
  24. }
  25. #else
  26. #define ASSERT_THROW_(cond) \
  27.     if (!(cond)) \
  28.         throw std::runtime_error(#cond);
  29. #endif
  30.  
  31. adsVarData::adsVarData(const adsData::subSymbolInfo* info, sizeType group, sizeType offset, sizeType index = -1)
  32.     : info_{ info }, group_{ group }, offset_{ offset }, index_{ index }
  33. {}
  34.  
  35. namespace {
  36.     std::string operator+(std::string lhs, std::string_view rhs) {
  37.         return std::move(lhs.append(rhs.data(), rhs.size()));
  38.     }
  39.     std::string operator+(std::string_view lhs, std::string_view rhs) {
  40.         std::string result;
  41.         result.reserve(lhs.size() + rhs.size());
  42.         result.assign(lhs.data(), lhs.size()).append(rhs);
  43.         return result;
  44.     }
  45.     using sizeType = adsData::sizeType;
  46.  
  47.     inline bool icmp_less(std::string_view lhs, std::string_view rhs) {
  48.         return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(),
  49.             [](auto x, auto y) { return
  50.             x == '\0' ? y != '\0' : y == '\0' ? false
  51.             : x == '.' ? y != '.' : y == '.' ? false
  52.             : std::tolower(x) < std::tolower(y); });
  53.     }
  54.     inline bool icmp_equal(std::string_view lhs, std::string_view rhs) {
  55.         return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(),
  56.             [](auto x, auto y) { return std::tolower(x) == std::tolower(y); });
  57.     }
  58.  
  59.     template <typename T, std::enable_if_t<std::is_trivial_v<T>, int> = 0>
  60.     T readRaw(const char* p) noexcept {
  61.         T res;
  62.         std::memcpy(&res, p, sizeof res);
  63.         return res;
  64.     }
  65.  
  66.     struct by_size {};
  67.     struct by_count {};
  68.  
  69.     namespace detail {
  70.  
  71.         template <typename T>
  72.         struct properties_of;
  73.        
  74.         template <typename T>
  75.         struct property_ids {
  76.             using id_type = T;
  77.         };
  78.  
  79.         template <typename T>
  80.         class ref;
  81.  
  82.         using ref_prop = int;
  83.         constexpr ref_prop entryLen_ = -1;
  84.  
  85.         template <std::size_t align>
  86.         constexpr std::size_t do_align(std::size_t offset) {
  87.             static_assert(align != 0 && (align & (align - 1)) == 0, "power of 2");
  88.             return (offset + align - 1) & ~(align - 1);
  89.         }
  90.  
  91.         template <int Id, typename Accessor>
  92.         struct ref_property {
  93.             static constexpr int id = Id;
  94.             using accessor = Accessor;
  95.         };
  96.  
  97.         // Accessor models:
  98.         // with p = pointer to data of structed_type
  99.         // with q = pointer to data of containing structured_type
  100.         // entryLimit = maximum available memory starting at p
  101.         // all models:              member function   check(p, q, entryLimit)
  102.         // model value_accessor:    member function   get(p, q): see accessor description
  103.         //                          checks: nothing unless specified otherwise
  104.         // model memory_accessor:   member functions  offset(p, q):      offset rel. to p of memory location
  105.         //                                            size(p, q):        size of memory location
  106.         //                                            next_offset(p, q): offset(p, q) + size(p, q)
  107.         //                          checks: memory location within bounds of entryLimit
  108.         // model predicate: model of value_accessor of bool type
  109.  
  110.         struct unchecked {
  111.             static constexpr void check(const char*, const char*, std::size_t) noexcept {}
  112.         };
  113.  
  114.         template <typename T>
  115.         struct memory_check {
  116.             static void check(const char* p, const char* q, std::size_t entryLimit) {
  117.                 auto off = T::offset(p, q); (void)off;
  118.                 auto size = T::size(p, q); (void)size;
  119.                 ASSERT_THROW(T::offset(p, q) <= entryLimit && entryLimit - T::offset(p, q) >= T::size(p, q));
  120.             }
  121.         };
  122.  
  123.         // simple property accessors
  124.  
  125.         // fixed_size
  126.         // model of: value_accessor
  127.         // returns:  size of T
  128.         template <typename T>
  129.         struct fixed_size : unchecked {
  130.             static constexpr std::size_t get        (const char*, const char*) {
  131.                 return sizeof(T);
  132.             }
  133.         };
  134.  
  135.         template <typename T>
  136.         struct is_fixed_size : std::false_type {};
  137.         template <typename T>
  138.         struct is_fixed_size<fixed_size<T>> : std::true_type {};
  139.         template <typename T>
  140.         constexpr bool is_fixed_size_v = is_fixed_size<T>::value;
  141.  
  142.         // element
  143.         // model of: value_accessor, memory_accessor
  144.         // object of type T at fixed displacement derived from structure member, see ELEMENT* macros
  145.         // returns:  stored value
  146.         template <typename T, std::size_t disp, typename U = T>
  147.         struct element : memory_check<element<T, disp, U>> {
  148.             static           U           get        (const char* p, const char*)   noexcept {
  149.                 return static_cast<U>(readRaw<T>(p + disp));
  150.             }
  151.             static constexpr std::size_t offset     (const char*,   const char*)   noexcept {
  152.                 return disp;
  153.             }
  154.             static constexpr std::size_t size       (const char*,   const char*)   noexcept {
  155.                 return sizeof(T);
  156.             }
  157.             static constexpr std::size_t next_offset(const char* p, const char* q) noexcept {
  158.                 return offset(p, q) + size(p, q);
  159.             }
  160.         };
  161.  
  162.         // element_checked
  163.         // derived from: element
  164.         // checks: stored value equals any of expected values
  165.         template <typename T, std::size_t disp, T... expected>
  166.         struct element_checked : element<T, disp> {
  167.             static void check(const char* p, const char* q, std::size_t entryLimit) {
  168.                 element<T, disp>::check(p, q, entryLimit);
  169.                 ASSERT_THROW((... || (get(p, q) == expected)));
  170.             }
  171.         };
  172. #define ELEMENT(CLASS, MEMBER)            element<        std::decay_t<decltype(std::declval<CLASS&>().*&CLASS::MEMBER)>, offsetof(CLASS, MEMBER)>
  173. #define ELEMENT_TYPE(CLASS, MEMBER, TYPE) element<        std::decay_t<decltype(std::declval<CLASS&>().*&CLASS::MEMBER)>, offsetof(CLASS, MEMBER), TYPE>
  174. #define ELEMENT_EXP(CLASS, MEMBER, ...)   element_checked<std::decay_t<decltype(std::declval<CLASS&>().*&CLASS::MEMBER)>, offsetof(CLASS, MEMBER), __VA_ARGS__>
  175.  
  176.         // compound property accessors
  177.         // common template parameters:
  178.         // T - type indexing current structected_type
  179.         // before - id of preceding property which must model memory_accessor
  180.         // align - alignment of memory location
  181.  
  182.         // next_data
  183.         // model of: value_accessor
  184.         // returns: aligned offset following preceding property
  185.         template <typename T, ref_prop before, std::size_t align = 1>
  186.         struct next_data : unchecked {
  187.             static constexpr std::size_t get        (const char* p, const char* q) noexcept {
  188.                 return do_align<align>(properties_of<T>::template next_offset<before>(p, q));
  189.             }
  190.         };
  191.  
  192.         // next_data_c
  193.         // derived from: next_data
  194.         // checks: value equals or is less than value of referenced property which must model value_accessor
  195.         template <typename T, ref_prop before, ref_prop expected, std::size_t align = 1>
  196.         struct next_data_c : next_data<T, before, align> {
  197.             static void check(const char* p, const char* q, std::size_t) {
  198.                 ASSERT_THROW((next_data<T, before, align>::get(p, q) <= properties_of<T>::template get<expected>(p, q)));
  199.             }
  200.         };
  201.  
  202.         // next_element
  203.         // model of: value_accessor, memory_accessor
  204.         // object of type U following preceding property
  205.         // returns: stored value
  206.         // checks: stored value equals one of the expected values (if any)
  207.         template <typename T, ref_prop before, typename U, std::size_t align = 1, U... expected>
  208.         struct next_element : memory_check<next_element<T, before, U, align, expected...>> {
  209.             static           U           get        (const char* p, const char* q) noexcept {
  210.                 return readRaw<U>(p + offset(p, q));
  211.             }
  212.             static constexpr std::size_t offset     (const char* p, const char* q) noexcept {
  213.                 return do_align<align>(properties_of<T>::template next_offset<before>(p, q));
  214.             }
  215.             static constexpr std::size_t size       (const char*,   const char*)   noexcept {
  216.                 return sizeof(U);
  217.             }
  218.             static constexpr std::size_t next_offset(const char* p, const char* q) noexcept {
  219.                 return offset(p, q) + size(p, q);
  220.             }
  221.  
  222.             static void check(const char* p, const char* q, std::size_t entryLimit) {
  223.                 memory_check<next_element<T, before, U, align, expected...>>::check(p, q, entryLimit);
  224.                 if constexpr (sizeof...(expected) != 0)
  225.                     ASSERT_THROW((... || (get(p, q) == expected)));
  226.             }
  227.         };
  228.  
  229.         template <typename T, T... expected>
  230.         bool val_check(T v) noexcept {
  231.             return (... || (v == expected));
  232.         }
  233.         // var_data
  234.         // model of: value_accessor, memory_accessor
  235.         // string of Element with offset of data's value and size of length
  236.         // returns: string_view
  237.         template <typename T, ref_prop data, ref_prop length, typename Element = char>
  238.         struct var_data : memory_check<var_data<T, data, length>> {
  239.             static constexpr auto        get        (const char* p, const char* q) noexcept {
  240.                 return std::basic_string_view<Element>{ reinterpret_cast<const Element*>(p + offset(p, q)), size(p, q) };
  241.             }
  242.             static constexpr std::size_t offset     (const char* p, const char* q) noexcept {
  243.                 return properties_of<T>::template get<data>(p, q);
  244.             }
  245.             static constexpr std::size_t size       (const char* p, const char* q) noexcept {
  246.                 return properties_of<T>::template get<length>(p, q);
  247.             }
  248.             static constexpr std::size_t next_offset(const char* p, const char* q) noexcept {
  249.                 return offset(p, q) + size(p, q);
  250.             }
  251.         };
  252.  
  253.         // string
  254.         // derived from: var_data
  255.         // checks: no embedded '\0' in character string
  256.         template <typename T, ref_prop data, ref_prop length>
  257.         struct string : var_data<T, data, length> {
  258.             static void check(const char* p, const char* q, std::size_t entryLimit) {
  259.                 var_data<T, data, length>::check(p, q, entryLimit);
  260.                 ASSERT_THROW((var_data<T, data, length>::get(p, q).find('\0') == std::string_view::npos)); // no embedded \0
  261.             }
  262.         };
  263.  
  264.         // flag
  265.         // model of: predicate
  266.         // returns: bool: (apply mask to value of referenced property) == compare
  267.         template <typename T, ref_prop ref, unsigned long long mask, unsigned long long compare = mask>
  268.         struct flag : unchecked {
  269.             static constexpr bool        get        (const char* p, const char* q) noexcept {
  270.                 return (properties_of<T>::template get<ref>(p, q) & mask) == compare;
  271.             }
  272.         };
  273.  
  274.         // and_
  275.         // model of: predicate
  276.         // returns: all reference predicates are true
  277.         template <typename T, ref_prop... refs>
  278.         struct and_ : unchecked {
  279.             static constexpr bool        get(const char* p, const char* q) noexcept {
  280.                 return (... && properties_of<T>::template get<ref>(p, q));
  281.             }
  282.         };
  283.  
  284.         // optional
  285.         // model of: value_accessor, memory_accessor
  286.         // property accessed by Accessor (of model memory_accessor) which existence is predicated by flag
  287.         // returns: std::optional<...> with value of Accessor
  288.         template <typename T, ref_prop flag, typename Access>
  289.         struct optional : memory_check<optional<T, flag, Access>> {
  290.             static constexpr auto        get        (const char* p, const char* q) noexcept {
  291.                 using return_type = std::optional<decltype(value(p, q))>;
  292.                 return exists(p, q) ? return_type{ value(p, q) } : return_type{};
  293.             }
  294.             static constexpr std::size_t offset     (const char* p, const char* q) noexcept {
  295.                 return Access::offset(p, q);
  296.             }
  297.             static constexpr std::size_t size       (const char* p, const char* q) noexcept {
  298.                 return exists(p, q) ? Access::size(p, q) : 0;
  299.             }
  300.             static constexpr std::size_t next_offset(const char* p, const char* q) noexcept {
  301.                 return offset(p, q) + size(p, q);
  302.             }
  303.  
  304.             static void check(const char* p, const char* q, std::size_t entryLimit) {
  305.                 memory_check<optional>::check(p, q, entryLimit);
  306.                 if (exists(p, q))
  307.                     Access::check(p, q, entryLimit);
  308.             }
  309.  
  310.             static constexpr bool exists(const char* p, const char* q) noexcept {
  311.                 return properties_of<T>::template get<flag>(p, q);
  312.             };
  313.             static constexpr auto value (const char* p, const char* q) noexcept {
  314.                 return Access::get(p, q);
  315.             }
  316.         };
  317.  
  318.         // optional_def
  319.         // derived from: optional
  320.         // returns: predicated on flag value of Accessor or default_value
  321.         template <typename T, ref_prop flag, typename Access, decltype(Access::get(nullptr, nullptr)) default_value>
  322.         struct optional_def : optional<T, flag, Access> {
  323.             static constexpr auto get               (const char* p, const char* q) noexcept {
  324.                 return optional<T, flag, Access>::exists(p, q) ? optional<T, flag, Access>::value(p, q) : default_value;
  325.             }
  326.         };
  327.        
  328.         // array
  329.         // model of: value_accessor, memory_accessor
  330.         // sequence of #num structured types indexed by Element following preceding property
  331.         // returns: ref to first element
  332.         // check: all contained elements
  333.         template <typename T, ref_prop before, ref_prop num, typename Element, std::size_t align = 1>
  334.         struct array {
  335.             static constexpr auto get               (const char* p, const char* q) noexcept {
  336.                 return ref<Element>(p + offset(p, q), by_count{}, elements(p, q), p);
  337.             }
  338.             static constexpr std::size_t offset     (const char* p, const char* q) noexcept {
  339.                 return do_align<align>(properties_of<T>::template next_offset<before>(p, q));
  340.             }
  341.             static constexpr std::size_t size       (const char* p, const char* q) noexcept {
  342.                 if constexpr (is_fixed_size_v<typename properties_of<Element>::template accessor<entryLen_>>) {
  343.                     return elements(p, q) * properties_of<Element>::template get<entryLen_>(p + offset(p, q), p);
  344.                 } else {
  345.                     ref<Element> elem = get(p, q);
  346.                     while (elem)
  347.                         ++elem;
  348.                     return elem.data() - p - offset(p, q);
  349.                 }
  350.             }
  351.             static constexpr std::size_t next_offset(const char* p, const char* q) noexcept {
  352.                 return offset(p, q) + size(p, q);
  353.             }
  354.  
  355.             static void check(const char* p, const char* q, std::size_t entryLimit) {
  356.                 ASSERT_THROW(offset(p, q) <= entryLimit);
  357.                 ref<Element> elem(p + offset(p, q), by_size{}, entryLimit - offset(p, q), p);
  358.                 for (auto n = elements(p, q); n--; ++elem) {
  359.                     elem.do_check();
  360.                 }
  361.             }
  362.  
  363.             static constexpr std::size_t elements   (const char* p, const char* q) noexcept {
  364.                 return properties_of<T>::template get<num>(p, q);
  365.             }
  366.         };
  367.  
  368.         // parent_prop
  369.         // model of: value_accessor
  370.         // returns: value of referenced property of containing structured type indexed by P
  371.         template <typename T, typename P, ref_prop prop>
  372.         struct parent_prop : unchecked {
  373.             constexpr static auto        get(const char*, const char* q) noexcept {
  374.                 return properties_of<P>::template get<prop>(q, nullptr);
  375.             }
  376.         };
  377.  
  378.         // conditional
  379.         // model of: value_accessor
  380.         // returns: select value of referenced properties depending on predicate
  381.         template <typename T, ref_prop flag, ref_prop yes_prop, ref_prop no_prop>
  382.         struct conditional : unchecked {
  383.             constexpr static auto        get(const char* p, const char* q) noexcept {
  384.                 return properties_of<T>::template get<flag>(p, q) ? properties_of<T>::template get<yes_prop>(p, q) : properties_of<T>::template get<no_prop>(p, q);
  385.             }
  386.         };
  387.  
  388.         /////////////////////////////////////////
  389.         struct unmatchedAccessor {
  390.             friend constexpr auto operator^(unmatchedAccessor, unmatchedAccessor) noexcept { return unmatchedAccessor{}; }
  391.             template <typename T>
  392.             friend constexpr auto operator^(unmatchedAccessor, T) noexcept { return T{}; }
  393.             template <typename T>
  394.             friend constexpr auto operator^(T, unmatchedAccessor) noexcept { return T{}; }
  395.         };
  396.  
  397.         template <typename... Properties>
  398.         struct structured_type {
  399.             using type = structured_type<Properties...>;
  400.  
  401.             template <ref_prop id>
  402.             static auto get_accessor() noexcept {
  403.                 static_assert((... + (Properties::id == id)) <= 1, "id cannot appear more than once in Properties");
  404.                 static_assert((... + (Properties::id == id)) == 1, "id must appear exactly once in Properties");
  405.                 return (... ^ std::conditional_t<Properties::id == id, typename Properties::accessor, unmatchedAccessor>{});
  406.             }
  407.             template <ref_prop id>
  408.             using accessor = decltype(get_accessor<id>());
  409.  
  410.             template <ref_prop id> static constexpr decltype(auto) get(const char* p, const char* q)         noexcept {
  411.                 return accessor<id>::get(p, q);
  412.             }
  413.             template <ref_prop id> static constexpr decltype(auto) offset(const char* p, const char* q)      noexcept {
  414.                 return accessor<id>::offset(p, q);
  415.             }
  416.             template <ref_prop id> static constexpr decltype(auto) size(const char* p, const char* q)        noexcept {
  417.                 return accessor<id>::size(p, q);
  418.             }
  419.             template <ref_prop id> static constexpr decltype(auto) next_offset(const char* p, const char* q) noexcept {
  420.                 return accessor<id>::next_offset(p, q);
  421.             }
  422.  
  423.             static void check(const char* p, const char* q, std::size_t entryLimit) {
  424.                 (..., Properties::accessor::check(p, q, entryLimit)); // check all properties in order
  425.             }
  426.         };
  427.  
  428. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  429.         // note: in the absence of support for auto non-type template parameters where we could different enum class types,
  430.         // we make sure different *_prop constants do not oberlap, so that trying to access a property of the wrong type
  431.         // will result in an error at comptile time
  432.         // entryLen is special and needs to be the same in all cases, since it is used in the definition of accessors
  433.  
  434.         struct copy_mask_byte {};
  435.  
  436.         struct copy_mask_prop { enum { entryLen = entryLen_, value = 0}; };
  437.         template <>
  438.         struct properties_of<copy_mask_byte>
  439.             : property_ids<copy_mask_prop>, structured_type<
  440.             ref_property<copy_mask_prop::entryLen, fixed_size<copy_mask_byte>>,
  441.             ref_property<copy_mask_prop::value,    element_checked<ADS_UINT8, 0, 0, 0xff>>>
  442.         {};
  443.  
  444.         struct array_info_prop { enum { entryLen = entryLen_, lBound = 1000, elements }; };
  445.         template <>
  446.         struct properties_of<AdsDatatypeArrayInfo>
  447.             : property_ids<array_info_prop>, structured_type<
  448.             ref_property<array_info_prop::entryLen, fixed_size<AdsDatatypeArrayInfo>>,
  449.             ref_property<array_info_prop::lBound,   ELEMENT(AdsDatatypeArrayInfo, lBound)>,
  450.             ref_property<array_info_prop::elements, ELEMENT(AdsDatatypeArrayInfo, elements)>>
  451.         {};
  452.  
  453.         struct symbol_entry_prop {
  454.             enum {
  455.                 entryLen = entryLen_, group = 2000, offset, size, typeId, flags,
  456.                 isPersistent, isBitValue, isReference, hasTypeGuid, isTcComIFacePtr, isReadOnly, ITFMethodAccess, isMethodRef, hasAttributes, isStatic,
  457.                 reserved, nameLen, typeLen, commentLen,
  458.                 nameData, name, nameTerminator, typeData, type, typeTerminator, commentData, comment, commentTerminator, guid, numAttributes, attributeData,
  459.                 expEntryLen
  460.             };
  461.         };
  462.         template <>
  463.         struct properties_of<AdsSymbolEntry>
  464.             : property_ids<symbol_entry_prop>, structured_type<
  465.             ref_property<symbol_entry_prop::entryLen,          ELEMENT(     AdsSymbolEntry, entryLength)>,
  466.             ref_property<symbol_entry_prop::group,             ELEMENT(     AdsSymbolEntry, iGroup)>,
  467.             ref_property<symbol_entry_prop::offset,            ELEMENT(     AdsSymbolEntry, iOffs)>,
  468.             ref_property<symbol_entry_prop::size,              ELEMENT(     AdsSymbolEntry, size)>,
  469.             ref_property<symbol_entry_prop::typeId,            ELEMENT_TYPE(AdsSymbolEntry, dataType, adsDatatypeId)>,
  470.             ref_property<symbol_entry_prop::flags,             ELEMENT(     AdsSymbolEntry, flags)>,
  471.             ref_property<symbol_entry_prop::isPersistent,      flag<        AdsSymbolEntry, symbol_entry_prop::flags, ADSSYMBOLFLAG_PERSISTENT>>,
  472.             ref_property<symbol_entry_prop::isBitValue,        flag<        AdsSymbolEntry, symbol_entry_prop::flags, ADSSYMBOLFLAG_BITVALUE>>,
  473.             ref_property<symbol_entry_prop::isReference,       flag<        AdsSymbolEntry, symbol_entry_prop::flags, ADSSYMBOLFLAG_REFERENCETO>>,
  474.             ref_property<symbol_entry_prop::hasTypeGuid,       flag<        AdsSymbolEntry, symbol_entry_prop::flags, ADSSYMBOLFLAG_TYPEGUID>>,
  475.             ref_property<symbol_entry_prop::isTcComIFacePtr,   flag<        AdsSymbolEntry, symbol_entry_prop::flags, ADSSYMBOLFLAG_TCCOMIFACEPTR>>,
  476.             ref_property<symbol_entry_prop::isReadOnly,        flag<        AdsSymbolEntry, symbol_entry_prop::flags, ADSSYMBOLFLAG_READONLY>>,
  477.             ref_property<symbol_entry_prop::ITFMethodAccess,   flag<        AdsSymbolEntry, symbol_entry_prop::flags, ADSSYMBOLFLAG_ITFMETHODACCESS>>,
  478.             ref_property<symbol_entry_prop::isMethodRef,       flag<        AdsSymbolEntry, symbol_entry_prop::flags, ADSSYMBOLFLAG_METHODDEREF>>,
  479.             ref_property<symbol_entry_prop::hasAttributes,     flag<        AdsSymbolEntry, symbol_entry_prop::flags, ADSSYMBOLFLAG_ATTRIBUTES>>,
  480.             ref_property<symbol_entry_prop::isStatic,          flag<        AdsSymbolEntry, symbol_entry_prop::flags, ADSSYMBOLFLAG_STATIC>>,
  481.             ref_property<symbol_entry_prop::reserved,          ELEMENT_EXP( AdsSymbolEntry, reserved, 0)>,
  482.             ref_property<symbol_entry_prop::nameLen,           ELEMENT(     AdsSymbolEntry, nameLength)>,
  483.             ref_property<symbol_entry_prop::typeLen,           ELEMENT(     AdsSymbolEntry, typeLength)>,
  484.             ref_property<symbol_entry_prop::commentLen,        ELEMENT(     AdsSymbolEntry, commentLength)>,
  485.             ref_property<symbol_entry_prop::nameData,          next_data<   AdsSymbolEntry, symbol_entry_prop::commentLen>>,
  486.             ref_property<symbol_entry_prop::name,              string<      AdsSymbolEntry, symbol_entry_prop::nameData, symbol_entry_prop::nameLen>>,
  487.             ref_property<symbol_entry_prop::nameTerminator,    next_element<AdsSymbolEntry, symbol_entry_prop::name, char, 1, '\0'>>,
  488.             ref_property<symbol_entry_prop::typeData,          next_data<   AdsSymbolEntry, symbol_entry_prop::nameTerminator>>,
  489.             ref_property<symbol_entry_prop::type,              string<      AdsSymbolEntry, symbol_entry_prop::typeData, symbol_entry_prop::typeLen>>,
  490.             ref_property<symbol_entry_prop::typeTerminator,    next_element<AdsSymbolEntry, symbol_entry_prop::type, char, 1, '\0'>>,
  491.             ref_property<symbol_entry_prop::commentData,       next_data<   AdsSymbolEntry, symbol_entry_prop::typeTerminator>>,
  492.             ref_property<symbol_entry_prop::comment,           string<      AdsSymbolEntry, symbol_entry_prop::commentData, symbol_entry_prop::commentLen>>,
  493.             ref_property<symbol_entry_prop::commentTerminator, next_element<AdsSymbolEntry, symbol_entry_prop::comment, char, 1, '\0'>>,
  494.             ref_property<symbol_entry_prop::guid,              optional<    AdsSymbolEntry, symbol_entry_prop::hasTypeGuid,   next_element<AdsSymbolEntry, symbol_entry_prop::commentTerminator, GUID>>>,
  495.             ref_property<symbol_entry_prop::numAttributes,     optional_def<AdsSymbolEntry, symbol_entry_prop::hasAttributes, next_element<AdsSymbolEntry, symbol_entry_prop::guid, ADS_UINT16>, 0>>,
  496.             ref_property<symbol_entry_prop::attributeData,     array<       AdsSymbolEntry, symbol_entry_prop::numAttributes, symbol_entry_prop::numAttributes, AdsAttributeEntry>>,
  497.             ref_property<symbol_entry_prop::expEntryLen,       next_data_c< AdsSymbolEntry, symbol_entry_prop::attributeData, symbol_entry_prop::entryLen>>>
  498.         {
  499.             static constexpr ref_prop sort_prop = symbol_entry_prop::name;
  500.             static constexpr auto cmp_less = icmp_less;
  501.             static constexpr auto cmp_equal = icmp_equal;
  502.         };
  503.  
  504.         struct attribute_entry_prop { enum { entryLen = entryLen_, nameLen = 3000, valueLen, nameData, name, nameTerminator, valueData, value, valueTerminator }; };
  505.         template <>
  506.         struct properties_of<AdsAttributeEntry>
  507.             : property_ids<attribute_entry_prop>, structured_type<
  508.             ref_property<attribute_entry_prop::nameLen,         ELEMENT(     AdsAttributeEntry, nameLength)>,
  509.             ref_property<attribute_entry_prop::valueLen,        ELEMENT(     AdsAttributeEntry, valueLength)>,
  510.             ref_property<attribute_entry_prop::nameData,        next_data<   AdsAttributeEntry, attribute_entry_prop::valueLen>>,
  511.             ref_property<attribute_entry_prop::name,            string<      AdsAttributeEntry, attribute_entry_prop::nameData, attribute_entry_prop::nameLen>>,
  512.             ref_property<attribute_entry_prop::nameTerminator,  next_element<AdsAttributeEntry, attribute_entry_prop::name, char, 1, '\0'>>,
  513.             ref_property<attribute_entry_prop::valueData,       next_data<   AdsAttributeEntry, attribute_entry_prop::nameTerminator>>,
  514.             ref_property<attribute_entry_prop::value,           string<      AdsAttributeEntry, attribute_entry_prop::valueData, attribute_entry_prop::valueLen>>,
  515.             ref_property<attribute_entry_prop::valueTerminator, next_element<AdsAttributeEntry, attribute_entry_prop::value, char, 1, '\0'>>,
  516.             ref_property<attribute_entry_prop::entryLen,        next_data<   AdsAttributeEntry, attribute_entry_prop::valueTerminator>>>
  517.         {};
  518.  
  519.         struct datatype_entry_prop {
  520.             enum {
  521.                 entryLen = entryLen_, version = 4000, size, offset, typeId, flags,
  522.                 isDatatype, isDataitem, isReference, isMethodRef, isOversample, hasBitValues, isPropItem, hasTypeGuid, flagPersistent, containsPersistent, isPersistent,
  523.                 hasCopyMask, flagTcComInterfacePtr, containsTcComInterfacePtr, isTcComInterfacePtr, hasMethodInfos, hasAttributes, hasEnumInfos, isAligned, isStatic,
  524.                 nameLen, typeLen, commentLen, numArrayDims, numSubItems,
  525.                 nameData, name, nameTerminator, typeData, type, typeTerminator, commentData, comment, commentTerminator,
  526.                 arrayInfoData, subItemData, guid, copyMaskLen, copyMaskData, copyMaskBegin, copyMask,
  527.                 numMethods, methodData, numAttributes, attributeData,
  528.                 numEnumInfos, enumInfoData,
  529.                 expEntryLen
  530.             };
  531.         };
  532.         template <>
  533.         struct properties_of<AdsDatatypeEntry>
  534.             : property_ids<datatype_entry_prop>, structured_type<
  535.             ref_property<datatype_entry_prop::entryLen,                  ELEMENT(     AdsDatatypeEntry, entryLength)>,
  536.             ref_property<datatype_entry_prop::version,                   ELEMENT_EXP( AdsDatatypeEntry, version, 1)>,
  537.             ref_property<datatype_entry_prop::size,                      ELEMENT(     AdsDatatypeEntry, size)>,
  538.             ref_property<datatype_entry_prop::offset,                    ELEMENT(     AdsDatatypeEntry, offs)>,
  539.             ref_property<datatype_entry_prop::typeId,                    ELEMENT_TYPE(AdsDatatypeEntry, dataType, adsDatatypeId)>,
  540.             ref_property<datatype_entry_prop::flags,                     ELEMENT(     AdsDatatypeEntry, flags)>,
  541.             ref_property<datatype_entry_prop::isDatatype,                flag<        AdsDatatypeEntry, datatype_entry_prop::flags, ADSDATATYPEFLAG_DATATYPE>>,
  542.             ref_property<datatype_entry_prop::isDataitem,                flag<        AdsDatatypeEntry, datatype_entry_prop::flags, ADSDATATYPEFLAG_DATAITEM>>,
  543.             ref_property<datatype_entry_prop::isReference,               flag<        AdsDatatypeEntry, datatype_entry_prop::flags, ADSDATATYPEFLAG_REFERENCETO>>,
  544.             ref_property<datatype_entry_prop::isMethodRef,               flag<        AdsDatatypeEntry, datatype_entry_prop::flags, ADSDATATYPEFLAG_METHODDEREF>>,
  545.             ref_property<datatype_entry_prop::isOversample,              flag<        AdsDatatypeEntry, datatype_entry_prop::flags, ADSDATATYPEFLAG_OVERSAMPLE>>,
  546.             ref_property<datatype_entry_prop::hasBitValues,              flag<        AdsDatatypeEntry, datatype_entry_prop::flags, ADSDATATYPEFLAG_BITVALUES>>,
  547.             ref_property<datatype_entry_prop::isPropItem,                flag<        AdsDatatypeEntry, datatype_entry_prop::flags, ADSDATATYPEFLAG_PROPITEM>>,
  548.             ref_property<datatype_entry_prop::hasTypeGuid,               flag<        AdsDatatypeEntry, datatype_entry_prop::flags, ADSDATATYPEFLAG_TYPEGUID>>,
  549.             ref_property<datatype_entry_prop::flagPersistent,            flag<        AdsDatatypeEntry, datatype_entry_prop::flags, ADSDATATYPEFLAG_PERSISTENT>>,
  550.             ref_property<datatype_entry_prop::containsPersistent,        and_<        AdsDatatypeEntry, datatype_entry_prop::flagPersistent, datatype_entry_prop::isDatatype>>,
  551.             ref_property<datatype_entry_prop::isPersistent,              and_<        AdsDatatypeEntry, datatype_entry_prop::flagPersistent, datatype_entry_prop::isDataitem>>,
  552.             ref_property<datatype_entry_prop::hasCopyMask,               flag<        AdsDatatypeEntry, datatype_entry_prop::flags, ADSDATATYPEFLAG_COPYMASK>>,
  553.             ref_property<datatype_entry_prop::flagTcComInterfacePtr,     flag<        AdsDatatypeEntry, datatype_entry_prop::flags, ADSDATATYPEFLAG_TCCOMIFACEPTR>>,
  554.             ref_property<datatype_entry_prop::containsTcComInterfacePtr, and_<        AdsDatatypeEntry, datatype_entry_prop::flagTcComInterfacePtr, datatype_entry_prop::isDatatype>>,
  555.             ref_property<datatype_entry_prop::isTcComInterfacePtr,       and_<        AdsDatatypeEntry, datatype_entry_prop::flagTcComInterfacePtr, datatype_entry_prop::isDataitem>>,
  556.             ref_property<datatype_entry_prop::hasMethodInfos,            flag<        AdsDatatypeEntry, datatype_entry_prop::flags, ADSDATATYPEFLAG_METHODINFOS>>,
  557.             ref_property<datatype_entry_prop::hasAttributes,             flag<        AdsDatatypeEntry, datatype_entry_prop::flags, ADSDATATYPEFLAG_ATTRIBUTES>>,
  558.             ref_property<datatype_entry_prop::hasEnumInfos,              flag<        AdsDatatypeEntry, datatype_entry_prop::flags, ADSDATATYPEFLAG_ENUMINFOS>>,
  559.             ref_property<datatype_entry_prop::isAligned,                 flag<        AdsDatatypeEntry, datatype_entry_prop::flags, ADSDATATYPEFLAG_ALIGNED>>,
  560.             ref_property<datatype_entry_prop::isStatic,                  flag<        AdsDatatypeEntry, datatype_entry_prop::flags, ADSDATATYPEFLAG_STATIC>>,
  561.             ref_property<datatype_entry_prop::nameLen,                   ELEMENT(     AdsDatatypeEntry, nameLength)>,
  562.             ref_property<datatype_entry_prop::typeLen,                   ELEMENT(     AdsDatatypeEntry, typeLength)>,
  563.             ref_property<datatype_entry_prop::commentLen,                ELEMENT(     AdsDatatypeEntry, commentLength)>,
  564.             ref_property<datatype_entry_prop::numArrayDims,              ELEMENT(     AdsDatatypeEntry, arrayDim)>,
  565.             ref_property<datatype_entry_prop::numSubItems,               ELEMENT(     AdsDatatypeEntry, subItems)>,
  566.             ref_property<datatype_entry_prop::nameData,                  next_data<   AdsDatatypeEntry, datatype_entry_prop::numSubItems>>,
  567.             ref_property<datatype_entry_prop::name,                      string<      AdsDatatypeEntry, datatype_entry_prop::nameData, datatype_entry_prop::nameLen>>,
  568.             ref_property<datatype_entry_prop::nameTerminator,            next_element<AdsDatatypeEntry, datatype_entry_prop::name, char, 1, '\0'>>,
  569.             ref_property<datatype_entry_prop::typeData,                  next_data<   AdsDatatypeEntry, datatype_entry_prop::nameTerminator>>,
  570.             ref_property<datatype_entry_prop::type,                      string<      AdsDatatypeEntry, datatype_entry_prop::typeData, datatype_entry_prop::typeLen>>,
  571.             ref_property<datatype_entry_prop::typeTerminator,            next_element<AdsDatatypeEntry, datatype_entry_prop::type, char, 1, '\0'>>,
  572.             ref_property<datatype_entry_prop::commentData,               next_data<   AdsDatatypeEntry, datatype_entry_prop::typeTerminator>>,
  573.             ref_property<datatype_entry_prop::comment,                   string<      AdsDatatypeEntry, datatype_entry_prop::commentData, datatype_entry_prop::commentLen>>,
  574.             ref_property<datatype_entry_prop::commentTerminator,         next_element<AdsDatatypeEntry, datatype_entry_prop::comment, char, 1, '\0'>>,
  575.             ref_property<datatype_entry_prop::arrayInfoData,             array<       AdsDatatypeEntry, datatype_entry_prop::commentTerminator, datatype_entry_prop::numArrayDims, AdsDatatypeArrayInfo>>,
  576.             ref_property<datatype_entry_prop::subItemData,               array<       AdsDatatypeEntry, datatype_entry_prop::arrayInfoData, datatype_entry_prop::numSubItems, AdsDatatypeEntry>>,
  577.             ref_property<datatype_entry_prop::guid,                      optional<    AdsDatatypeEntry, datatype_entry_prop::hasTypeGuid, next_element<AdsDatatypeEntry, datatype_entry_prop::subItemData, GUID>>>,
  578.             ref_property<datatype_entry_prop::copyMaskLen,               optional_def<AdsDatatypeEntry, datatype_entry_prop::hasCopyMask, ELEMENT(AdsDatatypeEntry, size), 0>>,
  579.             ref_property<datatype_entry_prop::copyMaskData,              array<       AdsDatatypeEntry, datatype_entry_prop::guid, datatype_entry_prop::copyMaskLen, copy_mask_byte>>,
  580.             ref_property<datatype_entry_prop::copyMaskBegin,             next_data<   AdsDatatypeEntry, datatype_entry_prop::guid>>,
  581.             ref_property<datatype_entry_prop::copyMask,                  var_data<    AdsDatatypeEntry, datatype_entry_prop::copyMaskBegin, datatype_entry_prop::copyMaskLen, ADS_UINT8>>,
  582.             ref_property<datatype_entry_prop::numMethods,                optional_def<AdsDatatypeEntry, datatype_entry_prop::hasMethodInfos, next_element<AdsDatatypeEntry, datatype_entry_prop::copyMask, ADS_UINT16>, 0>>,
  583.             ref_property<datatype_entry_prop::methodData,                array<       AdsDatatypeEntry, datatype_entry_prop::numMethods, datatype_entry_prop::numMethods, AdsMethodEntry>>,
  584.             ref_property<datatype_entry_prop::numAttributes,             optional_def<AdsDatatypeEntry, datatype_entry_prop::hasAttributes, next_element<AdsDatatypeEntry, datatype_entry_prop::methodData, ADS_UINT16>, 0>>,
  585.             ref_property<datatype_entry_prop::attributeData,             array<       AdsDatatypeEntry, datatype_entry_prop::numAttributes, datatype_entry_prop::numAttributes, AdsAttributeEntry>>,
  586.             ref_property<datatype_entry_prop::numEnumInfos,              optional_def<AdsDatatypeEntry, datatype_entry_prop::hasEnumInfos, next_element<AdsDatatypeEntry, datatype_entry_prop::attributeData, ADS_UINT16>, 0>>,
  587.             ref_property<datatype_entry_prop::enumInfoData,              array<       AdsDatatypeEntry, datatype_entry_prop::numEnumInfos, datatype_entry_prop::numEnumInfos, AdsEnumInfoEntry>>,
  588.             ref_property<datatype_entry_prop::expEntryLen,               next_data_c< AdsDatatypeEntry, datatype_entry_prop::enumInfoData, datatype_entry_prop::entryLen>>>
  589.         {
  590.             static constexpr ref_prop sort_prop = datatype_entry_prop::name;
  591.             static constexpr auto cmp_less = std::less<>{};
  592.             static constexpr auto cmp_equal = std::equal_to<>{};
  593.         };
  594.  
  595.         struct method_para_entry_prop {
  596.             enum {
  597.                 entryLen = entryLen_, size = 5000, alignSize, typeId, flags,
  598.                 isIn, isOut, isInOut, isReference,
  599.                 reserved, guid, lengthIsPara, nameLen, typeLen, commentLen,
  600.                 nameData, name, nameTerminator, typeData, type, typeTerminator, commentData, comment, commentTerminator,
  601.                 expEntryLen
  602.             };
  603.         };
  604.         constexpr auto method_para_inoutreference_mask = ADSMETHODPARAFLAG_IN | ADSMETHODPARAFLAG_OUT | ADSMETHODPARAFLAG_INOUT | ADSMETHODPARAFLAG_BYREFERENCE;
  605.         template <>
  606.         struct properties_of<AdsMethodParaEntry>
  607.             : property_ids<method_para_entry_prop>, structured_type<
  608.             ref_property<method_para_entry_prop::entryLen,          ELEMENT(     AdsMethodParaEntry, entryLength)>,
  609.             ref_property<method_para_entry_prop::size,              ELEMENT(     AdsMethodParaEntry, size)>,
  610.             ref_property<method_para_entry_prop::alignSize,         ELEMENT(     AdsMethodParaEntry, alignSize)>,
  611.             ref_property<method_para_entry_prop::typeId,            ELEMENT_TYPE(AdsMethodParaEntry, dataType, adsDatatypeId)>,
  612.             ref_property<method_para_entry_prop::flags,             ELEMENT(     AdsMethodParaEntry, flags)>,
  613.             ref_property<method_para_entry_prop::isIn,              flag<        AdsMethodParaEntry, method_para_entry_prop::flags, method_para_inoutreference_mask, ADSMETHODPARAFLAG_IN>>,
  614.             ref_property<method_para_entry_prop::isOut,             flag<        AdsMethodParaEntry, method_para_entry_prop::flags, method_para_inoutreference_mask, ADSMETHODPARAFLAG_OUT>>,
  615.             ref_property<method_para_entry_prop::isInOut,           flag<        AdsMethodParaEntry, method_para_entry_prop::flags, method_para_inoutreference_mask, ADSMETHODPARAFLAG_INOUT>>,
  616.             ref_property<method_para_entry_prop::isReference,       flag<        AdsMethodParaEntry, method_para_entry_prop::flags, method_para_inoutreference_mask, ADSMETHODPARAFLAG_BYREFERENCE>>,
  617.             ref_property<method_para_entry_prop::reserved,          ELEMENT_EXP( AdsMethodParaEntry, reserved, 0)>,
  618.             ref_property<method_para_entry_prop::guid,              ELEMENT(     AdsMethodParaEntry, typeGuid)>,
  619.             ref_property<method_para_entry_prop::lengthIsPara,      ELEMENT(     AdsMethodParaEntry, lengthIsPara)>,
  620.             ref_property<method_para_entry_prop::nameLen,           ELEMENT(     AdsMethodParaEntry, nameLength)>,
  621.             ref_property<method_para_entry_prop::typeLen,           ELEMENT(     AdsMethodParaEntry, typeLength)>,
  622.             ref_property<method_para_entry_prop::commentLen,        ELEMENT(     AdsMethodParaEntry, commentLength)>,
  623.             ref_property<method_para_entry_prop::nameData,          next_data<   AdsMethodParaEntry, method_para_entry_prop::commentLen>>,
  624.             ref_property<method_para_entry_prop::name,              string<      AdsMethodParaEntry, method_para_entry_prop::nameData, method_para_entry_prop::nameLen>>,
  625.             ref_property<method_para_entry_prop::nameTerminator,    next_element<AdsMethodParaEntry, method_para_entry_prop::name, char, 1, '\0'>>,
  626.             ref_property<method_para_entry_prop::typeData,          next_data<   AdsMethodParaEntry, method_para_entry_prop::nameTerminator>>,
  627.             ref_property<method_para_entry_prop::type,              string<      AdsMethodParaEntry, method_para_entry_prop::typeData, method_para_entry_prop::typeLen>>,
  628.             ref_property<method_para_entry_prop::typeTerminator,    next_element<AdsMethodParaEntry, method_para_entry_prop::type, char, 1, '\0'>>,
  629.             ref_property<method_para_entry_prop::commentData,       next_data<   AdsMethodParaEntry, method_para_entry_prop::typeTerminator>>,
  630.             ref_property<method_para_entry_prop::comment,           string<      AdsMethodParaEntry, method_para_entry_prop::commentData, method_para_entry_prop::commentLen>>,
  631.             ref_property<method_para_entry_prop::commentTerminator, next_element<AdsMethodParaEntry, method_para_entry_prop::comment, char, 1, '\0'>>,
  632.             ref_property<method_para_entry_prop::expEntryLen,       next_data_c< AdsMethodParaEntry, method_para_entry_prop::commentTerminator, method_para_entry_prop::entryLen>>>
  633.         {};
  634.  
  635.         struct method_entry_prop {
  636.             enum {
  637.                 entryLen = entryLen_, version = 6000, vTableIndex, returnSize, returnAlignSize, reserved, returnTypeGuid, returnTypeId, flags,
  638.                 isPlcCallingConvention, isCallUnlocked,
  639.                 nameLen, typeLen, commentLen, numParas,
  640.                 nameData, name, nameTerminator, typeData, type, typeTerminator, commentData, comment, commentTerminator, paraData,
  641.                 expEntryLen
  642.             };
  643.         };
  644.         template <>
  645.         struct properties_of<AdsMethodEntry>
  646.             : property_ids<method_entry_prop>, structured_type<
  647.             ref_property<method_entry_prop::entryLen,          ELEMENT(     AdsMethodEntry, entryLength)>,
  648.             ref_property<method_entry_prop::version,           ELEMENT_EXP( AdsMethodEntry, version, 1)>,
  649.             ref_property<method_entry_prop::vTableIndex,       ELEMENT(     AdsMethodEntry, vTableIndex)>,
  650.             ref_property<method_entry_prop::returnSize,        ELEMENT(     AdsMethodEntry, returnSize)>,
  651.             ref_property<method_entry_prop::returnAlignSize,   ELEMENT(     AdsMethodEntry, returnAlignSize)>,
  652.             ref_property<method_entry_prop::reserved,          ELEMENT_EXP( AdsMethodEntry, reserved, 0)>,
  653.             ref_property<method_entry_prop::returnTypeGuid,    ELEMENT(     AdsMethodEntry, returnTypeGuid)>,
  654.             ref_property<method_entry_prop::returnTypeId,      ELEMENT_TYPE(AdsMethodEntry, returnDataType, adsDatatypeId)>,
  655.             ref_property<method_entry_prop::flags,             ELEMENT(     AdsMethodEntry, flags)>,
  656.             ref_property<method_entry_prop::nameLen,           ELEMENT(     AdsMethodEntry, nameLength)>,
  657.             ref_property<method_entry_prop::typeLen,           ELEMENT(     AdsMethodEntry, returnTypeLength)>,
  658.             ref_property<method_entry_prop::commentLen,        ELEMENT(     AdsMethodEntry, commentLength)>,
  659.             ref_property<method_entry_prop::numParas,          ELEMENT(     AdsMethodEntry, paras)>,
  660.             ref_property<method_entry_prop::nameData,          next_data<   AdsMethodEntry, method_entry_prop::numParas>>,
  661.             ref_property<method_entry_prop::name,              string<      AdsMethodEntry, method_entry_prop::nameData, method_entry_prop::nameLen>>,
  662.             ref_property<method_entry_prop::nameTerminator,    next_element<AdsMethodEntry, method_entry_prop::name, char, 1, '\0'>>,
  663.             ref_property<method_entry_prop::typeData,          next_data<   AdsMethodEntry, method_entry_prop::nameTerminator>>,
  664.             ref_property<method_entry_prop::type,              string<      AdsMethodEntry, method_entry_prop::typeData, method_entry_prop::typeLen>>,
  665.             ref_property<method_entry_prop::typeTerminator,    next_element<AdsMethodEntry, method_entry_prop::type, char, 1, '\0'>>,
  666.             ref_property<method_entry_prop::commentData,       next_data<   AdsMethodEntry, method_entry_prop::typeTerminator>>,
  667.             ref_property<method_entry_prop::comment,           string<      AdsMethodEntry, method_entry_prop::commentData, method_entry_prop::commentLen>>,
  668.             ref_property<method_entry_prop::commentTerminator, next_element<AdsMethodEntry, method_entry_prop::comment, char, 1, '\0'>>,
  669.             ref_property<method_entry_prop::paraData,          array<       AdsMethodEntry, method_entry_prop::commentTerminator, method_entry_prop::numParas, AdsMethodParaEntry>>,
  670.             ref_property<method_entry_prop::expEntryLen,       next_data_c< AdsMethodEntry, method_entry_prop::paraData, method_entry_prop::entryLen>>>
  671.         {};
  672.  
  673.         struct enum_info_prop {
  674.             enum {
  675.                 entryLen = entryLen_, nameLen = 7000, valueLen,
  676.                 nameData, name, nameTerminator, valueData, value
  677.             };
  678.         };
  679.         template <>
  680.         struct properties_of<AdsEnumInfoEntry>
  681.             : property_ids<enum_info_prop>, structured_type<
  682.             ref_property<enum_info_prop::nameLen,        ELEMENT(     AdsEnumInfoEntry, nameLength)>,
  683.             ref_property<enum_info_prop::valueLen,       parent_prop< AdsEnumInfoEntry, AdsDatatypeEntry, datatype_entry_prop::size>>,
  684.             ref_property<enum_info_prop::nameData,       next_data<   AdsEnumInfoEntry, enum_info_prop::nameLen>>,
  685.             ref_property<enum_info_prop::name,           string<      AdsEnumInfoEntry, enum_info_prop::nameData, enum_info_prop::nameLen>>,
  686.             ref_property<enum_info_prop::nameTerminator, next_element<AdsEnumInfoEntry, enum_info_prop::name, char, 1, '\0'>>,
  687.             ref_property<enum_info_prop::valueData,      next_data<   AdsEnumInfoEntry, enum_info_prop::nameTerminator>>,
  688.             ref_property<enum_info_prop::value,          var_data<    AdsEnumInfoEntry, enum_info_prop::valueData, enum_info_prop::valueLen>>,
  689.             ref_property<enum_info_prop::entryLen,       next_data<   AdsEnumInfoEntry, enum_info_prop::value>>>
  690.         {};
  691.  
  692.         template <typename T>
  693.         class ref {
  694.         public:
  695.             using prop_type = typename properties_of<T>::id_type;
  696.  
  697.             constexpr explicit ref(const char* data, by_size, std::size_t limit, const char* parent = nullptr) noexcept
  698.                 : data_(data), parent_data_(parent), limit_(limit), count_by_size_(true)
  699.             {}
  700.  
  701.             constexpr explicit ref(const char* data, by_count, std::size_t limit, const char* parent = nullptr) noexcept
  702.                 : data_(data), parent_data_(parent), limit_(limit), count_by_size_(false)
  703.             {}
  704.  
  705.             ref(const char* p, std::size_t entryLimit) : data(p) {
  706.                
  707.             }
  708.  
  709.             template <ref_prop prop>
  710.             auto get() const noexcept {
  711.                 return properties_of<T>::template get<prop>(data_, parent_data_);
  712.             }
  713.  
  714.             ref& operator++() noexcept {
  715.                 assert(*this);
  716.                 if (count_by_size_)
  717.                     limit_ -= get<entryLen_>();
  718.                 else
  719.                     --limit_;
  720.                 data_ += get<entryLen_>();
  721.                 return *this;
  722.             }
  723.  
  724.             ref operator++(int) noexcept {
  725.                 auto tmp = *this;
  726.                 ++*this;
  727.                 return tmp;
  728.             }
  729.  
  730.             explicit operator bool() const noexcept {
  731.                 return limit_ != 0;
  732.             }
  733.  
  734.             const char* data() const noexcept {
  735.                 return data_;
  736.             }
  737.             std::size_t count() const noexcept {
  738.                 assert(!count_by_size_);
  739.                 return limit_;
  740.             }
  741.  
  742.             // verify integrity of data
  743.             void do_check() const noexcept {
  744.                 assert(*this);
  745.                 if (count_by_size_)
  746.                     properties_of<T>::check(data_, parent_data_, limit_);
  747.                 else
  748.                     properties_of<T>::check(data_, parent_data_, get<entryLen_>());
  749.             }
  750.         private:
  751.             const char* data_;
  752.             const char* parent_data_;
  753.             std::size_t limit_;
  754.             bool count_by_size_; // do we count down by entrySize (if unknown number of entries)
  755.                                  // or else by 1 if the number is known ?
  756.  
  757.             friend bool operator<(const ref& lhs, const ref& rhs) noexcept {
  758.                 return properties_of<T>::cmp_less(lhs.get<properties_of<T>::sort_prop>(), rhs.get<properties_of<T>::sort_prop>());
  759.             }
  760.             template <typename U>
  761.             friend bool operator<(const U& lhs, const ref& rhs) noexcept {
  762.                 return properties_of<T>::cmp_less(lhs, rhs.get<properties_of<T>::sort_prop>());
  763.             }
  764.             template <typename U>
  765.             friend bool operator<(const ref& lhs, const U& rhs) noexcept {
  766.                 return properties_of<T>::cmp_less(lhs.get<properties_of<T>::sort_prop>(), rhs);
  767.             }
  768.             friend bool operator==(const ref& lhs, const ref& rhs) noexcept {
  769.                 return properties_of<T>::cmp_equal(lhs.get<properties_of<T>::sort_prop>(), rhs.get<properties_of<T>::sort_prop>());
  770.             }
  771.             template <typename U>
  772.             friend bool operator==(const U& lhs, const ref& rhs) noexcept {
  773.                 return properties_of<T>::cmp_equal(lhs, rhs.get<properties_of<T>::sort_prop>());
  774.             }
  775.             template <typename U>
  776.             friend bool operator==(const ref& lhs, const U& rhs) noexcept {
  777.                 return properties_of<T>::cmp_equal(lhs.get<properties_of<T>::sort_prop>(), rhs);
  778.             }
  779.         };
  780.     } // namespace detail
  781.     using detail::ref;
  782.  
  783.     template <typename T>
  784.     struct reverse_view { T& iterable; };
  785.  
  786.     template <typename T>
  787.     auto begin(reverse_view<T> w) {
  788.         using std::rbegin;
  789.         return rbegin(w.iterable);
  790.     }
  791.  
  792.     template <typename T>
  793.     auto end(reverse_view<T> w) {
  794.         using std::rend;
  795.         return rend(w.iterable);
  796.     }
  797.  
  798.     template <typename T>
  799.     reverse_view<T> reverse(T&& iterable) {
  800.         return { iterable };
  801.     }
  802. }
  803.  
  804. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  805. using refDT = ref<AdsDatatypeEntry>;
  806. using refSym = ref<AdsSymbolEntry>;
  807. using refArrayInfo = ref<AdsDatatypeArrayInfo>;
  808. using refAttribute = ref<AdsAttributeEntry>;
  809. using refEnumInfo = ref<AdsEnumInfoEntry>;
  810.  
  811. class adsData::subSymbolInfo {
  812. public:
  813.     subSymbolInfo(std::string_view name, std::string_view comment, sizeType offset, bool isStatic, adsData::datatypeInfo* typeData = nullptr)
  814.         : name(name), comment(comment), offset(offset), isStatic(isStatic), typeData(typeData)
  815.     {}
  816.     subSymbolInfo(std::string_view name, std::string_view comment, sizeType offset, bool isStatic, adsData::datatypeInfo* typeData, refAttribute attr)
  817.         : subSymbolInfo(name, comment, offset, isStatic, typeData)
  818.     {
  819.         attributes.reserve(attr.count());
  820.         for (;attr; ++attr) {
  821.             using attr_prop = refAttribute::prop_type;
  822.             attributes.emplace_back(attr.get<attr_prop::name>(), attr.get<attr_prop::value>());
  823.         }
  824.     }
  825.     std::string name;
  826.     std::string comment;
  827.     sizeType offset;
  828.     bool isStatic;
  829.     adsData::datatypeInfo* typeData;
  830.     std::vector<attributeInfo> attributes{};
  831.  
  832.     friend bool operator==(const subSymbolInfo& lhs, const subSymbolInfo& rhs) {
  833.         return icmp_equal(lhs.name, rhs.name);
  834.     }
  835.     friend bool operator==(const subSymbolInfo& lhs, std::string_view rhs) {
  836.         return icmp_equal(lhs.name, rhs);
  837.     }
  838.     friend bool operator==(std::string_view lhs, const subSymbolInfo& rhs) {
  839.         return icmp_equal(lhs, rhs.name);
  840.     }
  841. };
  842.  
  843. class adsData::symbolInfo {
  844.     using prop = refSym::prop_type;
  845. public:
  846.     subSymbolInfo baseInfo;
  847.     sizeType group;
  848.  
  849.     symbolInfo(refSym sym, datatypeInfo* typeData)
  850.         : baseInfo{ sym.get<prop::name>(), sym.get<prop::comment>(), sym.get<prop::offset>(), sym.get<prop::isStatic>(),
  851.                     typeData, sym.get<prop::attributeData>() },
  852.         group{ sym.get<prop::group>() }
  853.     {}
  854.  
  855.     friend bool operator<(const symbolInfo& lhs, const symbolInfo& rhs) {
  856.         return icmp_less(lhs.baseInfo.name, rhs.baseInfo.name);
  857.     }
  858.     friend bool operator<(const symbolInfo& lhs, std::string_view rhs) {
  859.         return icmp_less(lhs.baseInfo.name, rhs);
  860.     }
  861.     friend bool operator<(std::string_view lhs, const symbolInfo& rhs) {
  862.         return icmp_less(lhs, rhs.baseInfo.name);
  863.     }
  864. };
  865.  
  866. class adsData::arrayInfo : public subSymbolInfo
  867. {
  868.     using dt_prop = refDT::prop_type;
  869.     using arr_prop = refArrayInfo::prop_type;
  870.     std::vector<AdsDatatypeArrayInfo> arrayInfoData_;
  871.     sizeType numElements_;
  872. public:
  873.     explicit arrayInfo(refDT dt)
  874.         : adsData::subSymbolInfo(""sv, dt.get<dt_prop::comment>(), 0, false), arrayInfoData_{}, numElements_{ 1 }
  875.     {
  876.         auto arrayInfoData = dt.get<dt_prop::arrayInfoData>();
  877.         auto arraySize = dt.get<dt_prop::size>();
  878.         assert(arrayInfoData.count() != 0);
  879.         arrayInfoData_.reserve(arrayInfoData.count());
  880.         for (; arrayInfoData; ++arrayInfoData) {
  881.             using prop = refArrayInfo::prop_type;
  882.             auto& info = arrayInfoData_.emplace_back(AdsDatatypeArrayInfo{
  883.                 arrayInfoData.get<arr_prop::lBound>(), arrayInfoData.get<arr_prop::elements>() });
  884.             sizeType rBound = info.lBound + info.elements - 1;
  885.             ASSERT_THROW("array info corrupted" && info.elements != 0 && rBound >= info.lBound && arraySize % info.elements == 0);
  886.             arraySize /= info.elements;
  887.             numElements_ *= info.elements;
  888.         }
  889.     }
  890.     sizeType index(std::string_view i) const {
  891.         auto workIndex = i;
  892.         sizeType realIndex = 0;
  893.         for (auto& info : arrayInfoData_) {
  894.             auto pos = workIndex.find(',');
  895.             if ((&info == &arrayInfoData_.back()) == (pos == workIndex.npos))
  896.                 throw std::out_of_range("index with wrong number of dimensions: "s + i);
  897.             auto curIndex = workIndex;
  898.             if (pos != workIndex.npos) {
  899.                 curIndex.remove_suffix(curIndex.size() - pos);
  900.                 workIndex.remove_prefix(pos + 1);
  901.             }
  902.             auto n = svtoi(curIndex);
  903.             if (n < info.lBound || n - info.lBound >= info.elements)
  904.                 throw std::out_of_range("index out of range: "s + i);
  905.             // we don't need to check for overflow here since the constructor already ensures that
  906.             // indizes stay within proper bounds
  907.             realIndex = realIndex * info.elements + (n - info.lBound);
  908.         }
  909.         return realIndex;
  910.     }
  911.     std::string toString(sizeType i) const {
  912.         if (i >= numElements_)
  913.             throw std::out_of_range("index out of range");
  914.         std::string result = "]";
  915.         for (auto& info : reverse(arrayInfoData_)) {
  916.             auto curIndex = i % info.elements + info.lBound;
  917.             i /= info.elements;
  918.             do {
  919.                 result.push_back(static_cast<char>('0' + curIndex % 10));
  920.             } while (curIndex /= 10);
  921.             if (&info != &arrayInfoData_.front())
  922.                 result.push_back(',');
  923.         }
  924.         result.push_back('[');
  925.         std::reverse(result.begin(), result.end());
  926.         return result;
  927.     }
  928.     sizeType elemSize() const noexcept;
  929.     sizeType numElements() const noexcept { return numElements_; }
  930.     static sizeType svtoi(std::string_view s) {
  931.         sizeType result = 0;
  932.         if (s.empty())
  933.             throw std::out_of_range("index corrupted");
  934.         for (; !s.empty(); s.remove_prefix(1)) {
  935.             if (s[0] < '0' || s[0] > '9' || (std::numeric_limits<sizeType>::max() - (s[0] - '0')) / 10 < result)
  936.                 throw std::out_of_range("index corrupted");
  937.             result = result * 10 + (s[0] - '0');
  938.         }
  939.         return result;
  940.     }
  941. };
  942.  
  943. class adsData::datatypeInfo {
  944.     using prop = refDT::prop_type;
  945. public:
  946.     datatypeInfo(std::string_view name, sizeType size, adsDatatypeId id)
  947.         : name(name), size(size), id(id)
  948.     {}
  949.     explicit datatypeInfo(refDT dt)
  950.         : name(dt.get<prop::name>()), size(dt.get<prop::size>()), guid(dt.get<prop::guid>()), id(dt.get<prop::typeId>()),
  951.         copyMask(dt.get<prop::copyMask>())
  952.     {
  953.         ASSERT_THROW(dt.get<prop::isDatatype>() != dt.get<prop::isDataitem>());
  954.         ASSERT_THROW(!dt.get<prop::isDatatype>() || (dt.get<prop::numArrayDims>() == 0 || dt.get<prop::numSubItems>() == 0));
  955.         ASSERT_THROW(!dt.get<prop::isDataitem>() || (dt.get<prop::numArrayDims>() == 0 && dt.get<prop::numSubItems>() == 0));
  956.  
  957.         if (dt.get<prop::numSubItems>() != 0) {
  958.             auto& subs = typeSpecs.emplace<std::vector<subSymbolInfo>>();
  959.             subs.reserve(dt.get<prop::numSubItems>());
  960.             for (refDT sub = dt.get<prop::subItemData>(); sub; ++sub) {
  961.                 ASSERT_THROW(sub.get<prop::isDataitem>());
  962.                 subs.emplace_back(sub.get<prop::name>(), sub.get<prop::comment>(),
  963.                     sub.get<prop::offset>(), sub.get<prop::isStatic>(),
  964.                     nullptr, sub.get<prop::attributeData>());
  965.             }
  966.         } else if (dt.get<prop::numArrayDims>() != 0) {
  967.             typeSpecs.emplace<arrayInfo>(dt);
  968.         } else if (!dt.get<prop::type>().empty()) {
  969.             typeSpecs.emplace<datatypeInfo*>();
  970.         } else {
  971.             typeSpecs.emplace<std::monostate>();
  972.         }
  973.  
  974.         attributes.reserve(dt.get<prop::numAttributes>());
  975.         for (refAttribute attr = dt.get<prop::attributeData>(); attr; ++attr) {
  976.             using attr_prop = refAttribute::prop_type;
  977.             attributes.emplace_back(attr.get<attr_prop::name>(), attr.get<attr_prop::value>());
  978.         }
  979.  
  980.         enums.reserve(dt.get<prop::numEnumInfos>());
  981.         for (refEnumInfo e = dt.get<prop::enumInfoData>(); e; ++e) {
  982.             using enum_prop = refEnumInfo::prop_type;
  983.             auto value = e.get<enum_prop::value>();
  984.             switch (id) {
  985.                 case adsDatatypeId::int8_:
  986.                     enums.emplace_back(e.get<enum_prop::name>() + ""sv, readRaw<std::int8_t>(value.data()));
  987.                     break;
  988.                 case adsDatatypeId::uint8_:
  989.                     enums.emplace_back(e.get<enum_prop::name>() + ""sv, readRaw<std::uint8_t>(value.data()));
  990.                     break;
  991.                 case adsDatatypeId::int16_:
  992.                     enums.emplace_back(e.get<enum_prop::name>() + ""sv, readRaw<std::int16_t>(value.data()));
  993.                     break;
  994.                 case adsDatatypeId::uint16_:
  995.                     enums.emplace_back(e.get<enum_prop::name>() + ""sv, readRaw<std::uint16_t>(value.data()));
  996.                     break;
  997.                 case adsDatatypeId::int32_:
  998.                     enums.emplace_back(e.get<enum_prop::name>() + ""sv, readRaw<std::int32_t>(value.data()));
  999.                     break;
  1000.                 case adsDatatypeId::uint32_:
  1001.                     enums.emplace_back(e.get<enum_prop::name>() + ""sv, readRaw<std::uint32_t>(value.data()));
  1002.                     break;
  1003.                 case adsDatatypeId::int64_:
  1004.                     enums.emplace_back(e.get<enum_prop::name>() + ""sv, readRaw<std::int64_t>(value.data()));
  1005.                     break;
  1006.                 case adsDatatypeId::uint64_:
  1007.                     enums.emplace_back(e.get<enum_prop::name>() + ""sv, readRaw<std::uint64_t>(value.data()));
  1008.                     break;
  1009.                 case adsDatatypeId::bool_:
  1010.                     enums.emplace_back(e.get<enum_prop::name>() + ""sv, readRaw<bool>(value.data()));
  1011.                     break;
  1012.                 case adsDatatypeId::float_:
  1013.                 case adsDatatypeId::double_:
  1014.                 case adsDatatypeId::void_:
  1015.                 case adsDatatypeId::cstring_:
  1016.                 case adsDatatypeId::wcstring_:
  1017.                 case adsDatatypeId::ldouble_:
  1018.                 case adsDatatypeId::blob_:
  1019.                 default:
  1020.                     enums.emplace_back(e.get<enum_prop::name>() + ""sv, value + ""sv);
  1021.             }
  1022.         }
  1023.     }
  1024.  
  1025.     std::string name;
  1026.     sizeType size;
  1027.     std::optional<GUID> guid;
  1028.     adsDatatypeId id;
  1029.     bool isPointer = false;
  1030.     bool visited = false; // used for cycle detection
  1031.     enum { baseSpec, compoundSpec, arraySpec, classSpec };
  1032.     std::variant<std::monostate,                         // base type
  1033.                  datatypeInfo*,                          // compound type (alias or pointer)
  1034.                  arrayInfo,                              // array
  1035.                  std::vector<subSymbolInfo>> typeSpecs;  // class
  1036.     ustring copyMask{};
  1037.     std::vector<attributeInfo> attributes{};
  1038.     std::vector<enumInfo> enums{};
  1039.  
  1040.     friend bool operator<(const adsData::datatypeInfo& lhs, const adsData::datatypeInfo& rhs) {
  1041.         return lhs.name < rhs.name;
  1042.     }
  1043.     friend bool operator<(const adsData::datatypeInfo& lhs, std::string_view rhs) {
  1044.         return lhs.name < rhs;
  1045.     }
  1046.     friend bool operator<(std::string_view lhs, const adsData::datatypeInfo& rhs) {
  1047.         return lhs == rhs.name;
  1048.     }
  1049.     friend bool operator==(const adsData::datatypeInfo& lhs, const adsData::datatypeInfo& rhs) {
  1050.         return lhs.name == rhs.name;
  1051.     }
  1052.     friend bool operator==(const adsData::datatypeInfo& lhs, std::string_view rhs) {
  1053.         return lhs.name < rhs;
  1054.     }
  1055.     friend bool operator==(std::string_view lhs, const adsData::datatypeInfo& rhs) {
  1056.         return lhs == rhs.name;
  1057.     }
  1058. };
  1059.  
  1060. sizeType adsData::arrayInfo::elemSize() const noexcept {
  1061.     return typeData->size;
  1062. }
  1063.  
  1064. namespace {
  1065.     template <typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
  1066.     std::string to_string_hex(T v) {
  1067.         auto val = static_cast<std::uint64_t>(v);
  1068.         char buf[2 * sizeof(val) + 1];
  1069.         std::snprintf(buf, sizeof(buf), "%llx", val);
  1070.         return buf;
  1071.     }
  1072.  
  1073.     void cycleCheck(adsData::datatypeInfo* p) {
  1074.         ASSERT_THROW("Datatypes corrupted: cycle detected" && !p->visited);
  1075.         p->visited = true;
  1076.         static constexpr auto deleter = [](auto* x) { x->visited = false; };
  1077.         std::unique_ptr<adsData::datatypeInfo, decltype(deleter)> guard(p, deleter);
  1078.         switch (p->typeSpecs.index()) {
  1079.             default:
  1080.                 assert(false);
  1081.             case adsData::datatypeInfo::baseSpec:
  1082.                 return;
  1083.             case adsData::datatypeInfo::compoundSpec:
  1084.                 cycleCheck(std::get<adsData::datatypeInfo::compoundSpec>(p->typeSpecs));
  1085.                 return;
  1086.             case adsData::datatypeInfo::arraySpec: {
  1087.                 cycleCheck(std::get<adsData::datatypeInfo::arraySpec>(p->typeSpecs).typeData);
  1088.                 return;
  1089.             }
  1090.             case adsData::datatypeInfo::classSpec: {
  1091.                 for (auto& sub : std::get<adsData::datatypeInfo::classSpec>(p->typeSpecs))
  1092.                     cycleCheck(sub.typeData);
  1093.                 return;
  1094.             }
  1095.         }
  1096.     }
  1097.  
  1098.     const adsData::datatypeInfo* followAlias(const adsData::datatypeInfo* p) noexcept {
  1099.         while (p->typeSpecs.index() == adsData::datatypeInfo::compoundSpec && !p->isPointer)
  1100.             p = std::get<adsData::datatypeInfo::compoundSpec>(p->typeSpecs);
  1101.         return p;
  1102.     }
  1103. } // namespace
  1104.  
  1105. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1106. adsData::adsData() noexcept = default;
  1107.  
  1108. adsData::adsData(decltype(AmsAddr::port) port)
  1109.     : symbols_{}, types_{}
  1110. {
  1111.     auto adsPort = tcAdsAPI::AdsPortOpenEx();
  1112.     ASSERT_THROW("cannot open ads port" && adsPort != 0);
  1113.     auto deleter = [=](auto) { tcAdsAPI::AdsPortCloseEx(adsPort); };
  1114.     std::unique_ptr<void, decltype(deleter)> portGuard(nullptr, deleter);
  1115.     tcAdsAPI::AmsAddr ams_addr{};
  1116.     if (auto err = tcAdsAPI::AdsGetLocalAddressEx(adsPort, &ams_addr); err != ADSERR_NOERR)
  1117.         throw std::runtime_error("cannot get local ams address: 0x" + to_string_hex(err));
  1118.     ams_addr.port = port;
  1119.     AdsSymbolUploadInfo2 info{};
  1120.     unsigned long bytes_read = 0;
  1121.     if (auto err = tcAdsAPI::AdsSyncReadReqEx2(adsPort, &ams_addr, ADSIGRP_SYM_UPLOADINFO2, 0, sizeof(info),
  1122.         &info, &bytes_read); err != ADSERR_NOERR)
  1123.         throw std::runtime_error("cannot read symbol upload info: 0x" + to_string_hex(err));
  1124.     if (bytes_read != sizeof(info))
  1125.         throw std::runtime_error("error reading sym_uploadinfo2: " + std::to_string(bytes_read) + " bytes read");
  1126.     std::vector<char> symData(info.nSymSize);
  1127.     std::vector<char> dtData(info.nDatatypeSize);
  1128.     if (auto err = tcAdsAPI::AdsSyncReadReqEx2(adsPort, &ams_addr, ADSIGRP_SYM_UPLOAD, 0, info.nSymSize,
  1129.         &symData[0], &bytes_read); err != ADSERR_NOERR)
  1130.         throw std::runtime_error("cannot read symbol info: 0x" + to_string_hex(err));
  1131.     if (bytes_read != info.nSymSize)
  1132.         throw std::runtime_error("error reading symbols: " + std::to_string(bytes_read) + " bytes read, "
  1133.             + std::to_string(info.nSymSize) + " expected");
  1134.     if (auto err = tcAdsAPI::AdsSyncReadReqEx2(adsPort, &ams_addr, ADSIGRP_SYM_DT_UPLOAD, 0, info.nDatatypeSize,
  1135.         &dtData[0], &bytes_read); err != ADSERR_NOERR)
  1136.         throw std::runtime_error("cannot read datatype info: 0x" + to_string_hex(err));
  1137.     if (bytes_read != info.nDatatypeSize)
  1138.         throw std::runtime_error("error reading datatypes: " + std::to_string(bytes_read) + " bytes read, "
  1139.             + std::to_string(info.nDatatypeSize) + " expected");
  1140.     portGuard.release();
  1141.     initialize(symData, dtData);
  1142. }
  1143.  
  1144. adsData::adsData(const std::vector<char>& symData, const std::vector<char>& dtData)
  1145.     : symbols_{}, types_{}
  1146. {
  1147.     initialize(symData, dtData);
  1148. }
  1149.  
  1150. adsData::adsData(adsData&&) noexcept = default;
  1151. adsData& adsData::operator=(adsData&&) noexcept = default;
  1152.  
  1153. adsData::~adsData() noexcept = default;
  1154.  
  1155. void adsData::initialize(const std::vector<char>& symData, const std::vector<char>& dtData) {
  1156.     using dtprop = refDT::prop_type;
  1157.     using symprop = refSym::prop_type;
  1158.     // count how many datatype entries we have and verify their integrity
  1159.     std::size_t dtCount = 0;
  1160.     for (refDT dt(&dtData[0], by_size{}, dtData.size()); dt; ++dt) {
  1161.         dt.do_check();
  1162.         ++dtCount;
  1163.     }
  1164.     // add to types_
  1165.     types_.reserve(dtCount);
  1166.     for (refDT dt(&dtData[0], by_count{}, dtCount); dt; ++dt) {
  1167.         types_.emplace_back(dt);
  1168.     }
  1169.     std::sort(types_.begin(), types_.end());
  1170.  
  1171.     // TwinCAT2 doesn't send info about basic datatypes so we add them here as needed
  1172.     // we don't link them yet, because adding to the types-vector might cause a reallocation,
  1173.     // invalidating references in the process
  1174.     for (refDT dt(&dtData[0], by_count{}, dtCount); dt; ++dt) {
  1175.         for (refDT sub = dt.get<dtprop::subItemData>(); sub; ++sub) {
  1176.             ASSERT_THROW("structure elements must have a type" && !sub.get<dtprop::type>().empty());
  1177.             auto it = std::lower_bound(types_.begin(), types_.end(), sub.get<dtprop::type>());
  1178.             if (it == types_.end() || it->name != sub.get<dtprop::type>())
  1179.                 types_.emplace(it, sub.get<dtprop::type>(), sub.get<dtprop::size>(), sub.get<dtprop::typeId>());
  1180.         }
  1181.         if (dt.get<dtprop::type>().empty())
  1182.             continue; // base type or structure
  1183.         auto it = std::lower_bound(types_.begin(), types_.end(), dt.get<dtprop::type>());
  1184.         if (it == types_.end() || it->name != dt.get<dtprop::type>()) {
  1185.             if (dt.get<dtprop::numArrayDims>() != 0) {
  1186.                 types_.emplace(it, dt.get<dtprop::type>(), dt.get<dtprop::size>() / arrayInfo{ dt }.numElements(), dt.get<dtprop::typeId>());
  1187.             } else {
  1188.                 types_.emplace(it, dt.get<dtprop::type>(), dt.get<dtprop::size>(), dt.get<dtprop::typeId>());
  1189.             }
  1190.         }
  1191.     }
  1192.     // count how many symbol entries we have and verify their integrity and add base types as needed
  1193.     std::size_t symCount = 0;
  1194.     for (refSym sym(&symData[0], by_size{}, symData.size()); sym; ++sym) {
  1195.         sym.do_check();
  1196.         ++symCount;
  1197.         ASSERT_THROW("symbols must have a type" && !sym.get<symprop::type>().empty());
  1198.         auto it = std::lower_bound(types_.begin(), types_.end(), sym.get<symprop::type>());
  1199.         if (it == types_.end() || it->name != sym.get<symprop::type>())
  1200.             types_.emplace(it, sym.get<symprop::type>(), sym.get<symprop::size>(), sym.get<symprop::typeId>());
  1201.     }
  1202.  
  1203.     // crosslink datatypes
  1204.     for (refDT dt(&dtData[0], by_count{}, dtCount); dt; ++dt) {
  1205.         auto type = std::lower_bound(types_.begin(), types_.end(), dt.get<dtprop::name>());
  1206.         switch (type->typeSpecs.index()) {
  1207.             default:
  1208.                 assert(false);
  1209.             case adsData::datatypeInfo::baseSpec:
  1210.                 break;
  1211.             case adsData::datatypeInfo::compoundSpec: {
  1212.                 auto it = std::lower_bound(types_.begin(), types_.end(), dt.get<dtprop::type>());
  1213.                 static constexpr auto pt_str = "POINTER TO "sv;
  1214.                 if (std::string_view{ type->name }.compare(0, pt_str.size(), pt_str) == 0) // starts_with
  1215.                     type->isPointer = true;
  1216.                 ASSERT_THROW(type->isPointer || dt.get<dtprop::size>() == it->size);
  1217.                 std::get<adsData::datatypeInfo::compoundSpec>(type->typeSpecs) = &*it;
  1218.                 break;
  1219.             }
  1220.             case adsData::datatypeInfo::arraySpec: {
  1221.                 auto it = std::lower_bound(types_.begin(), types_.end(), dt.get<dtprop::type>());
  1222.                 auto& info = std::get<adsData::datatypeInfo::arraySpec>(type->typeSpecs);
  1223.                 ASSERT_THROW("Datatypes corrupted: mismatched element size" && dt.get<dtprop::size>() / info.numElements() == it->size);
  1224.                 info.typeData = &*it;
  1225.                 break;
  1226.             }
  1227.             case adsData::datatypeInfo::classSpec: {
  1228.                 refDT sub = dt.get<dtprop::subItemData>();
  1229.                 for (auto& info : std::get<adsData::datatypeInfo::classSpec>(type->typeSpecs)) {
  1230.                     auto it = std::lower_bound(types_.begin(), types_.end(), sub.get<dtprop::type>());
  1231.                     ASSERT_THROW("Datatypes corrupted: mismatched size" && sub.get<dtprop::size>() == it->size);
  1232.                     info.typeData = &*it;
  1233.                     ++sub;
  1234.                 }
  1235.                 break;
  1236.             }
  1237.         }
  1238.     }
  1239.     // make sure we didn't create a cycle, which should never happen,
  1240.     // an alias cannot refer to itself and a structure cannot contain itself
  1241.     for (auto& info : types_)
  1242.         cycleCheck(&info);
  1243.    
  1244.     symbols_.reserve(symCount);
  1245.     for (refSym sym(&symData[0], by_count{}, symCount); sym; ++sym) {
  1246.         using prop = refSym::prop_type;
  1247.         auto it = std::lower_bound(types_.begin(), types_.end(), sym.get<prop::type>());
  1248.         ASSERT_THROW("Symboldata corrupted: mismatched size" && sym.get<prop::size>() == it->size);
  1249.         ASSERT_THROW("mismatched guid" && sym.get<prop::guid>() == it->guid);
  1250.         symbols_.emplace_back(sym, &*it);
  1251.     }
  1252.     std::sort(symbols_.begin(), symbols_.end());
  1253. }
  1254.  
  1255. adsVarData adsData::operator[](std::string_view name) const {
  1256.     auto curName = name;
  1257.     if (auto pos = curName.find('['); pos != curName.npos)
  1258.         curName.remove_suffix(curName.size() - pos);
  1259.     auto sym = std::lower_bound(symbols_.begin(), symbols_.end(), curName);
  1260.     if (sym == symbols_.end())
  1261.         throw std::out_of_range("var not found: "sv + name);
  1262.     auto group = sym->group;
  1263.     auto offset = sym->baseInfo.offset;
  1264.     if (name.size() == sym->baseInfo.name.size())
  1265.         return adsVarData{ &sym->baseInfo, group, offset };
  1266.     curName = std::string_view{ name.data() + sym->baseInfo.name.size(), name.size() - sym->baseInfo.name.size() };
  1267.     if (curName[0] != '.' && curName[0] != '[')
  1268.         throw std::out_of_range("var not found: "sv + name);
  1269.     auto dt = followAlias(sym->baseInfo.typeData);
  1270.     for (;;) {
  1271.         if (curName[0] == '.') {
  1272.             if (dt->typeSpecs.index() != adsData::datatypeInfo::classSpec)
  1273.                 throw std::out_of_range("has no subobjects: "s
  1274.                     + std::string_view{ name.data(), static_cast<std::size_t>(curName.data() - name.data()) }
  1275.                     + " with "sv + curName);
  1276.             curName.remove_prefix(1); // '.'
  1277.             auto shortName = curName;
  1278.             auto pos = shortName.find_first_of(".["sv);
  1279.             if (pos != shortName.npos) {
  1280.                 shortName.remove_suffix(shortName.size() - pos);
  1281.             }
  1282.             auto& subs = std::get<adsData::datatypeInfo::classSpec>(dt->typeSpecs);
  1283.             auto sub = std::find_if(subs.begin(), subs.end(), [=](auto& v) { return icmp_equal(shortName, v.name); });
  1284.             if (sub == subs.end())
  1285.                 throw std::out_of_range("subobjects not found: "s + shortName + " of "sv
  1286.                     + std::string_view{ name.data(), static_cast<std::size_t>(curName.data() - name.data()) }
  1287.                     + " with "sv + name);
  1288.             if (sub->isStatic)
  1289.                 offset = sub->offset;
  1290.             else
  1291.                 offset += sub->offset;
  1292.             if (pos == shortName.npos)
  1293.                 return adsVarData{ &*sub, group, offset };
  1294.             curName.remove_prefix(pos);
  1295.             dt = followAlias(sub->typeData);
  1296.         } else {
  1297.             // cur_name[0] == '['
  1298.             if (dt->typeSpecs.index() != adsData::datatypeInfo::arraySpec)
  1299.                 throw std::out_of_range("is no array: "s
  1300.                     + std::string_view{ name.data(), static_cast<std::size_t>(curName.data() - name.data()) }
  1301.             +" with "sv + curName);
  1302.             curName.remove_prefix(1); // '['
  1303.             auto pos = curName.find(']');
  1304.             if (pos == curName.npos)
  1305.                 throw std::out_of_range("missing ]");
  1306.             auto index = curName;
  1307.             index.remove_suffix(index.size() - pos);
  1308.             auto& info = std::get<adsData::datatypeInfo::arraySpec>(dt->typeSpecs);
  1309.             auto i = info.index(index);
  1310.             offset += info.elemSize() * i;
  1311.             curName.remove_prefix(pos + 1); // "index]"
  1312.             if (curName.empty())
  1313.                 return adsVarData{ &info, group, offset, i };
  1314.             if (curName[0] != '[' && curName[0] != '.')
  1315.                 throw std::out_of_range("missing . or [");
  1316.             dt = followAlias(info.typeData);
  1317.         }
  1318.     }
  1319. }
  1320.  
  1321. adsData::iterator adsData::begin() const noexcept {
  1322.     return iterator{ symbols_.begin() };
  1323. }
  1324. adsData::iterator adsData::end() const noexcept {
  1325.     return iterator{ symbols_.end() };
  1326. }
  1327. adsData::iterator adsData::cbegin() const noexcept {
  1328.     return begin();
  1329. }
  1330. adsData::iterator adsData::cend() const noexcept {
  1331.     return end();
  1332. }
  1333. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1334. std::string adsData::enumInfo::valueToString() const {
  1335.     return std::visit([](const auto& value) -> std::string {
  1336.             if constexpr (std::is_arithmetic_v<std::decay_t<decltype(value)>>)
  1337.                 return std::to_string(readRaw<std::decay_t<decltype(value)>>(reinterpret_cast<const char*>(&value))); // TODO
  1338.             else
  1339.                 return value;
  1340.         }, second);
  1341. }
  1342. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1343. std::string adsVarData::name(std::string prefix) const {
  1344.     if (!info_->name.empty()) {
  1345.         prefix.reserve(prefix.size() + 1 + info_->name.size());
  1346.         prefix += '.';
  1347.         prefix += info_->name;
  1348.         return std::move(prefix);
  1349.     } else
  1350.         return std::move(prefix) + shortName();
  1351. }
  1352. std::string adsVarData::shortName() const {
  1353.     return info_->name.empty() ? static_cast<const adsData::arrayInfo*>(info_)->toString(index_) : info_->name;
  1354. }
  1355. const std::string& adsVarData::type() const noexcept {
  1356.     return info_->typeData->name;
  1357. }
  1358. const std::string& adsVarData::comment() const noexcept {
  1359.     return info_->comment;
  1360. }
  1361. adsDatatypeId adsVarData::typeId() const noexcept {
  1362.     return info_->typeData->id;
  1363. }
  1364. sizeType adsVarData::group() const noexcept {
  1365.     return group_;
  1366. }
  1367. sizeType adsVarData::offset() const noexcept {
  1368.     return offset_;
  1369. }
  1370. sizeType adsVarData::size() const noexcept {
  1371.     return info_->typeData->size;
  1372. }
  1373. bool adsVarData::isStatic() const noexcept {
  1374.     return info_->isStatic;
  1375. }
  1376. const GUID* adsVarData::typeGuid() const noexcept {
  1377.     return info_->typeData->guid.has_value() ? &*info_->typeData->guid : nullptr;
  1378. }
  1379.  
  1380. const ustring& adsVarData::copyMask() const noexcept {
  1381.     return info_->typeData->copyMask;
  1382. }
  1383. const std::vector<adsData::attributeInfo>& adsVarData::attributes() const noexcept {
  1384.     return info_->attributes;
  1385. }
  1386. const std::vector<adsData::attributeInfo>& adsVarData::typeAttributes() const noexcept {
  1387.     return info_->typeData->attributes;
  1388. }
  1389. const std::vector<adsData::enumInfo>& adsVarData::typeEnums() const noexcept {
  1390.     return info_->typeData->enums;
  1391. }
  1392.  
  1393.  
  1394. bool adsVarData::isScalar() const noexcept {
  1395.     auto real_type = followAlias(info_->typeData);
  1396.     return real_type->typeSpecs.index() == adsData::datatypeInfo::baseSpec
  1397.         || real_type->typeSpecs.index() == adsData::datatypeInfo::compoundSpec;
  1398. }
  1399. bool adsVarData::isPointer() const noexcept {
  1400.     return followAlias(info_->typeData)->isPointer;
  1401. }
  1402. bool adsVarData::isArray() const noexcept {
  1403.     return followAlias(info_->typeData)->typeSpecs.index() == adsData::datatypeInfo::arraySpec;
  1404. }
  1405. bool adsVarData::isStruct() const noexcept {
  1406.     return followAlias(info_->typeData)->typeSpecs.index() == adsData::datatypeInfo::classSpec;
  1407. }
  1408. bool adsVarData::empty() const noexcept {
  1409.     auto real_type = followAlias(info_->typeData);
  1410.     return isScalar() || isStruct() && std::get<adsData::datatypeInfo::classSpec>(real_type->typeSpecs).empty();
  1411. }
  1412.  
  1413. sizeType adsVarData::subElements() const noexcept {
  1414.     auto real_type = followAlias(info_->typeData);
  1415.     switch (real_type->typeSpecs.index()) {
  1416.         default:
  1417.             assert(false);
  1418.         case adsData::datatypeInfo::baseSpec:
  1419.         case adsData::datatypeInfo::compoundSpec:
  1420.             return 0;
  1421.         case adsData::datatypeInfo::arraySpec:
  1422.             return std::get<adsData::datatypeInfo::arraySpec>(real_type->typeSpecs).numElements();
  1423.         case adsData::datatypeInfo::classSpec:
  1424.             return static_cast<sizeType>(std::get<adsData::datatypeInfo::classSpec>(real_type->typeSpecs).size());
  1425.     }
  1426. }
  1427.  
  1428. adsVarData::iterator adsVarData::begin() const noexcept {
  1429.     auto real_type = followAlias(info_->typeData);
  1430.     switch (real_type->typeSpecs.index()) {
  1431.         default:
  1432.             assert(false);
  1433.         case adsData::datatypeInfo::baseSpec:
  1434.         case adsData::datatypeInfo::compoundSpec:
  1435.             return iterator{ nullptr, 0, 0, 0 };
  1436.         case adsData::datatypeInfo::arraySpec: {
  1437.             auto& info = std::get<adsData::datatypeInfo::arraySpec>(real_type->typeSpecs);
  1438.             return iterator{ &info, group_, offset_, 0 };
  1439.         }
  1440.         case adsData::datatypeInfo::classSpec: {
  1441.             auto& info = std::get<adsData::datatypeInfo::classSpec>(real_type->typeSpecs);
  1442.             return iterator{ info.empty() ? nullptr : &info.front(), group_, offset_, 0 };
  1443.         }
  1444.     }
  1445. }
  1446. adsVarData::iterator adsVarData::end() const noexcept {
  1447.     auto real_type = followAlias(info_->typeData);
  1448.     switch (real_type->typeSpecs.index()) {
  1449.         default:
  1450.             assert(false);
  1451.         case adsData::datatypeInfo::baseSpec:
  1452.         case adsData::datatypeInfo::compoundSpec:
  1453.             return iterator{ nullptr, 0, 0, 0 };
  1454.         case adsData::datatypeInfo::arraySpec: {
  1455.             auto& info = std::get<adsData::datatypeInfo::arraySpec>(real_type->typeSpecs);
  1456.             return iterator{ &info, group_, offset_, info.numElements() };
  1457.         }
  1458.         case adsData::datatypeInfo::classSpec: {
  1459.             auto& info = std::get<adsData::datatypeInfo::classSpec>(info_->typeData->typeSpecs);
  1460.             return iterator{ info.empty() ? nullptr : &info.back() + 1, group_, offset_, 0 };
  1461.         }
  1462.     }
  1463. }
  1464. adsVarData::iterator adsVarData::cbegin() const noexcept {
  1465.     return begin();
  1466. }
  1467. adsVarData::iterator adsVarData::cend() const noexcept {
  1468.     return end();
  1469. }
  1470.  
  1471. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1472. adsData::iterator::reference adsData::iterator::operator*() const noexcept {
  1473.     return adsVarData{ &iter_->baseInfo, iter_->group, iter_->baseInfo.offset };
  1474. }
  1475. adsData::iterator::pointer adsData::iterator::operator->() const noexcept {
  1476.     return **this;
  1477. }
  1478. bool operator==(adsData::iterator lhs, adsData::iterator rhs) noexcept {
  1479.     return lhs.iter_ == rhs.iter_;
  1480. }
  1481. bool operator!=(adsData::iterator lhs, adsData::iterator rhs) noexcept {
  1482.     return lhs.iter_ != rhs.iter_;
  1483. }
  1484. adsData::iterator& adsData::iterator::operator++() noexcept {
  1485.     ++iter_;
  1486.     return *this;
  1487. }
  1488. adsData::iterator adsData::iterator::operator++(int) noexcept {
  1489.     auto tmp = *this;
  1490.     ++*this;
  1491.     return tmp;
  1492. }
  1493. adsData::iterator::iterator(std::vector<adsData::symbolInfo>::const_iterator it) noexcept
  1494.     : iter_{ it }
  1495. {}
  1496.  
  1497. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1498. adsVarData::iterator::reference adsVarData::iterator::operator*() const noexcept {
  1499.     if (info_->name.empty())
  1500.         return adsVarData{ info_, group_, offset_ + index_ * info_->typeData->size, index_ };
  1501.     else
  1502.         return adsVarData{ info_, group_, info_->isStatic ? offset_ + info_->offset : info_->offset };
  1503. }
  1504. adsVarData::iterator::pointer adsVarData::iterator::operator->() const noexcept {
  1505.     return **this;
  1506. }
  1507. bool operator==(const adsVarData::iterator& lhs, const adsVarData::iterator& rhs) noexcept {
  1508.     return lhs.info_ == rhs.info_ && lhs.index_ == rhs.index_
  1509.         && lhs.offset_ == rhs.offset_ && lhs.group_ == rhs.group_;
  1510. }
  1511. bool operator!=(const adsVarData::iterator& lhs, const adsVarData::iterator& rhs) noexcept {
  1512.     return !(lhs == rhs);
  1513. }
  1514. adsVarData::iterator& adsVarData::iterator::operator++() noexcept {
  1515.     if (info_->name.empty())
  1516.         ++index_;
  1517.     else
  1518.         ++info_;
  1519.     return *this;
  1520. }
  1521. adsVarData::iterator adsVarData::iterator::operator++(int) noexcept {
  1522.     auto tmp = *this;
  1523.     ++*this;
  1524.     return tmp;
  1525. }
  1526. adsVarData::iterator::iterator(const adsData::subSymbolInfo* info, sizeType group, sizeType offset, sizeType index) noexcept
  1527.     : info_{ info }, group_{ group }, offset_{ offset }, index_{ index }
  1528. {}
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement