a575963da9
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
311 lines
6.5 KiB
C#
311 lines
6.5 KiB
C#
//
|
|
// 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);
|
|
}
|
|
}
|
|
}
|
|
|