1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
|
//
// index.cs: Handling of the index files
//
// Author:
// Miguel de Icaza (miguel@xamarin.com)
//
// (C) 2003 Ximian, Inc.
// Copyright 2003-2011 Novell Inc
// Copyright 2011 Xamarin 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".
//
//
using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
namespace Monodoc
{
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
{
List<Topic> topics;
public int Position {
get;
private set;
}
public IList<Topic> Topics {
get {
return topics.AsReadOnly ();
}
}
public int Count {
get;
private set;
}
public void Add (Topic t)
{
Count++;
topics.Add (t);
}
public Topic this [int idx] {
get {
if (idx < 0 || idx > topics.Count)
throw new ArgumentOutOfRangeException ("idx");
return topics[idx];
}
}
//
// Constructor from a stream
//
public IndexEntry (FileStream fs, BinaryReader reader, int position)
{
Count = reader.ReadInt32 ();
int caption_offset = reader.ReadInt32 ();
string caption;
topics = new List<Topic> (Count);
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 ();
topics.Add (new Topic (caption, string.Empty, url));
}
}
//
// Regular constructor
public IndexEntry ()
{
topics = new List<Topic> ();
}
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 (Count == 0)
return;
writer.Write (maker.GetCode (topics[0].Caption));
foreach (Topic t in topics)
writer.Write (maker.GetCode (t.Url));
}
}
public class IndexMaker
{
Dictionary<string, IndexEntry> entries = new Dictionary<string, IndexEntry> ();
Dictionary<string, int> all_strings = new Dictionary<string, int> ();
int index_position;
void AddString (string str)
{
if (!all_strings.ContainsKey (str))
all_strings.Add (str, 0);
}
public void AddTopic (Topic topic)
{
IndexEntry entry;
if (!entries.TryGetValue (topic.SortKey, out entry)) {
entry = new IndexEntry ();
entries[topic.SortKey] = entry;
}
AddString (topic.SortKey);
AddString (topic.Caption);
AddString (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)
{
var keys = new List<string> (all_strings.Keys);
foreach (string s in keys) {
int pos = (int) stream.Position;
writer.Write (s);
all_strings [s] = pos;
}
}
public int GetCode (string s)
{
return all_strings [s];
}
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);
var keys = new List<string> (entries.Keys);
keys.Sort (StringComparer.OrdinalIgnoreCase);
foreach (string s in keys){
IndexEntry e = entries [s];
writer.Write (e.Position);
}
}
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);
}
}
}
|