// // index.cs: Handling of the index files // // Author: // Miguel de Icaza (miguel@ximian.com) // // (C) 2003 Ximian, Inc. // // Possible file format optimizations: // * Do not use 4 bytes for each index entry, use 3 bytes // * Find a way of compressing strings, there are plenty of duplicates // Find common roots, and use an encoding that uses a root to compress data. // "System", "System.Data", "System.Data class" // 0: PLAIN: "System" // 1: PLAIN: " class" // 2: LINK0 PLAIN ".DATA" // 3: LINK0 LINK1 // // Maybe split everything at spaces and dots, and encode that: // string-1-idx "System." // string-1-idx "Data" // 2-items [ string-1-idx string-2-idx] // // Other variations are possible; Like Archive "System", "System." when we // see "System.Data". // // namespace Monodoc { using System; using System.IO; using System.Text; using System.Collections; public class Topic { public readonly string Caption; public readonly string SortKey; public readonly string Url; public Topic (string caption, string sort_key, string url) { Caption = caption; SortKey = sort_key; Url = url; } } public class IndexEntry { public int Position; public object topics; public int Count; public void Add (Topic t) { Count++; if (topics == null) topics = t; else { if (!(topics is ArrayList)){ Topic temp = (Topic) topics; topics = new ArrayList (); ((ArrayList)topics).Add (temp); } ((ArrayList)topics).Add (t); } } public Topic this [int idx] { get { if (topics is Topic){ if (idx == 0) return (Topic) topics; else throw new Exception ("Out of range index"); } else { return (Topic) (((ArrayList)topics) [idx]); } } } // // Constructor from a stream // public IndexEntry (FileStream fs, BinaryReader reader, int position) { Count = reader.ReadInt32 (); int caption_offset = reader.ReadInt32 (); string caption; if (Count == 1){ int url_offset = reader.ReadInt32 (); fs.Position = caption_offset; caption = reader.ReadString (); fs.Position = url_offset; string url = reader.ReadString (); topics = new Topic (caption, "", url); } else { ArrayList l = new ArrayList (Count); topics = l; int [] offsets = new int [Count]; for (int i = 0; i < Count; i++){ offsets [i] = reader.ReadInt32 (); } fs.Position = caption_offset; caption = reader.ReadString (); for (int i = 0; i < Count; i++){ fs.Position = offsets [i]; string url = reader.ReadString (); l.Add (new Topic (caption, "", url)); } } } // Topic ReadTopic (FileStream fs, BinaryReader reader, ref string caption) // { // int caption_offset = -1; // if (caption == null) // caption_offset = reader.ReadInt32 (); // int url_offset = reader.ReadInt32 (); // // if (caption == null){ // fs.Position = caption_offset; // caption = reader.ReadString (); // } // fs.Position = url_offset; // string url = reader.ReadString (); // // return new Topic (caption, "", url); // } // // Regular constructor public IndexEntry () { } public void WriteTopics (IndexMaker maker, Stream stream, BinaryWriter writer) { // // Convention: entries with the same SortKey should have the same Caption // Position = (int) stream.Position; writer.Write (Count); if (topics is ArrayList){ bool first = true; foreach (Topic t in (ArrayList) topics){ if (first){ writer.Write (maker.GetCode (t.Caption)); first = false; } writer.Write (maker.GetCode (t.Url)); } } else { Topic t = (Topic) topics; writer.Write (maker.GetCode (t.Caption)); writer.Write (maker.GetCode (t.Url)); } } } public class IndexMaker { Hashtable entries = new Hashtable (); Hashtable all_strings = new Hashtable (); void add_string (string s) { if (all_strings.Contains (s)) return; all_strings [s] = 0; } public void AddTopic (Topic topic) { IndexEntry entry = (IndexEntry) entries [topic.SortKey]; if (entry == null){ entry = new IndexEntry (); entries [topic.SortKey] = entry; } add_string (topic.SortKey); add_string (topic.Caption); add_string (topic.Url); entry.Add (topic); } public void Add (string caption, string sort_key, string url) { Topic t = new Topic (caption, sort_key, url); AddTopic (t); } void SaveStringTable (Stream stream, BinaryWriter writer) { ICollection k = all_strings.Keys; string [] ks = new string [k.Count]; k.CopyTo (ks, 0); foreach (string s in ks){ int pos = (int) stream.Position; writer.Write (s); all_strings [s] = pos; } } public int GetCode (string s) { return (int) all_strings [s]; } int index_position; void SaveTopics (Stream stream, BinaryWriter writer) { // // Convention: entries with the same SortKey should have the same Caption // foreach (IndexEntry e in entries.Values) e.WriteTopics (this, stream, writer); } void SaveIndexEntries (Stream stream, BinaryWriter writer) { index_position = (int) stream.Position; writer.Write (entries.Count); ICollection keys = entries.Keys; string [] keys_name = new string [keys.Count]; keys.CopyTo (keys_name, 0); Array.Sort (keys_name, new NameSort ()); foreach (string s in keys_name){ IndexEntry e = (IndexEntry) entries [s]; writer.Write (e.Position); } } class NameSort : IComparer { public int Compare (object a, object b) { string sa = (string) a; string sb = (string) b; return String.Compare (sa, sb, true); } } public void Save (string filename) { Encoding utf8 = new UTF8Encoding (false, true); using (FileStream fs = File.OpenWrite (filename)){ BinaryWriter writer = new BinaryWriter (fs, utf8); writer.Write (new byte [] { (byte) 'M', (byte) 'o', (byte) 'i', (byte) 'x'}); // Leave room for pointer fs.Position = 8; SaveStringTable (fs, writer); SaveTopics (fs, writer); // index_position is set here SaveIndexEntries (fs, writer); fs.Position = 4; writer.Write (index_position); } } } public interface IListModel { int Rows {get; } string GetValue (int row); string GetDescription (int row); } public class IndexReader : IListModel { Encoding utf8 = new UTF8Encoding (false, true); FileStream fs; BinaryReader reader; // The offset of the table of entries int table_offset; int entries; static public IndexReader Load (string filename) { if (!File.Exists (filename)) return null; try { return new IndexReader (filename); } catch { return null; } } IndexReader (string filename) { fs = File.OpenRead (filename); reader = new BinaryReader (fs, utf8); if (fs.ReadByte () != 'M' || fs.ReadByte () != 'o' || fs.ReadByte () != 'i' || fs.ReadByte () != 'x'){ throw new Exception ("Corrupt index"); } // Seek to index_entries fs.Position = reader.ReadInt32 (); entries = reader.ReadInt32 (); table_offset = (int) fs.Position; } public int Rows { get { return entries; } } public string GetValue (int row) { fs.Position = row * 4 + table_offset; fs.Position = reader.ReadInt32 () + 4; int code = reader.ReadInt32 (); fs.Position = code; string caption = reader.ReadString (); return caption; } public string GetDescription (int row) { return GetValue (row); } public IndexEntry GetIndexEntry (int row) { fs.Position = row * 4 + table_offset; int entry_offset = reader.ReadInt32 (); fs.Position = entry_offset; return new IndexEntry (fs, reader, entry_offset); } } } // Namespace