Advertisement
Guest User

Untitled

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