pl: Rekursiver Algorithmus über Callbackfunktion

Hi,

Gegeben sei ein Blob. Die ersten 4 Byte beinhalten eine Zahl (Length), um diese Zahl zu ermitteln müsste der Blob.slice(0,4) mit dem FileReader als ArrayBuffer gelesen werden. In der onload-Callbackfunktion bekäme man also diese Zahl Length und als Nächstes müsste der Blob.slice(4, 4 + Length) als Text gelesen werden. Nach dem onload-Event für FileReader.readAsText wiederholt sich der ganze Zyklus, solange bis das Ende des Blobs erreicht ist.

Kann mir mal jemand bezüglich einer ansatzweisen Grundstruktur auf die Sprünge helfen?

Es soll nach diesem Prinzip ablaufen, nur mit dem Unterschied daß die Längenangaben nunmehr im Blob selbst untergebracht werden sollen. Deswegen die Rekursion.

MfG

  1. Hallo pl,

    wie wär's, wenn Du auf den FileReader verzichtest? Du kannst doch mit einem DataView quer durch den ArrayBuffer lesen.

    let view = new DataView(arrBuf);
    let offs = 0;
    while (offs < view.byteLength) {
       let blobbySize = view.getInt32(offs, true);
       // Erzeuge Data URL für view.slice(offs+4, blobbySize)
       offs = offs + 4 + blobbySize;
    }
    

    Ich bekomme auf deiner Seite übrigens http 404 für die Blob-URLs - darf man die vielleicht nicht revoken, bevor der Scriptzyklus zu Ende ist und der Browser Zeit zum Layouten hatte?

    Rolf

    1. Hi danke Dir,

      das geht schonmal ganz ordentlich, also die Binary erzeuge ich so:

      my $control = sub{
          my $self = shift;
          my @ar = qw(a bb ccc asdf);
          foreach my $e(@ar){
              $self->{CONTENT} .= pack("V", length $e).$e;
          }
      };
      

      und mit Deinem Code lese ich die Längenangaben sauber raus

          var arrBuf = this.result;
          let view = new DataView(arrBuf);
          let offs = 0;
          while (offs < view.byteLength) {
             let blobbySize = view.getUint32(offs, true);
             offs = offs + 4 + blobbySize ;
             console.log(blobbySize);
          }
      

      Jezt müsste ich nur noch die Inhalte selbst da rausziehen. Aber hierzu wäre es vielleicht besser, nicht alternierend offset.content.offs.cont zu serialisieren sondern blockweise offset.offs.content.cont.

      Für heute reichts 😉

      Danke Dich!

    2. Sieht gut aus, danke Dir!

      Bei Datenmengen > 20MB gibt es hiermit keinen Absturz mehr im Gegensatz zu meiner bisherigen Lösung EAV.js. Aus einem Array eine EAV Struktur zu erzeugen ist übrigens auch kein Problem.

      Du hattest noch ein Problem mit hier:

      Ich bekomme auf deiner Seite übrigens http 404 für die Blob-URLs - darf man die vielleicht nicht revoken, bevor der Scriptzyklus zu Ende ist und der Browser Zeit zum Layouten hatte?

      Ich verstehe die Frage nicht. BlobURLs sind doch eine lokale Geschichte, wie kann es da zu einem 404 komen? Aber vielleicht fehlen Dir ja die Längenangaben, guck mal in den Header x-lens da findest Du 3 Zahlen 😉

      Viele Grüße und vielen Dank nochemal.

      1. Hallo pl,

        nein, die Längen sind da. Der http 404 wird z.B. für blob:http://rolfrost.de/e46ec984-689b-44ef-a3e4-4d1c0ef6a313 gemeldet, d.h. für deine DATA URL.

        Es ist aber browserabghängig, in Chrome kommen Broken Images, im Firefox nicht. D.h. du hast ein Kompatibilitätsproblem irgendwelcher Art. Du machst ein createObjectURL, hängst das Bild mit einem src darauf ins DOM und revokest die URL dann wieder. Ich vermute, dass Firefox ein anderes Timing beim rendern hat als Chrome. Vielleicht musst Du die erzeugten object URLs in ein Array schreiben und mit setTimeout(revokeUrls, 100) verzögert freigeben. Keine Ahnung. Vielleicht ist es auch ein Chrome Bug. Oder du darfst in Chrome nicht mit new Image() arbeiten, sondern musst document.createElement("img") aufrufen - was mir als richtiges Erzeugen von DOM Elementen bekannt ist. MDN sagt zwar das sei synonym, aber wenn was nicht tickt wie gewünscht denkt man ja an alles mögliche...

        Ob es effizienter ist, Längen und Blobbies abwechselnd zu senden, oder erst die Längen und dann die Blobbies, ist vermutlich eher serverseitig relevant. Wenn Du abwechselnd sendest, kannst Du auf dem Server Datei für Datei verarbeiten und brauchst ggf. weniger Speicher. Auf einem Server, der unter Last steht, kann das günstiger sein.

        Beachte das "kann" - ich kenne deine serverseitigen Rahmenbedingungen nicht. Ist halt nur ein Kriterium von vielen.

        Rolf

        --
        sumpsi - posui - clusi
        1. Hallo,

          update - scheinbar habe doch ICH das Problem. Zum einen braucht die Seite ewig zum Laden, was an unserem derzeit humpelnden Proxy liegen dürfte, zum anderen kommen die Bilder jetzt, nachdem ich einmal im Script debuggt habe. Bekloppt. Ich muss das nochmal zu Hause nachstellen.

          Rolf

          --
          sumpsi - posui - clusi
          1. Hallo,

            update - scheinbar habe doch ICH das Problem. Zum einen braucht die Seite ewig zum Laden, was an unserem derzeit humpelnden Proxy liegen dürfte, zum anderen kommen die Bilder jetzt,

            Ja, ich habs ja gleich geändert 😉

            nachdem ich einmal im Script debuggt habe. Bekloppt. Ich muss das nochmal zu Hause nachstellen.

            Da kannste ja gleich mal die neue Lib testen, beachte den zweiten Parameter 'raw' und aus den arraybuffers im Array musst Du dann Blobs machen ... 😉

            MfG

        2. Hi Rolf

          Es ist aber browserabghängig, in Chrome kommen Broken Images, im Firefox nicht. D.h. du hast ein Kompatibilitätsproblem irgendwelcher Art. Du machst ein createObjectURL, hängst das Bild mit einem src darauf ins DOM und revokest die URL dann wieder.

          Genau da war da Problem, nun tuts auch im Chrome danke Dir!

          Wenns halt immer so einfach wäre 😉 Mein EAV.js ist schon ne Weile her, auf die Idee, einen slice direkt auf den ArrayBuffer anzuwenden bin ich damals wohl nicht gekommen.

          Und nochewas zum Lesen Mfg 😉

          1. Hallo pl,

            wat weiß ich denn dass Du direkt vor der Kiste hockst und auf mein Posting wartest? 😉

            Aber: jetzt revokest Du erstmal gar nicht mehr - sicher weißt Du, dass das bei SPA Seiten nicht gut ist. Wenn die Seite entladen wird (das document gelöscht wird), revoked der Browser die URLs, aber du hast einen Button zum mehrfachen Laden und mit jedem Klick gehen Ressourcen fliegen, wenn Du nicht revokest. Da brauchst Du einen handgemachten Müllsammler.

            Zu deinem Aufsatz zur Serialisierung: Diese Diskussion ist alt, älter als SelfHTML. Du setzt einfach einen ganz anderen Schwerpunkt als andere und kommst daher auch zu anderen Ergebnissen. Für deinen Zweck und für viele andere Zwecke ist die von Dir vorgestellte Technik zielführend. JSON und XML verfolgen aber einen breiteren Ansatz. Zwei Dinge, die Du als selbstverständlich voraussetzt, werden von JSON und XML ganz bewusst NICHT vorausgesetzt: Endianität und Zeichencodierung. JSON und XML sind Textformate, um die binäre Repräsentation irgendwelcher Werte ausdrücklich NICHT voraussetzen zu müssen. Der Endian ist ja nur ein Aspekt. Zum Beispiel speichert nicht jedes System Dezimalzahlen als IEEE 754 Floats. Und die Textformate machen keine Aussage über die Codierung von Zeichen. Du setzt UTF-8 voraus, was eine häufige, aber nicht universelle Variante der Unicode-Darstellung ist. Es gibt noch UTF-16 und UTF-32, jeweils als BE und LE, und wenn das Dokument einen bestimmten Zeichenvorrat nutzt, spricht auch nichts dagegen, für die Speicherung Latin-1, ASCII oder EBCDIC zu nutzen. All das ist in JSON und XML möglich; eine Übersetzung von der einen Codierung in eine andere ist möglich, ohne die Dokumentstruktur zu kennen und ohne in die Software einzugreifen, die sich um die Decodierung kümmert. Das ist bei deinem Ansatz nicht möglich, du legst bestimmte Parameter fest und eine Transcodierung setzt voraus, dass man dein Serialisierungslayout kennt. Damit lässt Du einen einen der wichtigeren Ansätze des Software Engineerings beiseite: Separation of Concerns.

            Eine geringere Abstraktion erzeugt fast immer einfacheren Code. Und SE-Prinzipien wegzulassen bringt fast immer Laufzeitvorteile. SE Prinzipien verfolgen ja auch nicht das Ziel, möglichst kleine und schnelle Programme zu produzieren. Wenn man das wollte, würde man heute noch alles in Assembler schreiben. Man merkt Programmierern oft an, mit welcher Sprache sie angefangen haben. Ein guter Assemblerprogrammierer baut auch in Java noch Sprungtabellen, und ein guter COBOL Programmierer organisiert auch in C# alle Daten als WORKING-STORAGE SECTION und holt Funktionsergebnisse als ref-Parameter ab (selbst wenn sie in einem Objekt stecken). Ein guter C# Programmierer, der vor einem COBOL Compiler sitzt, bekommt nach 5 Minuten Kopfschmerzen, und ein 80er Jahre Programmierer braucht einige Behandlungen mit dem Holzhammer, bevor er einsieht, dass Laufzeit billiger ist als die Zeit, die man mit dem Schreiben und Pflegen der Software verbringt.

            Software Engineering verfolgt das Ziel, Software so modular wie möglich zu machen und Kopplungen zwischen den Bausteinen zu minimieren. Das tut sie, seit man von Assembler nach Fortran und COBOL gewechselt hat, und es ist mit Sprachen wie Erlang oder Haskell sicherlich noch nicht zu Ende. Die Programme werden dadurch langsamer. Sie werden auch größer. Aber Systeme einer gewissen Größe werden dadurch überhaupt erst realisier- und wartbar.

            Rolf

            --
            sumpsi - posui - clusi
            1. Du setzt UTF-8 voraus,

              Nein: Eine Low-Level-Serialisierung bzw. Bytesequenzen kennen gar keine Zeichenkodierung. Auch JSON ist nur eine Sequenz. Nun, die Grundlagen sind doch höchst interessant. In Fakt ist es nämlich gar nicht das Dateiformat, was die Datenstruktur abbildet, genau da irren sich so ziemlich Alle diejenigen die mit HTML zu tun haben. Entscheidend ist immer, wie das was aus HTML oder auch XML oder JSON gelesen wird, danach im Hauptspeicher liegt -- erst hier reden wir von einer Datenstruktur. So kann man einunddenselben Baum der sich aus den Daten einer XML oder HTML Datei ergibt, auch als CSV abspeichern und mit einem speziellen Algorithmus daraus das Original unzerknittert wiederherstellen.

              Eine Solche ist oftmals baumartig und und kann entweder zyklisch, linear oder nichtlinear sein. Nichtlinear heißt auch geschachtelt (nested). Manche Algorithmen zum Serialisieren transformieren nichtlineare Datenstrukturen zunächst in einer lineare, also zyklische Struktur, weil sich zyklische Strukturen einfacher serialisieren lassen.

              Wird eine zyklische Struktur serialisiert, hat man innerhalb der resultierenden Bytesequenz ebenfalls sich wiederholende zyklische Sequenzen, sog. Frames, Tupel oder Records. Innerhalb der Tupel gibt es dann den Feldbegriff ganz ähnlich wie in relationalen Datenbanken. Beispielsweise baucht man 3 Felder, wenn man die Daten eines threadbasierten Forums speichern will, egal ob in einer Datei oder in einer Tabelle gespeichert wird. So hat ein ganzer Thread z.B. mal angenommene 200 Records in einer 3 spaltigen Tabelle oder 200 Frames in einer Binärdatei. Wenn man diese Frames an der richtigen Stelle abschneidet, ist das Lesen dieser Datei kein Problem, genauso können Frames auch angehängt werden.

              Nach diesem Prinzip funktioniert auch mp3, hier kann man problemlos auf Dateiebene, also direkt in der Sequenz operieren ohne daß man den gesamten Stream in den Hauptspeicher lesen muss (Schneiden von Audiodateien). Das einfachste Beispiel einer zyklischen und linearen Datenstruktur ist das Array. Auch ein Hash ist zyklisch und gerade wir Perler wissen ja, daß ein Hash auch nur ein Array ist. Man kann also einen Hash mit demselben Algorithmus in eine Datei schreiben wie ein Array. Allgemein gesagt kann man JEDE zyklische Datenstruktur in ein Array umwandeln. Und wenn man einen Algorithmus hat der nichtlineare Strukturen in lineare und zyklische Strukturen umwandelt, kann man jede beliebige Datenstruktur als Array in einer Datei speichern.

              Hier bestätigt sich die Genialität des Niklaus Wirth. Seine Bücher gibt es noch. Ich habe hier "Algorithmen und Datenstrukturen mit Modula II" da steht im Prinzip dasselbe drin, was ich hier schrieb :)

              Was Niklaus Wirth um 1980 festgestellt hat, ist alles Andere als Theorie. Insbesondere seit OOP die Welt der Softwareentwicklung erobert hat, ist es die Grundlage, nur die Begriffe sind neu. So reden wir vom Transport-Layer und meinen damit die Sequenz. Files sind nicht mehr Lochkarten sondern Dateien. Wir definieren Data Access Layer und meinen damit den wahlfreien Zugriff auf die Innereien komplexer Datenstrukturen. Wir bezeichnen komplexe Datenstrukturen als abstrakte Datentypen. Wir segnen abstrakte Datentypen mit dem Namen einer Klasse und reden dann von Objekten bzw. Instanzen dieser Klassen. Wir vermitteln zwischen abstrakten Datentypen und Dateien über spezielle Algorithmen die wir heute als Serializer bezeichnen. Wir nutzen Protokolle wie Memcache um den wahlfreien Zugriff systemübergreifend zu machen. Wir transportieren Dateien über FTP, HTTP usw. Wir bilden komplexe Datenstrukturen in JSON Sequenzen ab damit sie transportiert werden können. Außer JSON gibt es ungezählte weitere Serializer und Dateiformate. Bei all dem was wir da tun, bestätigt sich die Richtigkeit des Wirth'schen Dateibegriffes. Wir entwickeln weitere Schichtenmodelle weil unsere Anwendungen und Datenstrukturen immer komplexer werden. Wir arbeiten objektorientiert damit wir mit Veränderungen besser umgehen können.

              Schöne Grüße 😉

              1. Hallo pl,

                Nein: Eine Low-Level-Serialisierung bzw. Bytesequenzen kennen gar keine Zeichenkodierung.

                Hat keiner anders behauptet. Aber die Daten darin, die schon.

                Du hast doch drei Ebenen:

                physisch: Die Bytefolge in der Datei (oder im Serialisieungsdatenstrom)
                logisch: Die durch die Bytes codierten Symbole
                semantisch: Die Bedeutung dieser Symbole.

                Will ich die Datei inhaltlich verarbeiten, muss ich über alle drei Ebenen weg. Um das mal mit Compilerbau[^1]-Begriffen zu formulieren: Von physisch nach logisch brauche ich einen Lexer, von logisch nach semantisch einen Parser. Reicht mir das Hantieren mit dem Symbolen (z.B. weil ich die Codierung ändern will oder im Text suchen), brauche ich nur den Lexer.

                Dein Verfahren verwendet zwei Klassen von Symbolen: Integers und Zeichen. Ich sehe aber deiner Bytefolge nicht an, welche Symbole sie enthält, ohne das inhaltliche Layout der Datei zu kennen. Das Layout gehört aber eher auf die dritte Ebene - der codierte Inhalt. D.h. ein Lexer muss Dinge wissen, die eigentlich in den Parser gehören. Darum sprach ich von der Verletzung der Separation Of Concerns.

                JSON und XML verwenden nur eine Klasse von Symbolen: Zeichen. Die Unicode-Codierung ermöglicht es, einem Byte anzusehen, ob es der Beginn eines Symbols ist oder nicht. Der Lexer muss das technische Layout nicht kennen.

                DAS ist meine Kritik an deinem Verfahren. Wenn Wirth vor 30 Jahren etwas anderes propagiert haben sollte, dann muss man dazu auch beachten, dass sich seit 1986 (A&D mit M2) in der Info eine Menge getan hat. Vor 30 Jahren war dein Verfahren durchaus state-of-the-art.

                Rolf

                --
                sumpsi - posui - clusi [^1]: Wirth, Niklaus, 1977, ISBN 978-3519223382 (3. Aufl. '84)
                1. hi Rolf

                  Dein Verfahren verwendet zwei Klassen von Symbolen: Integers und Zeichen.

                  Integer ja, Zeichen nein sondern Bytesequenzen.

                  Ich sehe aber deiner Bytefolge nicht an, welche Symbole sie enthält, ohne das inhaltliche Layout der Datei zu kennen. Das Layout gehört aber eher auf die dritte Ebene - der codierte Inhalt.

                  Eben. Und dieses Layout muss der Algorithmus der die Sequez sequentiell einliest, gar nicht kennen.

                  D.h. ein Lexer muss Dinge wissen, die eigentlich in den Parser gehören. Darum sprach ich von der Verletzung der Separation Of Concerns.

                  Dann ist Dir wohl der Unterschied zwischen sequentiellen Lesen und Parsen nicht klar. JSON braucht Parser und Lexer, ein Low Level Algorithmus hingegen braucht das nicht. Ein sequentieller Algorithmus hat nichts weiter zu tun, als zwischen Sequenz und wahlfreiem Zugriff zu vermitteln, also die Sequenz sequentiell zu lesen und so zu verarbeiten, daß eine Datenstruktur für den wahlfreien Zugriff entsteht.

                  D.h., im Hauptspeicher liegen danach auch nur die aus der Datei gelesenen Bytesequenzen mit der beabsichtigten Struktur zur Adressierung z.B. als Array, assoz. Array usw.

                  Und erst die Anwendung weiter oben im Schichtenmodell entscheidet, was aus den Einzelbytefolgen im Hauptspeicher wird: Entweder weiterhin Binaries oder Zeichen.

                  JSON und XML verwenden nur eine Klasse von Symbolen: Zeichen.

                  Genau das stellt eine Vergewaltigung des Schichtenmodells und Wirth'schen Dateibegriffes dar. Mit JSON, XML wird sozusagen versucht, den wahlfreien Zugriff auf Dateiebene abzubilden.

                  Aus Dateien jedoch kommen grundsätzlich nur Bytesequenzen, das ist auch bei JSON so. Nur sind die hier so angeordnet, dass ein Mensch die beabsichtigte Datenstruktur erkennen kann, so wie sie nach dem Parsen im Hauptspeicher liegt.

                  DAS ist meine Kritik an deinem Verfahren. Wenn Wirth vor 30 Jahren etwas anderes propagiert haben sollte, dann muss man dazu auch beachten, dass sich seit 1986 (A&D mit M2) in der Info eine Menge getan hat. Vor 30 Jahren war dein Verfahren durchaus state-of-the-art.

                  Ohne Wirth gäbe es kein Mp3. Der Wirth'sche Dateibegriff hat heute selbstverständlich immer noch Gültigkeit und zwar mehr denn je, weil IT immer komplexer wird. Deswegen gibt es ja auch Schichtenmodelle und auch zu Wirth's Zeiten ist niemand auf die Idee gekommen, Lochkarten händisch zu bohren oder visuell zu lesen -- was heute mit JSON und XML unsinnigerweise ja gemacht wird.

                  MfG 😉

                  PS: Interessant wird der Sachverhalt in c. Mehr dazu hier und da und auch daran siehst Du daß Low Level Serializer alles Andere als Plattformabhängig sind.

                2. PS: Auch im Wirth'schen Sinne ist, daß Transport Layer transparent sind. D.h., die Anwendung guckt da einfach nur durch, als gäbe es diesen Layer gar nicht.

                  So ist das was JSON.parse() liefert genau dasselbe was EAV.buffer2eav() in den Hauptspeicher legt, nämlich genau dieselbe Datenstruktur für den wahlfreien Zugriff (Random Access).

                  Im Layer selbst, egal on json oder binary, ist 3.55€ eine Sequenz, bestehend aus 7 Oktetten. Und genauso liegt diese Sequenz auch im Hauptspeicher, egal aus welchem Dateiformat die wiederhergstellt wurde. Erst für die Anwendung könnte diese Sequenz eine Preisangabe sein.

                  Die beiden hier verlinkten SelfWikis demonstrieren ja letztendlich, daß ein Transportlayer transparent wie austauschbar ist, ab dem Zeitpunkt wo der EAV im Hauptspeicher liegt, is der Code identisch.

                  Außerdem erklärt sich der Transparenz-Begriff auch dadurch, daß die serverseitig bereitgestellte Datenstruktur genauso aufgebaut ist wie sie clientseitig verwendet wird. Nur daß sie in Perl anders heißt als in JS, aus einem Perl-Hash wird ein JS-Object und umgekehrt, die darin enthaltenen Daten sind identisch.

                  Wir sehen also, der wahlfreie Zugriff findet mitnichten auf Dateiebene statt sondern nur im Hauptspeicher der deswegen auch so heißt 😉

                  Schöne Grüße.

            2. PS: Selbst INI Dateien sind zwischen PHP/Perl nicht immer kompatibel (PHP verlangt gequotete Strings, Perl nicht). Bei meinen Perl/PHP Framework, was mit PHP und Perl gleichermaßen eine gemeinsame Konfigurationsdatei benutzt die außerdem OS-unabhängig funktionieren muss, war eine Low-Level-Serialisierung dieser EAV Datenstruktur eine sowohl einfache als auch zweckmäßige Lösung. Solche und ähnliche Beispiele lassen sich beliebig fortführen.

              Low Level Algorithmen funktionieren auf jedem System, egal ob das von Haus aus die Network oder Vaxorder unterstützt (Endianess). Beim Transport der Daten muss man sich nur auf eins von beiden einigen, ansonsten sind die 4 Oktetten eines 32 Bit unsigned Integer auf allen Systemen physikalisch gleich.

              MfG

            3. PS: Hash serialisieren (Schlüssel Werte Paare):

               while( my($k, $v ) = each %ENV ){
                  $self->{CONTENT} .= pack("N", length $k).$k.pack("N", length $v).$v;
               }
              

              Keine Quoten, keine Trennzeichen, binary safe, von der Plattform und von der Zeichenkodierung völlig unabhängig: Einfacher gehts nicht!

              Noch Fragen 😉

              1. Noch Fragen 😉

                Ja! Warum nicht JSON? Achso, ich vergaß Deine kaputte Perl-Installation 😉

                1. Noch Fragen 😉

                  Ja! Warum nicht JSON? Achso, ich vergaß Deine kaputte Perl-Installation 😉

                  Es ist genau diese Arroganz die einem meiner ehemaligen Arbeitgeber schwer auf die Füße gefallen ist. Mein Tipp: Lass es einfach! MfG

                  1. Hallo pl,

                    Noch Fragen 😉

                    Ja! Warum nicht JSON? Achso, ich vergaß Deine kaputte Perl-Installation 😉

                    Es ist genau diese Arroganz die einem meiner ehemaligen Arbeitgeber schwer auf die Füße gefallen ist. Mein Tipp: Lass es einfach! MfG

                    Genau. Alle anderen machen es falsch, wenn sie auf bewährte Techniken setzen.

                    Bis demnächst
                    Matthias

                    --
                    Rosen sind rot.
            4. hi Rolf,

              Aber: jetzt revokest Du erstmal gar nicht mehr - sicher weißt Du, dass das bei SPA Seiten nicht gut ist.

              Das ist überall zu lesen. Aber hat Du ein derartiges Problem schonmal gehabt? Wenn ja, würde ich das gerne mal nachstellen.

              MfG

        3. h Rolf,

          Ob es effizienter ist, Längen und Blobbies abwechselnd zu senden, oder erst die Längen und dann die Blobbies, ist vermutlich eher serverseitig relevant.

          Mit Perl oder PHP auf der Empfängerseite kann man z.b. sämtliche Offsetangaben als Array mit einer einzigen unpack()Anweisung bekommen. Genauso kann man sie serverseitig mit einer einzigen pack()Anweisung zu Oktetten machen.

          Deswegen fasse ich beim EAV immer 3 Längenangaben (Entity, Attribute, Value) zusammen, das sind dann 12 Bytes.

          Wenn Du abwechselnd sendest, kannst Du auf dem Server Datei für Datei verarbeiten und brauchst ggf. weniger Speicher. Auf einem Server, der unter Last steht, kann das günstiger sein.

          Weniger Speicher braucht man, wenn man die Oktetten unmittelbar aus einem Handle lesen kann, so liegt nicht die ganze Datei im Hauptspeicher sondern immer nur ein kleiner Teil (Streaming).

          Beachte das "kann" - ich kenne deine serverseitigen Rahmenbedingungen nicht. Ist halt nur ein Kriterium von vielen.

          Ja natürlich. Je mehr Offsetangaben man zusammenfassen will, desto länger ist die Teilsequenz die im Speicher liegen muss, denn darauf wir ja die Funktion length() angewandt..

          MfG

    3. Hi lieber Rolf,

      ich habe nun die Funktion buffer2eav in diese Lib gebracht. Den gesamten Content des SELFHTML Wiki in eine EAV Binary zu serialisieren, ist nun kein Problem mehr, ganz im Gegenteil:

      Sowohl die Erzeugung der Binary serverseitig mit Perl als auch der Einbau der Binary nach dem Download in die lokale SPA ist um einige Größenordnungen performanter als die JSON Lösung!

      Das liegt aber nicht etwa daran, dass die Binary mit 26MB um 1MB kleiner ist als der JSON mit 27MB sondern es liegt ganz einfach daran, daß ein Low Level Algorithmus CPU gefälliger arbeitet als ein Algorithmus der zeichenorientiert eine JSON Datei parsen muss.

      Und das obwohl JSON.parse() eine Built-In-Funktion ist, ist buffer2eav als native JavaScript viel performanter -- Das kann sich sehen lassen!!! -- Morgen ist es online 😉

      Ein schöner Erfolg für einen Freitag. Und danke Deiner Hilfe!!!

      Viele Grüße!

      1. Und das obwohl JSON.parse() eine Built-In-Funktion ist, ist buffer2eav als native JavaScript viel performanter -- Das kann sich sehen lassen!!! -- Morgen ist es online 😉

        Hey, das hört sich top an! Sofern das "morgen ist es online" bedeutet, dass wir es dann alle transparent als Benchmark "buffer2eav vs JSON.parse" nachvollziehen und messen können... ja... ja, dann freue ich mich drauf!

        Viele Grüße!!!

      2. Hallo pl,

        LOL, der blobby - oder das Blöbchen - war eigentlich ein Scherz. Bin gespannt drauf.

        Rolf

        --
        sumpsi - posui - clusi
        1. problematische Seite

          Hi Rolf

          LOL, der blobby - oder das Blöbchen - war eigentlich ein Scherz.

          Sprechende Variablen halt und einprägsam 😉

          Bin gespannt drauf.

          Nun, beides zum Vergleich JSON based und hier die Binaryvariante.

          Zum Testen die Dateien selfwiki.bin bzw. selfwiki.json jeweils zunächst downloaden.

          Viel Spaß beim Testen und schönes Wochenende 😉

          1. problematische Seite

            Viel Spaß beim Testen

            Bin dabei! Kannst Du mir dazu bitte noch in Deiner

            http://perl.rolfrost.de/Binary.js

            das Gegenstück zu "buffer2eav", also vermutlich "eav2buffer" hinterlegen? Danke!

            1. problematische Seite

              Viel Spaß beim Testen

              Bin dabei! Kannst Du mir dazu bitte noch in Deiner

              http://perl.rolfrost.de/Binary.js

              das Gegenstück zu "buffer2eav", also vermutlich "eav2buffer" hinterlegen? Danke!

              Guck mal in EAV.js also EAV.eav2blob heißt das Teil.

              1. problematische Seite

                Guck mal in EAV.js also EAV.eav2blob heißt das Teil.

                EAV.js sehe ich keine?! Hast Du den direkten Link zur Hand, in der die Routine definiert ist?

                1. problematische Seite

                  Hallo Mitleser,

                  ist aber eigentlich laut und deutlich in den Debug-Tools des Browsers zu finden...

                  Rolf

                  --
                  sumpsi - posui - clusi
                  1. problematische Seite

                    ist aber eigentlich laut und deutlich in den Debug-Tools des Browsers zu finden...

                    Dem war zum Zeitpunkt meines Postings noch nicht so. Rest folgt morgen, SPANNUNG!!!

                    1. problematische Seite

                      Ich hätte erwartet, folgendes auf

                      http://perl.rolfrost.de/selfbin.html

                      In der Entwickerkonsole ausführen zu können:

                      var meinObjekt = {'a' : 1, 'b' : 2 };
                      var eavDing = EAV.eav2blob(meinObjekt);
                      console.log('EAV.buffer2eav:', EAV.buffer2eav(eavDing));
                      

                      Produziert aber einen Laufzeitfehler: "Uncaught TypeError: First argument to DataView constructor must be an ArrayBuffer".

                      1. problematische Seite

                        Ich hätte erwartet, folgendes auf

                        http://perl.rolfrost.de/selfbin.html

                        In der Entwickerkonsole ausführen zu können:

                        var meinObjekt = {'a' : 1, 'b' : 2 };
                        var eavDing = EAV.eav2blob(meinObjekt);
                        console.log('EAV.buffer2eav:', EAV.buffer2eav(eavDing));
                        

                        Produziert aber einen Laufzeitfehler: "Uncaught TypeError: First argument to DataView constructor must be an ArrayBuffer".

                        Das ist ja auch keine EAV Struktur was Du da hast. Und ein Blob ist auch kein ArrayBuffer. Wenn Du aus einem Blob einen ArrayBuffer erzeugen willst, brauchst Du den FileReader.

                        MfG

                        1. problematische Seite

                          Das ist ja auch keine EAV Struktur was Du da hast. Und ein Blob ist auch kein ArrayBuffer. Wenn Du aus einem Blob einen ArrayBuffer erzeugen willst, brauchst Du den FileReader.

                          OK. Coole Antwort! Nachfrage: sollte Dein Konzept nicht etwa denselben Mechnamismus wie JSON.stringify / JSON.parse unterstützen? Weißt wie ich meine? So richtig vorwärts / rücktwärts, hin- und zurück und so? Wäre voll cool!

                          1. problematische Seite

                            Das ist ja auch keine EAV Struktur was Du da hast. Und ein Blob ist auch kein ArrayBuffer. Wenn Du aus einem Blob einen ArrayBuffer erzeugen willst, brauchst Du den FileReader.

                            OK. Coole Antwort! Nachfrage: sollte Dein Konzept nicht etwa denselben Mechnamismus wie JSON.stringify / JSON.parse unterstützen?

                            Nee. Ganz bestimmt nicht!

                            1. problematische Seite

                              OK. Coole Antwort! Nachfrage: sollte Dein Konzept nicht etwa denselben Mechnamismus wie JSON.stringify / JSON.parse unterstützen?

                              Nee. Ganz bestimmt nicht!

                              OK. Schade!

                              1. problematische Seite

                                OK. Coole Antwort! Nachfrage: sollte Dein Konzept nicht etwa denselben Mechnamismus wie JSON.stringify / JSON.parse unterstützen?

                                Nee. Ganz bestimmt nicht!

                                OK. Schade!

                                Mit Deinen Beiträgen zeigst Du immer wieder daß Du die fachlichen Zusammenhänge ignorierst. Eine fachliche Diskussion sähe nämlich anders aus. Das ist das was ich schade finde. MfG

                  2. problematische Seite

                    Hi Rolf,

                    ich habe Deinen Code mal ein bischen aufgeräumt und zwar so, daß er nachvollzogen werden kann. D.h., daß man auch anhand des Codes sehen sollte wie die Datei aufgebaut ist. Den EAV Datentyp kann man sich ja auf verschiedene Art und Weise verinnerlichen, am Anschaulichsten als INI-Datei:

                    [Entity]
                    Attribute = Value
                    

                    Und so ergibt die Datenabstraktion genau 3 Felder, die sich auch in einer binär serialisierten Datei wiederfinden: Jedes dieser Tupel besteht aus einem Teil mit konstanter und einem Teil mit variabler Länge. Konstante Länge hat, und das ist für die Wiederherstellung der Daten entscheidend, die Offsetangabe: Jeder Uint32 hat stets eine Länge von genau 4 Byte.

                    // elen => Länge Entity
                    let elen = dv.getUint32(offs, false);
                    offs += 4;
                    // alen => Länge Attribute
                    let alen = dv.getUint32(offs, false);
                    offs += 4;
                    // vlen => Länge Value
                    let vlen = dv.getUint32(offs, false);
                    offs += 4;
                    

                    Mit diesen Längenangaben werden E, A, V bytegenau aus der Sequenz gelesen und danach die Variable offs für den nächsten Lesezyklus um diese Beträge hochgesetzt.

                    Wobei bytegenau bedeutet, daß man beim Serialisieren nicht etwa die Länge eines Zeichen ermittelt, sondern die Anzahl der Oktetten (Bytesemantic vs. Charactersemantic). Ansonsten ist dieser extrem einfach zu verstehende Algorithmus in jeder Programmiersprache umsetzbar.

                    Dateien dieser Art eignen sich hervorragend als Multimediadatein wo Texte mit Grafiken, Audio und Video gleichermaßen transportiert werden können (Hypermediadatei). Mit Sicherheit ist dieses Dateiformat auch die zwckmäßigere Alternative für multipart/form-data -- Allein hieran sehen wir doch wie hoffnungslos überholt manche Standards sind, auch der ganze MIME (Mail) Schrott der nie über ASCII 7Bit hinausgekommen ist.

                    Schönen Sonntag 😉

                    1. problematische Seite

                      Hallo pl,

                      ich hatte gestern schon mal angesetzt, zu deinem Verfahren noch eine "Kritik" zu schreiben - in dem Sinne was ich daran gut und schlecht finde.

                      Gut ist, dass Du eine Möglichkeit bietest, Binärdaten mit einzupacken.

                      Ich bin aber nicht sicher, ob eine Festlegung auf EAV die ideale Lösung ist. Ich weiß, dass Du ein Weihnachtsmann[^1] bist, aber es setzt voraus, dass man vor dem Verpacken die eigentliche Quelldatenstruktur in EAV transformiert und nach dem Entpacken wieder zurücktransformiert. Wenn man ohnehin EVA als Input hat, stört das nicht weiter, aber im Allgemeinen überträgst Du den kompliziertesten Job eines Serialisierers, nämlich: "Wie klopfe ich eine beliebige Datenstruktur platt und hole sie nachher wieder hervor", ganz nonchalant dem Anwender.

                      Wenn Du einen universellen Serialisierer als Alternative zu JSON anbieten willst, müsstest Du Dir dafür etwas einfallen lassen. Viel Spaß 😉

                      Rolf

                      --
                      sumpsi - posui - clusi [^1]: HoH, HoH, HoH !!!
                      1. problematische Seite

                        hi,

                        Ich bin aber nicht sicher, ob eine Festlegung auf EAV die ideale Lösung ist.

                        Das habe ich nie behauptet. Aber der EAV eignet sich, da die Daten in 3 Feldern vorliegen, auch für hierarchisch aufgebaute Datenstrukturen die bliebig tief verschachtelt sein können.

                        Wenn Du einen universellen Serialisierer als Alternative zu JSON anbieten willst, müsstest Du Dir dafür etwas einfallen lassen.

                        Ein Serializer muß überhaupt nicht universell sein im Sinne beliebig tief geschachtelter Datenstrukturen. Man kann jedoch beliebig tief geschachtelte Datenstrukturen mit entsprechenden Algorithmen so linearisieren daß zum Serialisieren ein ganz normales Array übrigbleibt.

                        Aber auch in dieser Hinsicht halte ich es wie Wirth, so einfach wie möglich, so kompliziert wie notwendig. So ist die erste Frage immer die wie eine Datenstruktur im Hauptspeicher liegt und nicht die Frage, wie diese Struktur eingefroren in einer Datei aussieht. Vielmehr wird die Datenstruktur von der Anwendung bestimmt, denn die Anwendung muss damit arbeiten, sprich den wahlfreien Zugriff auf die Daten haben.

                        Wirth schrieb einst sinngmäß: Eine Rekursion ist keine Schande. Man kann sich jedoch überlegen, eine für die Anwendung bestimmte Datenstruktur so zu entwerfen, daß sie nicht rekursiv durchlaufen werden muss um den wahlfreien Zugriff zu erhalten. Darüber sollten viele heutige XML-Designer mal ernsthaft nachdenken.

                        Und schließlich ist der Begriff EAV auch erst, seit Alan Storm Magento an die Börse brachte, zu einem Hype geworden. Unter uns Perlern hieß das schon immer HoH (Hash of Hashes).

                        MfG 😉

                        1. problematische Seite

                          Aber auch in dieser Hinsicht halte ich es wie Wirth, so einfach wie möglich, so kompliziert wie notwendig.

                          Wenn das so ist, warum klappt das nicht?

                        2. problematische Seite

                          Hallo pl,

                          Ein Serializer muß überhaupt nicht universell sein im Sinne beliebig tief geschachtelter Datenstrukturen.

                          Oh 😟

                          So ist die erste Frage immer die wie eine Datenstruktur im Hauptspeicher liegt und nicht die Frage, wie diese Struktur eingefroren in einer Datei aussieht.

                          Aha! Das unterschreibe ich 😀

                          Vielmehr wird die Datenstruktur von der Anwendung bestimmt

                          Aha!!! Das auch, sehr gerne 😀 ❤️ 👍 Aber wie passt das nun zum Design deines Serialisierers 🤷 ❓

                          Versteh mich nicht falsch, die Idee eines binären Transports ist interessant, wenn es darum geht, binäre Komponenten übertragen können zu wollen. Ich widerspreche nur der Idee, die zu serialisierende Datenstruktur (zumindest als Kategorie "linearisiert") vorzugeben, und die Transformation in die anwendungsspezifische Datenstruktur einfach auszublenden. Der Mehrwert eines generischen Serialisierers liegt exakt in dieser Transformation.

                          Rolf

                          --
                          sumpsi - posui - clusi
                          1. problematische Seite

                            Moin;

                            natürlich muss man beim Entwurf der Datenstruktur (DS) berücksichtigen ob sie transportiert werden muss oder nicht. Instanzen zu DB-Anwendungen müssen z.B. nicht transportiert werden und können deswegen viel komplexer aussehen wie Parameterstrukturen für Webanwendungen die bei jeder Zustandsänderung serialisiert und per HTTP transportiert werden müssen.

                            Aber auch hier gibt es Möglichkeiten, aus einfachen Schlüssel-Werte-Paaren tiefer gegliederte DS zu machen einfach dadurch daß man die Namen der <input>-Felder, also die Schlüssel selbst strukturiert. Z.B. so:

                            name="data[person][name]"
                            name="data[person][vname]"
                            name="data[person][plz]"
                            name="data[person][ort]"
                            
                            name="parameter[action][submit][insert]"
                            name="parameter[action][submit][update]"
                            name="parameter[action][submit][delete]"
                            

                            und bekommt somit im $_POST Array eine entsprechende Datenstruktur die außerdem beliebig tief gestaffelt sein kann. Serverseitig kann man so die Schlüsselparameter von den übertragenen Nutzdaten recht einfach auseinanderhalten, PHP bietet dieses Feature schon immer was viele Entwickler gar nicht kennen, weil sie allein schon mit dem Begriffen Schlüsselparameter und Parameter-Kontrollstruktur nichts anfangen können.

                            Du siehst also, Dreh- und Angelpunkt einer Anwendung ist stets die Datenstruktur und obenstehendes Beispiel sollte mehr als deutlich machen wie das zum gegebenen Design des Serializers (Percentencoding, Enctype application/x-www-form-urlencoded) passend gemacht werden kann.

                            Man muss nur die richtigen Ideen haben 😉

                            1. problematische Seite

                              Hallo pl,

                              Man muss nur die richtigen Ideen haben 😉

                              Sorry. Da folge ich nicht. Ich verstehe zwar die Idee, die Du beschreibst, aber ich widerspreche vehement dem Gedanken, dass es eine gute Idee ist. Das ist keine Lösung, sondern ein Workaround.

                              Rolf

                              --
                              sumpsi - posui - clusi
                              1. problematische Seite

                                Hi Rolf,

                                Man muss nur die richtigen Ideen haben 😉

                                Sorry. Da folge ich nicht. Ich verstehe zwar die Idee, die Du beschreibst, aber ich widerspreche vehement dem Gedanken, dass es eine gute Idee ist. Das ist keine Lösung, sondern ein Workaround.

                                Und diese Idee ist nicht einmal von mir 😉

                                Was dahintersteckt, ist die Vielgestaltigkeit (Morphologie) von Datenstrukturen. Und was das Beispiel auch belegt: Es sind nicht die Anwendungen, die nach einem universellen Serializer verlangen sondern es sind die Programmierer die sich sowas wünschen. Die Anwendung selbst nämlich, verlangt eine lineare Adressierung, so wird sie den Vornamen stets in $data->{person}{vname} erwarten genauso wie plz und ort -- immer am selben Platz, egal mit welchem Inhalt.

                                Anderes Beispiel: EAV in ein Array transformieren. Wir haben

                                [1]
                                name = Hansel
                                vname= Ulrich
                                plz  = 12345
                                
                                [2]
                                name = Otto
                                vname= Ernst
                                plz  = 55055
                                

                                und fassen im ersten Schritt die Schlüssel zusammen. So können wir diesen Datentype auch als assoz. Array aufstellen

                                1.name  = Hansel
                                1.vname = Ulrich
                                1.plz   = 12334
                                2.name  = Otto
                                2.vname = Ernst
                                2.plz   = 55055
                                

                                und das Array schließlich ist einfach alles nacheinander aufgelistest

                                1 name Hansel 1 vname Ulrich 1 plz 12334 2 name Otto 2 vname Ernst 2 plz 55055
                                

                                Beachte: Eine wichtige Information, nämlich die Zuordnung (Assoziation), steckt in der Reihenfolge! Entity ist immer auf dem Index der modulo 3 = 0 ergibt und Attribute, Value lassen sich auf den diesem Sinn entsprechenden anderen Plätzen wiederfinden. So kann man die Daten in der ursprünglichen Struktur wiederherstellen.

                                MfG 😉

                                PS: Danke Niklaus Wirth!

                                1. problematische Seite

                                  Hallo pl,

                                  das ist doch alles zu kurz gedacht.

                                  class Partner {
                                     string Name;
                                     string Vorname;
                                     List<Adresse> Adressen;  // oder Adresse[] Adressen, ist wurscht
                                     List<Vertrag> Verträge;
                                  }
                                  class Vertrag {
                                     Partner Versicherungsnehmer;
                                     List<VersichertePerson> VersichertePersonen
                                     // etc
                                  }
                                  // etc
                                  

                                  Stell Dir einen Service vor, der eine Partnerübersicht liefert. Die EAV-Keystruktur für dieses Gebilde ist abenteuerlich. Ich verzichte damit vorsätzlich auf die Möglichkeiten, die mir OOP bietet, und baue mir eine Art relationaler Darstellung meines Objektbaumes auf.

                                  Es sind nicht die Anwendungen, die nach einem universellen Serializer verlangen sondern es sind die Programmierer die sich sowas wünschen

                                  JA! Genau das. Und diesen Wunsch willst Du kleinreden? Du bist doch selbst einer!

                                  Rolf

                                  --
                                  sumpsi - posui - clusi
                                  1. problematische Seite

                                    hi,

                                    Stell Dir einen Service vor, der eine Partnerübersicht liefert. Die EAV-Keystruktur für dieses Gebilde ist abenteuerlich.

                                    EAV ist ja auch nicht alles. Wie ich bereits schrieb, Instanzen die nicht serialisiert werden müssen, können wesentlich komplexer aufgebaut sein als eine EAV Struktur und sind das i.d.R. auch. Also auch tiefer gestaffelt und geschachtelt.

                                    Ich verzichte damit vorsätzlich auf die Möglichkeiten, die mir OOP bietet, und baue mir eine Art relationaler Darstellung meines Objektbaumes auf.

                                    Die innere Stuktur von Instanzen ist nirgendwo vorgeschrieben. Die kann sich jeder Programmierer so aufbauen wie er das für zweckmäßig hält und solange er damit OOP konsequent nutzt. Das ist ja gerade die Freiheit die OOP so mit sich bringt aber das was diese Innereien mit der Außenwelt verbindet, hat sich über klar definierte und gut dokumentierte Schnittstellen zu bewegen die man nicht komplizierter machen sollte als notwendig.

                                    Es sind nicht die Anwendungen, die nach einem universellen Serializer verlangen sondern es sind die Programmierer die sich sowas wünschen

                                    JA! Genau das. Und diesen Wunsch willst Du kleinreden? Du bist doch selbst einer!

                                    Ich rede doch nichts klein. Aber ich stelle fest, daß sich internationale Konsortien vehement und bockbeinig gegen den Fortschritt stellen. Warum macht man nicht z.B. Perls Storable::freeze() zum Standard, dieser und der thaw() Algorithmus liegen doch seit Jahrzenten offen, das wäre mal ein zeitgemäßer und universeller Serializer für beliebig tief verschachtelte Datenstrukturen auch für fetch(), Ajax und Javascript.

                                    Stattdessen macht man's umgekehrt und bietet auf CPAN PHP's serialize für Perl, den langsamsten Algorithmus der Welt, das ist einfach nur lächerlich.

                                    Jeder Algorithmus ist portable, ZIP ist doch auch auf jeder Kiste drauf. MfG

  2. problematische Seite

    hi und danke Rolf B,

    Es soll nach diesem Prinzip ablaufen, nur mit dem Unterschied daß die Längenangaben nunmehr im Blob selbst untergebracht werden sollen. Deswegen die Rekursion.

    Der neue Algorithmus kommt nun hier zur Anwendung. Abwechselnd werden Offset und Binary serverseitig in eine Sequenz geschrieben, Offset als BigEndian Uint32 kodiert. fetch() betrachtet diese Sequenz jedoch nicht als Blob sondern als Arraybuffer. In der Schleife wird diese Serialisierung wieder umgekehrt und somit die Grafikdatei bytegenau wiederhergestellt.

      let dv = new DataView(buffer);
      let offs = 0;
      while (offs < buffer.byteLength ){
         let elen = dv.getUint32(offs, false);
         offs += 4;
         let bin = buffer.slice(offs, offs + elen);
         offs += elen;
      }
    

    Aus den Einzelsequenzen BlobURLs zu machen ist dann kein Problem mehr. MfG