Zeno file format
ZenoReader/Library Aus DirectWiki Wechseln zu: Navigation, Suche Die Dokumentation wurde für das Zeno-2.0-Format aktualisiert! Inhaltsverzeichnis 1 Aufbau einer .zeno-Datei 1.1 Header 1.2 Inhaltsverzeichnis 1.3 Zusatzdaten bei Indexdateien 2 NamespaceCounts-Tabelle 3 Integer-Komprimierung
[bearbeiten] Aufbau einer .zeno-Datei
[bearbeiten] Header Nach dem Öffnen der Datei muss der Header eingelesen werden, er steht am Anfang der Datei.
TZenoLibraryHeaderFlag = (zlfIsIndex); TZenoLibraryHeaderFlags = set of TZenoLibraryHeaderFlag; TZenoLibraryHeader = record rMagicNumber: integer; // Erkennungsmarke, muss immer den Wert 1439867043 haben rVersion: integer; // wp2006=2, wp2007=3, bei Formatänderungen wird hochgezählt rCount: integer; // Anzahl der Artikel rUnused1: integer; // da Delphi anscheinend Int64 auf 8-Byte-Grenzen legt entsteht diese Lücke rIndexPos: Int64; // Position des Inhaltsverzeichnisses rIndexLen: integer; // Länge des Inhaltsverzeichnisses rUnused2: integer; // vormals rFlags rIndexPtrPos: Int64; // Position der Zeigerliste auf das Inhaltsverzeichnis rIndexPtrLen: integer; // Länge der Zeigerliste auf das Inhaltsverzeichnis, also 4*rCount rTreeDataPos: Int64; // bei wp nicht benutzt rTreeDataLen: integer; // bei wp nicht benutzt rIndexTotalArticleCount: integer; // nur für die Indexdatei rIsIndexCompressed: boolean; // in der ausgelieferten Version immer true bei der Indexdatei rNamespaceCountPos: int64; // Fileposition der Tabelle, die Infos über die Namespaces hat, siehe unten rNamespaceCountLen: integer; // Länge dieser Tabelle, z.Zt. fix auf 368 Bytes (8 Bytes * 46 Namespaces) rUnused: array [0..57] of integer;// mehr Luft als hier vorher war end;
[bearbeiten] Inhaltsverzeichnis Um den Code erstmal einfach zu halten, wird das ganze Inhaltsverzeichnis auf einen Schlag eingelesen (kann mit dem Parameter /f erzwungen werden, bringt Geschwindigkeitsgewinn auf Kosten von RAM).
TWPVLibrary = class (TObject) private FBuffer: array of byte; // das Inhaltsverzeichnis als Byte-Stream mit Records variabler Länge FBufferSize: integer;
Auf jeden Fall ganz eingelesen werden muss die Zeigerliste auf das Inhaltsverzeichnis. Sie wird in einer Listenstruktur gehalten, könnte aber auch ein array of integer sein. Die Liste ist vorsortiert, siehe ZenoReader/Qunicode.
FList: TList;
Beispiel: Das nullte Element der Liste hat den Wert 0 und zeigt auf Nullte Byte in FBuffer. Das erste Element hat z.B. den Wert 56 und zeigt auf das 56. Byte in FBuffer, was einem Zeiger auf den zweiten Eintrag des Inhaltsverzeichnisses entspricht, usw. Dabei ist der Inhalt von FBuffer folgendermaßen zu interpretieren:
TZenoLibraryCompressionType = (zenocompDefault, zenocompNone, zenocompZip); TZenoLibraryMimeType = (ZenomimeTextHtml, ZenomimeTextPlain, ZenomimeImageJpeg, ZenoMimeImagePng, ZenoMimeImageTiff, ZenoMimeTextCss, ZenoMimeImageGif, ZenoMimeIndex, ZenoMimeApplicationJavaScript, ZenoMimeImageIcon, ZenoMimeTextXml)
RZenoArticle = packed record rFilePos: Int64; // 8 Byte, absolute Position der Artikeldaten im Stream rFileLen: cardinal; // 4 Byte, Läge der Artikeldaten rCompression: TZenoLibraryCompressionType; // 1 Byte rMime: TWPVLibraryMimeType; // 1 Byte rRedirectFlag: char; // wird demnächst verwendet rNamespace: char; // A, B, ... rRedirectIndex: integer; // 4 Byte, wenn rRedirectFlag dann hier der Artikelindex des Hauptartikels rLogicalNumber: integer; // 4 Byte, in wp nicht benutzt rExtraLen: word; // 2-Byte-Länge des nachfolgenden Strings inklusive Nullbyte rExtra: array [0..$FFF] of char; // Artikeltitel, Nullbyte, evtl. Parameter wie h=200, durch Nullbyte getrennt end;
Es ist zu beachten, dass der Record auf Bytegrenzen gepackt ist, d.h. ohne rExtra genau 26 Byte groß ist. Durch das zwingene Nullbyte in rExtra auch bei einem leeren Artikeltitel ist somit jeder Eintrag mindestens 27 Byte groß. [bearbeiten] Zusatzdaten bei Indexdateien Die Artikel der Indexdateien gehören zu folgenden Namespaces: V/xxx: alle Artikel der Kategorie xxx, alphabetisch sortiert W/xxx: alle Artikel der Kategorie xxx, nach Artikelindex sortiert X/xxx: alle Artikel in denen das Wort xxx vorkommt, nach Artikelindex sortiert Y/xxx: reserviert für Weblinks (in WP-zeno nicht vorhanden) Z/xxx: reserviert für hervorgehobene Wortgruppen (in WP-zeno nicht vorhanden)
Ein Eintrag für V und W ist ein 4-Byte-Integer (Artikelindex), ein Eintrag für X ein 8-Byte-Integer (Artikelindex und Wortindex). Nach dem abschließenden Nullbyte des Wortes in rExtra kommt ein Längenbyte für die nachfolgende Struktur, das stets <= 255 ist. Alle Integers sind komprimiert, siehe unten. flags: die ersten vier Bit geben an, ob es einen Eintrag für die Gewichtung (0..3) gibt,
auch dieses Byte ist (unnötigerweise) schon komprimiert, d.h. in komprimiertem Zustand sind die Bits um 2 nach links verschoben.
Für jede Gewichtung folgt ein Eintrag:
len: Länge des Streams in der Datei in Bytes
firstArticleIndex: bei V, W, X
firstWordIndex: nur bei X
Der erste Eintrag einer jeden Gewichtung befindet sich somit in RZenoArticle und ist schnell zu lesen. Gibt es nur diesen einen Eintrag, ist len=0, ansonsten gibt es weitere Einträge in der Indexdatei. [bearbeiten] NamespaceCounts-Tabelle Gültige Namespace-Characters (TLibNamespaceChar) gehen von '-' bis 'Z', also 46 verschiedene Namespaces. Da die Artikel aufsteigend nach Namespace angelegt sind, kann man mit dem Startindex und der Anzahl der Artikel für diesen Namespace gut auf einen bestimmten Namespace zugreifen, z.B. wenn die Wildcardsuche über den ganzen Namespace 'X' (und nur über den) laufen soll.
RNamespaceStartCount = record rNamespaceStart: integer; // Startindex ist 1, nicht 0 rNamespaceCount: integer; end;
RNamespaceStartCountArray = array [TLibNamespaceChar] of RNamespaceStartCount;
[bearbeiten] Integer-Komprimierung Bei komprimierten Indexdateien sind sowohl die Zusatzdaten (s.o.) als auch die Daten in der Indexdatei wie folgt komprimiert: Die ersten beiden Bits des ersten Bytes sind reserviert und codieren ob es sich insgesamt um 1, 2, 3 oder 4 Byte handelt, die einen (Pseudo-)-Integer zwischen 0 und $4040403F codieren. 1 Byte : Wertebereich 0 bis 26-1 2 Bytes: Wertebereich 26 bis 214+26-1 3 Bytes: Wertebereich 214+26 bis 222+214+26-1 3 Bytes: Wertebereich 222+214+26 bis 230+222+214+26-1
Ferner wird bei manchen Streams von Interegers noch die Eigenschaft ausgenutzt, dass sie aufsteigend sortiert sind, was bei der Indexdatei für die Namespaces "W" und "X" gilt. Der Integer-Compressor/Decompressor hat eine Property, die das Verhalten steuert: TIntegerCompressionType =
(ictNone, // keine Komprimierung, je 4 Bytes pro Integer ict2Bits, // Komprimierung wie oben beschrieben, 1-4 Bytes, verwendet für den // Namespace V, da dort die Artikel alphabetisch sortiert sind sowie // für die Struktur in rExtra ict2BitsSeq1, // Namespace W: es wird nur die Differenz zwischen dem vorherigen Wert // und dem aktuellen gespeichert, was die Zahl "kleiner" hält ict2BitsSeq2); // Namespace X: da es sich um Paare von Artikel- und Wortindex handelt // wird die Different des Artikelindex gespeichert, ist dieser Null // wird die Differenz des Wortindex gespeichert, ansonsten die absolute // Zahl
Beispiel für ict2BitsSeq2: Es soll diese Folge codiert werden: (3,27), (5,3), (5,20), (12,25)
Unkomprimiert, aber mit Differenzenbildung reduziert sich das zu: (3,27), (2,3), (0,17), (7, 25)