2 // index.cs: Handling of the index files
5 // Miguel de Icaza (miguel@xamarin.com)
7 // (C) 2003 Ximian, Inc.
8 // Copyright 2003-2011 Novell Inc
9 // Copyright 2011 Xamarin Inc.
11 // Possible file format optimizations:
12 // * Do not use 4 bytes for each index entry, use 3 bytes
13 // * Find a way of compressing strings, there are plenty of duplicates
14 // Find common roots, and use an encoding that uses a root to compress data.
15 // "System", "System.Data", "System.Data class"
18 // 2: LINK0 PLAIN ".DATA"
21 // Maybe split everything at spaces and dots, and encode that:
22 // string-1-idx "System."
23 // string-1-idx "Data"
24 // 2-items [ string-1-idx string-2-idx]
26 // Other variations are possible; Like Archive "System", "System." when we
35 using System.Collections;
38 public readonly string Caption;
39 public readonly string SortKey;
40 public readonly string Url;
42 public Topic (string caption, string sort_key, string url)
50 public class IndexEntry {
55 public void Add (Topic t)
61 if (!(topics is ArrayList)){
62 Topic temp = (Topic) topics;
64 topics = new ArrayList ();
65 ((ArrayList)topics).Add (temp);
67 ((ArrayList)topics).Add (t);
71 public Topic this [int idx] {
75 return (Topic) topics;
77 throw new Exception ("Out of range index");
79 return (Topic) (((ArrayList)topics) [idx]);
85 // Constructor from a stream
87 public IndexEntry (FileStream fs, BinaryReader reader, int position)
89 Count = reader.ReadInt32 ();
90 int caption_offset = reader.ReadInt32 ();
94 int url_offset = reader.ReadInt32 ();
95 fs.Position = caption_offset;
96 caption = reader.ReadString ();
97 fs.Position = url_offset;
98 string url = reader.ReadString ();
99 topics = new Topic (caption, "", url);
101 ArrayList l = new ArrayList (Count);
103 int [] offsets = new int [Count];
104 for (int i = 0; i < Count; i++){
105 offsets [i] = reader.ReadInt32 ();
107 fs.Position = caption_offset;
108 caption = reader.ReadString ();
109 for (int i = 0; i < Count; i++){
110 fs.Position = offsets [i];
111 string url = reader.ReadString ();
112 l.Add (new Topic (caption, "", url));
117 // Topic ReadTopic (FileStream fs, BinaryReader reader, ref string caption)
119 // int caption_offset = -1;
120 // if (caption == null)
121 // caption_offset = reader.ReadInt32 ();
122 // int url_offset = reader.ReadInt32 ();
124 // if (caption == null){
125 // fs.Position = caption_offset;
126 // caption = reader.ReadString ();
128 // fs.Position = url_offset;
129 // string url = reader.ReadString ();
131 // return new Topic (caption, "", url);
135 // Regular constructor
141 public void WriteTopics (IndexMaker maker, Stream stream, BinaryWriter writer)
144 // Convention: entries with the same SortKey should have the same Caption
146 Position = (int) stream.Position;
147 writer.Write (Count);
149 if (topics is ArrayList){
151 foreach (Topic t in (ArrayList) topics){
153 writer.Write (maker.GetCode (t.Caption));
156 writer.Write (maker.GetCode (t.Url));
159 Topic t = (Topic) topics;
161 writer.Write (maker.GetCode (t.Caption));
162 writer.Write (maker.GetCode (t.Url));
167 public class IndexMaker {
168 Hashtable entries = new Hashtable ();
169 Hashtable all_strings = new Hashtable ();
171 void add_string (string s)
173 if (all_strings.Contains (s))
178 public void AddTopic (Topic topic)
180 IndexEntry entry = (IndexEntry) entries [topic.SortKey];
182 entry = new IndexEntry ();
183 entries [topic.SortKey] = entry;
186 add_string (topic.SortKey);
187 add_string (topic.Caption);
188 add_string (topic.Url);
192 public void Add (string caption, string sort_key, string url)
194 Topic t = new Topic (caption, sort_key, url);
198 void SaveStringTable (Stream stream, BinaryWriter writer)
200 ICollection k = all_strings.Keys;
201 string [] ks = new string [k.Count];
204 foreach (string s in ks){
205 int pos = (int) stream.Position;
207 all_strings [s] = pos;
211 public int GetCode (string s)
213 return (int) all_strings [s];
218 void SaveTopics (Stream stream, BinaryWriter writer)
221 // Convention: entries with the same SortKey should have the same Caption
223 foreach (IndexEntry e in entries.Values)
224 e.WriteTopics (this, stream, writer);
227 void SaveIndexEntries (Stream stream, BinaryWriter writer)
229 index_position = (int) stream.Position;
230 writer.Write (entries.Count);
231 ICollection keys = entries.Keys;
232 string [] keys_name = new string [keys.Count];
233 keys.CopyTo (keys_name, 0);
234 Array.Sort (keys_name, new NameSort ());
236 foreach (string s in keys_name){
237 IndexEntry e = (IndexEntry) entries [s];
238 writer.Write (e.Position);
242 class NameSort : IComparer {
243 public int Compare (object a, object b)
245 string sa = (string) a;
246 string sb = (string) b;
248 return String.Compare (sa, sb, true);
252 public void Save (string filename)
254 Encoding utf8 = new UTF8Encoding (false, true);
256 using (FileStream fs = File.OpenWrite (filename)){
257 BinaryWriter writer =
258 new BinaryWriter (fs, utf8);
259 writer.Write (new byte [] { (byte) 'M',
260 (byte) 'o', (byte) 'i',
263 // Leave room for pointer
266 SaveStringTable (fs, writer);
267 SaveTopics (fs, writer);
269 // index_position is set here
271 SaveIndexEntries (fs, writer);
274 writer.Write (index_position);
279 public interface IListModel {
281 string GetValue (int row);
282 string GetDescription (int row);
285 public class IndexReader : IListModel {
286 Encoding utf8 = new UTF8Encoding (false, true);
290 // The offset of the table of entries
294 static public IndexReader Load (string filename)
296 if (!File.Exists (filename))
300 return new IndexReader (filename);
306 IndexReader (string filename)
308 fs = File.OpenRead (filename);
309 reader = new BinaryReader (fs, utf8);
311 if (fs.ReadByte () != 'M' ||
312 fs.ReadByte () != 'o' ||
313 fs.ReadByte () != 'i' ||
314 fs.ReadByte () != 'x'){
315 throw new Exception ("Corrupt index");
318 // Seek to index_entries
319 fs.Position = reader.ReadInt32 ();
321 entries = reader.ReadInt32 ();
323 table_offset = (int) fs.Position;
332 public string GetValue (int row)
334 fs.Position = row * 4 + table_offset;
335 fs.Position = reader.ReadInt32 () + 4;
336 int code = reader.ReadInt32 ();
338 string caption = reader.ReadString ();
343 public string GetDescription (int row)
345 return GetValue (row);
348 public IndexEntry GetIndexEntry (int row)
350 fs.Position = row * 4 + table_offset;
351 int entry_offset = reader.ReadInt32 ();
352 fs.Position = entry_offset;
354 return new IndexEntry (fs, reader, entry_offset);