109
edits
(New page: Nach dem Öffnen der Datei muss der Header eingelesen werden, er steht am Anfang der Datei. <pre> TZenoLibraryHeaderFlag = (zlfIsIndex); TZenoLibraryHeaderFlags = set of TZenoLibraryHe...) |
|||
Line 1: | Line 1: | ||
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. | Nach dem Öffnen der Datei muss der Header eingelesen werden, er steht am Anfang der Datei. | ||
<pre> | <pre> | ||
TZenoLibraryHeaderFlag = (zlfIsIndex); | TZenoLibraryHeaderFlag = (zlfIsIndex); | ||
TZenoLibraryHeaderFlags = set of TZenoLibraryHeaderFlag; | TZenoLibraryHeaderFlags = set of TZenoLibraryHeaderFlag; | ||
TZenoLibraryHeader = record | TZenoLibraryHeader = record | ||
rMagicNumber: integer; // Erkennungsmarke, muss immer den Wert 1439867043 haben | rMagicNumber: integer; // Erkennungsmarke, muss immer den Wert 1439867043 haben | ||
Line 23: | Line 36: | ||
end; | end; | ||
</pre> | </pre> | ||
[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) |