Advertisement
Guest User

Untitled

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