Speicherverbrauch shared_ptr und normaler *



  • Welchen Typ hat TcAdsSymbolInfo::_parent?



  • TcAdsSymbolInfo::_parent ist aktuell vom typ weak_ptr<TcAdsSymbolInfo>.

    TcAdsSymbolInfo ist eigentlich nur eine wrapper klasse um AdsSymbolInfo
    die ich dann in meinen Collections abspeichere

    class TcAdsSymbolInfo
    {
    
        /// Zeiger auf erstes Symbol
        weak_ptr<TcAdsSymbolInfo> _firstSubSymbol;
    
        /// Zeiger auf nächstes Symbol
        weak_ptr<TcAdsSymbolInfo> _nextSymbol;
    
        /// Zeiger auf Elternsymbol
        weak_ptr<TcAdsSymbolInfo> _parent;
    
        /// Zeiger auf Untersymbole
        shared_ptr<TcAdsSymbolInfoCollection> _subSymbols;
    
        /// Struktur mit Basisinformationen
        ITcAdsSymbol _baseInfos;
    
        void AssignAdsSymbolInfo(AdsSymbolInfo &info, const int subSymbolCount )
        {
            _baseInfos.comment          = info.comment;
            _baseInfos.datatype         = AdsDatatypeId::_from_integral(info.dataType);
            _baseInfos.indexGroup       = info.iGrp;
            _baseInfos.indexOffset	= info.iOffs;
            _baseInfos.name		= info.fullname;
            _baseInfos.shortName        = info.name;
            _baseInfos.size		= info.size;
            _baseInfos.type		= info.type;
            _baseInfos.subSymbolCount   = subSymbolCount;
        }
    }
    


  • Schau dir nochmal das Beispiel von weak_ptr hier an http://en.cppreference.com/w/cpp/memory/weak_ptr

    std::weak_ptr<int> gw;
    
    void observe()
    {
        std::cout << "use_count == " << gw.use_count() << ": ";
        if (auto spt = gw.lock()) { // Has to be copied into a shared_ptr before usage
    	std::cout << *spt << "\n";
        }
        else {
            std::cout << "gw is expired\n";
        }
    }
    
    int main()
    {
        {
            auto sp = std::make_shared<int>(42);
    	gw = sp;
    
    	observe();
        }
    
        observe();
    }
    

    Du läufst genau in die Falle, du erstellst mit make_shared in Zeile 59 einen shared ptr und weißt den in Zeile 64 oder 67 einem weak_ptr zu. Wenn der shared_ptr jetzt out of scope geht, ist der weak_ptr auch ungültig.



  • Ja ich erzeuge einen shared_ptr in Zeile 59 den ich dann in Zeile 83 und 84 meiner Collection zuweise. Das heißt die Collection ist dann der Besitzer des Objekts.

    der weak_ptr muss dann doch auch ungültig werden wenn das eigentliche objekt gelöscht wird. Das passiert ja erst wenn das Objekt aus der Collection raus fliegt.

    Oder nicht?



  • Oh, die Add() Funktion da hatte ich tatsächlich übersehen.
    Hängt natürlich von der Implementierung ab. Bei den Funktionen die du hier zeigst übergibst du immer eine Referenze auf einen shared_ptr. Das ist natürlich gut für die Performance, auf der anderen Seite verhinderst du damit, dass der reference count inkrementiert wird.
    Wie auch immer, dass kannst du recht einfach im Debugger oder mit einer Debug Ausgabe überprüfen und erklärt auch nicht, den von dir beschriebenen Speicherverbrauch deiner Anwendung.



  • Ja die Add Methode ist aber nicht const&

    void Add(shared_ptr<TcAdsSymbolInfo> symbolInfo)
    

  • Mod

    Wie verhalten sich das Programm+Log, wenn du

    static symbol_count = 0;
        PROCESS_MEMORY_COUNTERS mem_info;
        GetProcessMemoryInfo(GetCurrentProcess(), &mem_info, sizeof(mem_info));
        TRACE((to_string(++symbol_count) + " " + to_string(mem_info.WorkingSetSize)).c_str());
    

    vor dem return in CreateSubSymbols einfügst?
    Wieviel Speicher wird per Symbol im Schnitt benötigt?
    Ist der Speicherverbrauch pro Symbol (einigermaßen) konstant oder wächst er mit der Zeit?



  • @camper
    Habe das mit dem Visual Studio memoery profiler schon getestet.
    da hat jedes Symbol zwischen 0,6 - 3 K Byte
    das mal 1,4 Millionen Objekte kommt schon etwa hin mit den 2 GB.

    Aber ich teste das mit deinem Code schnipsel auch noch bevor wieder...



  • Ich meinte weniger den Aufruf von der Add Methode sonder eher das hinzufügen zur Map. Aber das ist ja im Moment auch egal. Wenn da was schief läuft, fliegt dir die Software zur Laufzeit irgendwann um die Ohren und reserviert nicht 2 GB Speicher.

    Hast du mal Valgrind, oder eine entsprechende alternative laufen lassen. Bei dem externen Code, den du gezeigt hast, bin ich mir nicht sicher, dass da alles sauber läuft.



  • Ja bei dem externen Code bin ich mir auch nicht sicher. Der sieht wirklich graunhaft aus.

    Aber ich weiß noch nicht ob der mein Speicher verbraucht.

    Und wieso der Code nur 600KB braucht um die die 1,4 Millionen Datensätze auszulesen ist auch noch nicht ganz klar.



  • booster schrieb:

    @hustbear
    Bin unter Windows 7 und hier ist die Spalte Arbeitsspeicher (privater Arbeitssatz) eingeblendet!

    Ja. Das ist nicht die Spalte die du suchst. Die kann sich quasi beliebig ändern.

    Was du suchst ist "commit size". Ich weiss aber nicht wie die im Windows 7 Task Manager heisst. Und auch dann gibt's Pitfalls, nämlich dass der Heap freigegebenen Speicher nicht unbedingt ans System zurück gibt (bzw. das oft auch gar nicht kann), und dass AFAIK Programmcode sowie geladene DLLs mitgezählt werden.



  • @hustbear

    Würde mal sagen die commit size ist die "Zugesicherte Größe".



  • @camper

    Wie verhalten sich das Programm+Log, wenn du ... vor dem return in CreateSubSymbols einfügst?

    Also ich versuch mal zu interpretieren und hoffe ich bekomme nicht gleich wieder eine auf die Mütze, wie "ist ja alles inkonsistent" und du denkst ja gar nicht nach".

    Folgende Erkenntnise.
    WorkingSetSize nach 1. Aufruf von CreateSubSymbol:
    34631680 Bytes

    WorkingSetSize nach 500. Aufruf von CreateSubSymbol:
    35221504 Bytes

    Unterschied -> 589824 Bytes = 589KB

    Sprich nach 1,4 Millionen Symbolen
    kommt dann schon um die 2GB raus.



  • Also um noch die beiden Fragen zu beantworten

    Wieviel Speicher wird per Symbol im Schnitt benötigt?

    Also wenn ich noch weiterlaufen lasse und mehr als 500 Symbole betrachte um die
    1,5 KB

    Ist der Speicherverbrauch pro Symbol (einigermaßen) konstant oder wächst er mit der Zeit?

    Er ist einigermaßen konstant. Wächst auf jeden Fall nicht mit der Zeit.


  • Mod

    Wieviele von den 1,4 Millionen Symbolen sind Array-Elemente (der Datenblock enthält nur Informationen über Arraydimensionen, das Programm erzeugt aber für jedes einzelne Array-Element einen Symboleintrag) ?
    Ich habe ja keine Ahnung, was mit den Daten später gemacht werden soll, allerdings erscheint es mir relativ sinnlos, für jedes einzelne Element eines Arrays Informationen zu speichern, die sowieso identisch mit dem übergeordneten Symbol sind. Das würde auch erklären, wie ein 682KB großer Datenblock sich überhaupt auf so viele Elemente beziehen kann.



  • Hi camper.

    Du bist aktuell so weit wie ich :-).

    Dies gilt aber nicht nur für Array -Elemente sondern auch für die Instanzen von Strukturen.

    Die Daten werden dazu gebraucht um Zugriffsfunktionalität herzustellen um auf bestimmte Variablen der SPS direkt zugreifen zu können.

    Ok also praktisches Beipiel:

    Ich bekomme eine Liste von Variablennamen.
    z.B

    "Main.FunctionBlockComplex.FirstStruct.States[5].ErrorState"

    Zu dieser Variable muss ich nun die benötigten Informationen (Indexgroup, IndexOffest, Size, DataType usw.) einholen um dann später diese Variable direkt lesen bzw. schreiben zu können.

    Jetzt würde die Möglichkeit bestehen, nur die Variablendeklaration dieser einen Variablen auszulesen. Da es aber nicht nur eine Variable ist von der man die Informationen benötigt war die Idee alle Variablendeklarationen auf einmal auszulesen. (um nur einmal die SPS zu kontaktieren) Diese in einer Struktur zu speichern um schnellen Zugriff auf die Variablendeklartionen zu haben.
    Nach dem ich mir dann meine eigenen Klasseninstanzen der benötigten Variablen erzeugt habe kann ich diese "Lookup Tabelle" wieder löschen.

    Das hat bisher wie gesagt auch sehr gut funktioniert. Die Lookup Tabelle wird ja nur einmal ab Start erzeugt und danach wieder frei gegeben.


  • Mod

    booster schrieb:

    "Main.FunctionBlockComplex.FirstStruct.States[5].ErrorState

    Die Information, die für Arrayelemente gespeichert wird, ist für alle Elemente identisch. Es ist völlig absurd, die absolut gleiche Information mehrfach zu speichern, solange nur das Array groß genug ist, wird dir immer am Ende der Speicher ausgehen, unabhängig von allen anderen Versuchen, möglichst wenig Speicher zu verbruachen.

    Wie wäre es mit folgender Änderung

    UINT	CAdsParseSymbols::SubSymbolCount(PAdsDatatypeEntry	pEntry)
    ...		else if ( pEntry->arrayDim )
    		{
    			cnt = 1;
    		}
    ...
    
    BOOL	CAdsParseSymbols::SubSymbolInfo(CAdsSymbolInfo main, UINT sub, CAdsSymbolInfo& info)
    ...
    		else if ( pEntry->arrayDim )
    		{
                            // store one generic element representing any array elements
    
    			info.iGrp	= main.iGrp;
    			info.iOffs	= main.iOffs;
    			info.size	= baseSize;
    			info.dataType	= pEntry->dataType;
    			info.flags	= pEntry->flags;
    			info.type	= PADSDATATYPETYPE(pEntry);
    			info.comment	= PADSDATATYPECOMMENT(pEntry);
    			info.name	= main.name + "[]";
    			info.fullname	= main.fullname + "[]";
    			return TRUE;
    		}
    ...
    

    Bevor jetzt ein Lookup durchgeführt wird, muss nur zunächst geprüft werden, ob auf ein Arrayelement zugegriffen wird (name enthält Indexinformationen zwischen "[]"), dann muss dieser name entsprechend angepasst werden, indem die Indexinformationen gelöscht werden. Im Anschluss ggf. noch überprüfen, ob der Index innerhalb des Arrays liegt.



  • @camper

    Wenn ich den Code richtig lese, haben Arrayelemente nie selbst Subelemente.

    Also die haben schon Subelemente aber jeder Index halt die selben.
    Also ich kann ja ein Array mit Structs haben.

    Und die Information, die für Arrayelemente gespeichert wird, ist für alle Elemente identisch.

    Für das Oberste Element dann schon. Bis auf den IndexOffset der ist bei jedem Element anderes.

    Den muss ich dann halt noch berechnen. Also Offset von Array selbst + Arrayindex * Sizeof(Array Datatype)


  • Mod

    booster schrieb:

    Ich bekomme eine Liste von Variablennamen.
    z.B

    "Main.FunctionBlockComplex.FirstStruct.States[5].ErrorState"

    Zu dieser Variable muss ich nun die benötigten Informationen (Indexgroup, IndexOffest, Size, DataType usw.) einholen um dann später diese Variable direkt lesen bzw. schreiben zu können.

    Jetzt würde die Möglichkeit bestehen, nur die Variablendeklaration dieser einen Variablen auszulesen. Da es aber nicht nur eine Variable ist von der man die Informationen benötigt war die Idee alle Variablendeklarationen auf einmal auszulesen. (um nur einmal die SPS zu kontaktieren) Diese in einer Struktur zu speichern um schnellen Zugriff auf die Variablendeklartionen zu haben.
    Nach dem ich mir dann meine eigenen Klasseninstanzen der benötigten Variablen erzeugt habe kann ich diese "Lookup Tabelle" wieder löschen.

    Mir scheint dein Algorithmus arbeitet genau falschherum. Da du bereits eine vollständige Liste aller gesuchten Variablen hast, wäre es doch sinnvoller, beim Iterieren über die SPS-Daten direkt nur die Daten zu kopieren, die benötigt werden, anstatt erst eine umständliche Datenstruktur aller potentiell möglichen Variablen zu erstellen. Damit wird gar nicht erst unnötiger Speicher für Symbole verbraucht, die sowieso am Ende nicht benötigt werden, ausserdem kann der Parser von vornherein Subsymbole übergehen, die nicht benötigt werden.

    Die Aufgabe besteht also sinngemäß darin eine Funktion
    void update_from_sps_data(std::vector<ITcAdsSymbol>& vars, const vector<char>& sym_data, const vector<char>& dt_data)
    zu schreiben, wobei eingangs in vars die Namen der gesuchten Variablen bereits initialisiert sind, und die Funktion die restlichen Informationen der einzelnen vars-Elemente anhand der übergebenen Rohdaten aktualisiert?

    In dem Fall solltest du wahrscheinlich den Beispielcode wegwerfen und neuschreiben.
    Wenn du ein paar Beispieldaten zum runterladen und testen bereitstelltst, versuche ich mich evtl. auch selbst daran.


  • Mod

    Compiliert (g++/clang++ unter Linux, deshalb die paar Zeilen am Anfang) als C++17, aber nat. völlig ungetest:
    Fast keine dynamische Allokation (lediglich zwei kleine Vektoren und die string-Zuweisungen am Ende).

    #ifndef __MSC_VER
    #include <cstddef>
    using __int64 = long long;
    using BOOL = int;
    using PCHAR = char*;
    #define __stdcall
    #define __declspec(x)
    #endif
    #include "TcAdsDef.h" // https://raw.githubusercontent.com/bgstaal/ofxBeckhoffADS/master/include/TcAdsDef.h
    
    //////////////////////////////////////////////////////
    #include <algorithm>
    #include <cstddef>
    #include <cstdint>
    #include <cstring>
    #include <iostream>
    #include <string>
    #include <string_view>
    #include <stdexcept>
    #include <type_traits>
    #include <vector>
    using namespace std::literals::string_view_literals;
    
    enum class AdsDatatypeId;
    struct ITcAdsSymbol
    {
        std::string                           name;
        std::string                           shortName;
        std::string                           type;
        std::string                           comment;
        decltype(AdsSymbolEntry::size)        size;
        decltype(AdsSymbolEntry::iGroup)      indexGroup;
        decltype(AdsSymbolEntry::iOffs)       indexOffset;
        AdsDatatypeId                         datatype;       //enum
    };
    
    template <typename T, std::enable_if_t<std::is_trivial_v<T>, int> = 0>
    T read_raw(const char* p) {
        T res;
        std::memcpy(&res, p, sizeof res);
        return res;
    }
    
    std::size_t checked_sum(std::initializer_list<std::size_t> args) {
        std::size_t result = 0;
        for ( auto arg : args ) {
            auto sum = result + arg;
            if ( sum < result )
                throw std::runtime_error("overflow");
            result = sum;
        }
        return result;
    }
    
    class array_info {
        using size_type = decltype(AdsSymbolEntry::iOffs);
    public:
        array_info(const char* dim_data, size_type dims, size_type size) noexcept
        : dim_data(dim_data), dims(dims), size(size)
        {}
        array_info(const array_info&) noexcept = default;
        array_info(array_info&&) noexcept = default;
        array_info& operator=(const array_info&) noexcept = default;
        array_info& operator=(array_info&&) noexcept = default;
        ~array_info() noexcept = default;
        auto elem_info(std::string_view index) const {
            size_type real_index = 0;
            auto elem_size = size;
            for ( size_type i = 0; i != dims; ++i ) {
                auto pos = index.find(',');
                if ( ( i != dims - 1 ) == ( pos == index.npos ) )
                    throw std::runtime_error("index with wrong number of dimensions");
                auto cur_index = index;
                if ( pos != index.npos ) {
                    cur_index.remove_suffix(cur_index.size() - pos);
                    index.remove_prefix(pos + 1);
                }
                auto n = svtoi(cur_index);
                auto info = read_raw<AdsDatatypeArrayInfo>(dim_data + i * sizeof(AdsDatatypeArrayInfo));
                if ( n < info.lBound || n >= info.lBound + info.elements )
                    throw std::runtime_error("index out of range");
                real_index = real_index * info.elements + ( n - info.lBound );
                elem_size /= info.elements;
            }
            return std::pair(real_index * elem_size, elem_size);
        }
    private:
        static size_type svtoi(std::string_view s) {
            size_type result = 0;
            for ( ; !s.empty(); ) {
                if ( s[0] < '0' || s[0] > '9' )
                    throw std::runtime_error("index corrupted");
                result = result * 10 + ( s[0] - '0' );
                s.remove_prefix(1);
            }
            return result;
        }
        const char* dim_data;
        size_type dims;
        size_type size;
    };
    
    class adsDT {
    public:
        explicit adsDT(const char* raw_dt_data, bool check_size = true)
        : dt_data(raw_dt_data)
        {
            if ( check_size )
                check_item_size(this_len(dt_data), dt_data, 1);
            if ( PADSDATATYPENAME(dt_data)[name_len(dt_data)] != '\0'
              || PADSDATATYPETYPE(dt_data)[type_len(dt_data)] != '\0'
              || PADSDATATYPECOMMENT(dt_data)[comment_len(dt_data)] != '\0' )
                throw std::runtime_error("datatype corrupted: missing terminator");
            if ( strlen(name().data()) != name().length()
              || strlen(type().data()) != type().length()
              || strlen(comment().data()) != comment().length() )
                throw std::runtime_error("datatype corrupted: embedded \\0");
        }
        adsDT(const adsDT&) noexcept = default;
        adsDT(adsDT&&) noexcept = default;
        adsDT& operator=(const adsDT&) noexcept = default;
        adsDT& operator=(adsDT&&) noexcept = default;
        ~adsDT() noexcept = default;
        std::string_view name() const noexcept {
            return { PADSDATATYPENAME(dt_data), name_len(dt_data) };
        }
        std::string_view type() const noexcept {
            return { PADSDATATYPETYPE(dt_data), type_len(dt_data) };
        }
        std::string_view comment() const noexcept {
            return { PADSDATATYPECOMMENT(dt_data), comment_len(dt_data) };
        }
        auto offset() const noexcept {
           return read_raw<decltype(AdsDatatypeEntry::offs)>(dt_data + offsetof(AdsDatatypeEntry, offs));
        }
        auto size() const noexcept {
           return read_raw<decltype(AdsDatatypeEntry::size)>(dt_data + offsetof(AdsDatatypeEntry, size));
        }
        static const adsDT* find_by_name(const std::vector<adsDT>& data, const std::string_view& name) noexcept {
            auto it = std::lower_bound(data.begin(), data.end(), name,
                [](const adsDT& lhs, const std::string_view& rhs) { return lhs.name() < rhs; } );
            return it != data.end() && it->name() == name ? &*it : nullptr;
        }
        static std::size_t this_len(const char* data) noexcept {
             return read_raw<decltype(AdsDatatypeEntry::entryLength)>(data + offsetof(AdsDatatypeEntry, entryLength));
        }
        auto sub_symbols() const noexcept {
            return sub_items(dt_data);
        }
        const char* sub_symbol_data() const noexcept {
            return dt_data + sizeof(AdsDatatypeEntry) + 3 + name_len(dt_data) + type_len(dt_data) + comment_len(dt_data)
                           + array_dim(dt_data) * sizeof(AdsDatatypeArrayInfo);
        }
        array_info arr_info() const noexcept {
            return { dt_data + sizeof(AdsDatatypeEntry) + 3 + name_len(dt_data) + type_len(dt_data) + comment_len(dt_data),
                array_dim(dt_data), size() };
        }
    private:
        static std::size_t name_len(const char* data) noexcept {
            return read_raw<decltype(AdsDatatypeEntry::nameLength)>(data + offsetof(AdsDatatypeEntry, nameLength));
        }
        static std::size_t type_len(const char* data) noexcept {
            return read_raw<decltype(AdsDatatypeEntry::typeLength)>(data + offsetof(AdsDatatypeEntry, typeLength));
        }
        static std::size_t comment_len(const char* data) noexcept {
            return read_raw<decltype(AdsDatatypeEntry::commentLength)>(data + offsetof(AdsDatatypeEntry, commentLength));
        }
        static std::size_t array_dim(const char* data) noexcept {
            return read_raw<decltype(AdsDatatypeEntry::arrayDim)>(data + offsetof(AdsDatatypeEntry, arrayDim));
        }
        static std::size_t sub_items(const char* data) noexcept {
            return read_raw<decltype(AdsDatatypeEntry::subItems)>(data + offsetof(AdsDatatypeEntry, subItems));
        }
        static void check_item_size(std::size_t limit, const char* data, std::size_t count) {
            for ( ; count--; ) {
                if ( this_len(data) < sizeof(AdsDatatypeEntry) + 3 )
                    throw std::runtime_error("datatype corrupted: entryLength (1)");
                if ( this_len(data) > limit )
                    throw std::runtime_error("datatype corrupted: entryLength (2)");
                if ( count && limit - this_len(data) < sizeof(AdsDatatypeEntry) + 3 )
                    throw std::runtime_error("datatype corrupted: entryLength (3)");
                auto arrayinfo_offset = checked_sum({ sizeof(AdsDatatypeEntry), 3, name_len(data), type_len(data), comment_len(data) });
                if ( this_len(data) < arrayinfo_offset )
                    throw std::runtime_error("datatype corrupted: entryLength (4)");
                if ( ( this_len(data) - arrayinfo_offset ) / sizeof(AdsDatatypeArrayInfo) < array_dim(data) )
                    throw std::runtime_error("datatype corrupted: arrayDim");
                auto subitems_offset = arrayinfo_offset + array_dim(data) * sizeof(AdsDatatypeArrayInfo);
                if ( this_len(data) < subitems_offset )
                    throw std::runtime_error("datatype corrupted: entryLength (5)");
                if ( ( this_len(data) - subitems_offset ) / (sizeof(AdsDatatypeEntry) + 3) < sub_items(data) )
                    throw std::runtime_error("datatype corrupted: subItems");
                check_item_size(this_len(data) - subitems_offset, data + subitems_offset, sub_items(data));
                limit -= this_len(data);
                data += this_len(data);
            }
            if ( limit != 0 )
                throw std::runtime_error("datatype corrupted: unused memory");
        }
        const char* dt_data;
    };
    
    std::vector<adsDT> parse_dt_data(const std::vector<char>& dt_data) {
        std::vector<adsDT> result;
        auto len = dt_data.size();
        if ( len == 0 )
            return result;
        std::size_t dt_count = 0;
        for ( auto p = &dt_data[0]; len != 0; ) {
            if ( len < sizeof(AdsDatatypeEntry) + 3)
                throw std::runtime_error("datatypes corrupted (1)");
            auto cur_len = adsDT::this_len(p);
            if ( cur_len < sizeof(AdsDatatypeEntry) + 3 || cur_len > len )
                throw std::runtime_error("datatypes corrupted (2)");
            ++dt_count;
            p += cur_len;
            len -= cur_len;
        }
        result.reserve(dt_count);
        for ( auto p = &dt_data[0]; dt_count--; ) {
            result.emplace_back(p);
            p += adsDT::this_len(p);
        }
        // should be sorted & unique
        if ( std::adjacent_find(result.begin(), result.end(),
                 [](const adsDT& lhs, const adsDT& rhs) { return !(lhs.name() < rhs.name()); } ) != result.end() )
            throw std::runtime_error("datatypes not sorted or not unique");
        return result;
    }
    
    class adsSym {
    public:
        explicit adsSym(const char* raw_sym_data)
        : sym_data(raw_sym_data)
        {
            check_item_size(sym_data);
            if ( PADSSYMBOLNAME(sym_data)[name_len(sym_data)] != '\0'
              || PADSSYMBOLTYPE(sym_data)[type_len(sym_data)] != '\0'
              || PADSSYMBOLCOMMENT(sym_data)[comment_len(sym_data)] != '\0' )
                throw std::runtime_error("Symbol corrupted: missing terminator");
            if ( strlen(name().data()) != name().length()
              || strlen(type().data()) != type().length()
              || strlen(comment().data()) != comment().length() )
                throw std::runtime_error("Symbol corrupted: embedded \\0");
            if ( name().find_first_of(".["sv) != name().npos )
                throw std::runtime_error("Symbol name corrupted: embedded . or [");
        }
        adsSym(const adsSym&) noexcept = default;
        adsSym(adsSym&&) noexcept = default;
        adsSym& operator=(const adsSym&) noexcept = default;
        adsSym& operator=(adsSym&&) noexcept = default;
        ~adsSym() noexcept = default;
        std::string_view name() const noexcept {
            return { PADSSYMBOLNAME(sym_data), name_len(sym_data) };
        }
        std::string_view type() const noexcept {
            return { PADSSYMBOLTYPE(sym_data), type_len(sym_data) };
        }
        std::string_view comment() const noexcept {
            return { PADSSYMBOLCOMMENT(sym_data), comment_len(sym_data) };
        }
        auto size() const noexcept {
            return read_raw<decltype(AdsSymbolEntry::size)>(sym_data + offsetof(AdsSymbolEntry, size));
        }
        auto indexGroup() const noexcept {
            return read_raw<decltype(AdsSymbolEntry::iGroup)>(sym_data + offsetof(AdsSymbolEntry, iGroup));
        }
        auto indexOffset() const noexcept {
            return read_raw<decltype(AdsSymbolEntry::iOffs)>(sym_data + offsetof(AdsSymbolEntry, iOffs));
        }
        static const adsSym* find_by_name(const std::vector<adsSym>& data, const std::string_view& name) noexcept {
            auto it = std::lower_bound(data.begin(), data.end(), name,
                [](const adsSym& lhs, const std::string_view& rhs) { return lhs.name() < rhs; } );
            return it != data.end() && it->name() == name ? &*it : nullptr;
        }
        static std::size_t this_len(const char* data) noexcept {
             return read_raw<decltype(AdsSymbolEntry::entryLength)>(data + offsetof(AdsSymbolEntry, entryLength));
        }
    private:
        static std::size_t name_len(const char* data) noexcept {
            return read_raw<decltype(AdsSymbolEntry::nameLength)>(data + offsetof(AdsSymbolEntry, nameLength));
        }
        static std::size_t type_len(const char* data) noexcept {
            return read_raw<decltype(AdsSymbolEntry::typeLength)>(data + offsetof(AdsSymbolEntry, typeLength));
        }
        static std::size_t comment_len(const char* data) noexcept {
            return read_raw<decltype(AdsSymbolEntry::commentLength)>(data + offsetof(AdsSymbolEntry, commentLength));
        }
        static void check_item_size(const char* data) {
            if ( this_len(data) < sizeof(AdsSymbolEntry) + 3 )
                throw std::runtime_error("Symbol corrupted (1)");
            if ( this_len(data) != checked_sum({ sizeof(AdsSymbolEntry), 3, name_len(data), type_len(data), comment_len(data) }) )
                throw std::runtime_error("Symbol corrupted (2)");
        }
        const char* sym_data;
    };
    
    std::vector<adsSym> parse_sym_data(const std::vector<char>& sym_data) {
        std::vector<adsSym> result;
        auto len = sym_data.size();
        if ( len == 0 )
            return result;
        std::size_t sym_count = 0;
        for ( auto p = &sym_data[0]; len != 0; ) {
            if ( len < sizeof(AdsSymbolEntry) + 3)
                throw std::runtime_error("symboldata corrupted (1)");
            auto cur_len = adsSym::this_len(p);
            if ( cur_len < sizeof(AdsSymbolEntry) + 3 || cur_len > len )
                throw std::runtime_error("symboldata corrupted (2)");
            ++sym_count;
            p += cur_len;
            len -= cur_len;
        }
        result.reserve(sym_count);
        for ( auto p = &sym_data[0]; sym_count--; ) {
            result.emplace_back(p);
            p += adsSym::this_len(p);
        }
        // should be sorted & unique
        if ( std::adjacent_find(result.begin(), result.end(),
                 [](const adsSym& lhs, const adsSym& rhs) { return !(lhs.name() < rhs.name()); } ) != result.end() )
            throw std::runtime_error("symbols not sorted or not unique");
        return result;
    }
    
    template <typename T, typename std::enable_if_t<std::is_same_v<T, adsSym> || std::is_same_v<T, adsDT>, int> = 0>
    void update_var(ITcAdsSymbol& var, const T& data,
                    decltype(ITcAdsSymbol::indexGroup) group,
                    decltype(ITcAdsSymbol::indexOffset) offset,
                    decltype(AdsSymbolEntry::size) size) {
       	var.shortName = data.name();
       	var.type = data.type();
       	var.comment = data.comment();
       	var.size = size;
       	var.indexGroup = group;
       	var.indexOffset = offset;
       	//var.datatype =
    }
    
    void update_from_sps_data(std::vector<ITcAdsSymbol>& vars, const std::vector<char>& sym_data, const std::vector<char>& dt_data) {
        auto datatypes = parse_dt_data(dt_data);
        auto symbols = parse_sym_data(sym_data);
        for ( auto& var : vars ) {
            std::string_view cur_name = var.name;
            auto sym_name = cur_name;
            auto pos = sym_name.find_first_of(".[");
            if ( pos != sym_name.npos )
                sym_name.remove_suffix(sym_name.size() - pos);
            auto sym = adsSym::find_by_name(symbols, sym_name);
            if ( !sym )
                throw std::runtime_error("var not found");
            auto group = sym->indexGroup();
            auto offset = sym->indexOffset();
            if ( pos == cur_name.npos ) {
                update_var(var, *sym, group, offset, sym->size());
                continue;
            }
            auto type_name = sym->type();
            for ( ;; ) {
                auto dt = adsDT::find_by_name(datatypes, type_name);
                if ( !dt )
                    throw std::runtime_error("type not found");
                cur_name.remove_prefix(pos);
                if ( cur_name[0] == '.' ) {
                    cur_name.remove_prefix(1);
                    auto short_name = cur_name;
                    pos = short_name.find_first_of(".[");
                    if ( pos != short_name.npos ) {
                        short_name.remove_suffix(short_name.size() - pos);
                    }
                    // check all subsymbols in order
                    auto sub = dt->sub_symbol_data();
                    std::size_t n = 0;
                    for ( ; n != dt->sub_symbols(); ++n ) {
                        if ( adsDT(sub, false).name() == short_name )
                            break;
                    }
                    if ( n == dt->sub_symbols() )
                        throw std::runtime_error("subsymbol not found");
                    adsDT sub_item(sub, false);
                    offset += sub_item.offset();
                    if ( pos == short_name.npos ) {
                        update_var(var, sub_item, group, offset, sub_item.size());
                        break;
                    }
                    type_name = sub_item.type();
                } else {
                    // cur_name[0] == '['
                    cur_name.remove_prefix(1);
                    pos = cur_name.find("]");
                    if ( pos == cur_name.npos )
                        throw std::runtime_error("missing ]");
                    auto index = cur_name;
                    index.remove_suffix(index.size() - pos);
                    auto [elem_offset, elem_size] = dt->arr_info().elem_info(index);
                    offset += elem_offset;
                    cur_name.remove_prefix(pos + 1);
                    if ( cur_name.empty() ) {
                        update_var(var, *dt, group, offset, elem_size);
                        break;
                    }
                    if ( cur_name[0] != '[' && cur_name[0] != '.' )
                        throw std::runtime_error("missing . or [");
                    pos = 0;
                    type_name = dt->type();
                }
            }
        }
    }
    
    int main() {
        std::vector<ITcAdsSymbol> vars;
        std::vector<char> raw;
        try {
            update_from_sps_data(vars, raw, raw);
        } catch ( std::runtime_error& e ) {
            std::cout << e.what() << '\n';
            return 1;
        }
    }
    

Anmelden zum Antworten