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
34 using System.Collections.Generic;
40 public readonly string Caption;
41 public readonly string SortKey;
42 public readonly string Url;
44 public Topic (string caption, string sort_key, string url)
52 public class IndexEntry
61 public IList<Topic> Topics {
63 return topics.AsReadOnly ();
72 public void Add (Topic t)
78 public Topic this [int idx] {
80 if (idx < 0 || idx > topics.Count)
81 throw new ArgumentOutOfRangeException ("idx");
87 // Constructor from a stream
89 public IndexEntry (FileStream fs, BinaryReader reader, int position)
91 Count = reader.ReadInt32 ();
92 int caption_offset = reader.ReadInt32 ();
94 topics = new List<Topic> (Count);
96 int [] offsets = new int [Count];
97 for (int i = 0; i < Count; i++)
98 offsets [i] = reader.ReadInt32 ();
100 fs.Position = caption_offset;
101 caption = reader.ReadString ();
102 for (int i = 0; i < Count; i++){
103 fs.Position = offsets [i];
104 string url = reader.ReadString ();
105 topics.Add (new Topic (caption, string.Empty, url));
110 // Regular constructor
114 topics = new List<Topic> ();
117 public void WriteTopics (IndexMaker maker, Stream stream, BinaryWriter writer)
120 // Convention: entries with the same SortKey should have the same Caption
122 Position = (int) stream.Position;
123 writer.Write (Count);
128 writer.Write (maker.GetCode (topics[0].Caption));
129 foreach (Topic t in topics)
130 writer.Write (maker.GetCode (t.Url));
134 public class IndexMaker
136 Dictionary<string, IndexEntry> entries = new Dictionary<string, IndexEntry> ();
137 Dictionary<string, int> all_strings = new Dictionary<string, int> ();
140 void AddString (string str)
142 if (!all_strings.ContainsKey (str))
143 all_strings.Add (str, 0);
146 public void AddTopic (Topic topic)
149 if (!entries.TryGetValue (topic.SortKey, out entry)) {
150 entry = new IndexEntry ();
151 entries[topic.SortKey] = entry;
154 AddString (topic.SortKey);
155 AddString (topic.Caption);
156 AddString (topic.Url);
160 public void Add (string caption, string sort_key, string url)
162 Topic t = new Topic (caption, sort_key, url);
166 void SaveStringTable (Stream stream, BinaryWriter writer)
168 var keys = new List<string> (all_strings.Keys);
169 foreach (string s in keys) {
170 int pos = (int) stream.Position;
172 all_strings [s] = pos;
176 public int GetCode (string s)
178 return all_strings [s];
181 void SaveTopics (Stream stream, BinaryWriter writer)
184 // Convention: entries with the same SortKey should have the same Caption
186 foreach (IndexEntry e in entries.Values)
187 e.WriteTopics (this, stream, writer);
190 void SaveIndexEntries (Stream stream, BinaryWriter writer)
192 index_position = (int) stream.Position;
193 writer.Write (entries.Count);
194 var keys = new List<string> (entries.Keys);
195 keys.Sort (StringComparer.OrdinalIgnoreCase);
197 foreach (string s in keys){
198 IndexEntry e = entries [s];
199 writer.Write (e.Position);
203 public void Save (string filename)
205 Encoding utf8 = new UTF8Encoding (false, true);
207 using (FileStream fs = File.OpenWrite (filename)){
208 BinaryWriter writer = new BinaryWriter (fs, utf8);
209 writer.Write (new byte [] { (byte) 'M',
210 (byte) 'o', (byte) 'i',
213 // Leave room for pointer
216 SaveStringTable (fs, writer);
217 SaveTopics (fs, writer);
219 // index_position is set here
221 SaveIndexEntries (fs, writer);
224 writer.Write (index_position);
229 public interface IListModel
232 string GetValue (int row);
233 string GetDescription (int row);
236 public class IndexReader : IListModel
238 Encoding utf8 = new UTF8Encoding (false, true);
242 // The offset of the table of entries
246 static public IndexReader Load (string filename)
248 if (!File.Exists (filename))
252 return new IndexReader (filename);
258 IndexReader (string filename)
260 fs = File.OpenRead (filename);
261 reader = new BinaryReader (fs, utf8);
263 if (fs.ReadByte () != 'M' ||
264 fs.ReadByte () != 'o' ||
265 fs.ReadByte () != 'i' ||
266 fs.ReadByte () != 'x'){
267 throw new Exception ("Corrupt index");
270 // Seek to index_entries
271 fs.Position = reader.ReadInt32 ();
273 entries = reader.ReadInt32 ();
275 table_offset = (int) fs.Position;
284 public string GetValue (int row)
286 fs.Position = row * 4 + table_offset;
287 fs.Position = reader.ReadInt32 () + 4;
288 int code = reader.ReadInt32 ();
290 string caption = reader.ReadString ();
295 public string GetDescription (int row)
297 return GetValue (row);
300 public IndexEntry GetIndexEntry (int row)
302 fs.Position = row * 4 + table_offset;
303 int entry_offset = reader.ReadInt32 ();
304 fs.Position = entry_offset;
306 return new IndexEntry (fs, reader, entry_offset);