📄 dompersistence.cs
字号:
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
// <version>$Revision: 1307 $</version>
// </file>
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.IO;
using ICSharpCode.Core;
namespace ICSharpCode.SharpDevelop.Dom
{
/// <summary>
/// This class can write Dom entity into a binary file for fast loading.
/// </summary>
public static class DomPersistence
{
public const long FileMagic = 0x11635233ED2F428C;
public const long IndexFileMagic = 0x11635233ED2F427D;
public const short FileVersion = 6;
#region Cache management
#if DEBUG
const string tempPathName = "SharpDevelop/DomCacheDebug";
#else
const string tempPathName = "SharpDevelop/DomCache";
#endif
static string MakeTempPath()
{
string tempPath = Path.Combine(Path.GetTempPath(), tempPathName);
if (!Directory.Exists(tempPath))
Directory.CreateDirectory(tempPath);
return tempPath;
}
public static string SaveProjectContent(ReflectionProjectContent pc)
{
string assemblyFullName = pc.AssemblyFullName;
int pos = assemblyFullName.IndexOf(',');
string fileName = Path.Combine(MakeTempPath(),
assemblyFullName.Substring(0, pos)
+ "." + assemblyFullName.GetHashCode().ToString("x", CultureInfo.InvariantCulture)
+ "." + pc.AssemblyLocation.GetHashCode().ToString("x", CultureInfo.InvariantCulture)
+ ".dat");
AddFileNameToCacheIndex(Path.GetFileName(fileName), pc);
using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write)) {
using (BinaryWriter writer = new BinaryWriter(fs)) {
new ReadWriteHelper(writer).WriteProjectContent(pc);
}
}
return fileName;
}
public static ReflectionProjectContent LoadProjectContentByAssemblyName(string assemblyName)
{
string cacheFileName;
if (CacheIndex.TryGetValue(assemblyName, out cacheFileName)) {
cacheFileName = Path.Combine(MakeTempPath(), cacheFileName);
if (File.Exists(cacheFileName)) {
return LoadProjectContent(cacheFileName);
}
}
return null;
}
public static ReflectionProjectContent LoadProjectContent(string cacheFileName)
{
ReflectionProjectContent pc;
using (FileStream fs = new FileStream(cacheFileName, FileMode.Open, FileAccess.Read)) {
using (BinaryReader reader = new BinaryReader(fs)) {
try {
pc = new ReadWriteHelper(reader).ReadProjectContent();
} catch (EndOfStreamException) {
LoggingService.Warn("Read dom: EndOfStreamException");
return null;
}
}
}
if (pc != null) {
pc.InitializeSpecialClasses();
}
return pc;
}
#endregion
#region Cache index
static string GetIndexFileName() { return Path.Combine(MakeTempPath(), "index.dat"); }
static Dictionary<string, string> cacheIndex;
static Dictionary<string, string> CacheIndex {
get {
if (cacheIndex == null) {
cacheIndex = LoadCacheIndex();
}
return cacheIndex;
}
}
static Dictionary<string, string> LoadCacheIndex()
{
string indexFile = GetIndexFileName();
Dictionary<string, string> list = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
if (File.Exists(indexFile)) {
try {
using (FileStream fs = new FileStream(indexFile, FileMode.Open, FileAccess.Read)) {
using (BinaryReader reader = new BinaryReader(fs)) {
if (reader.ReadInt64() != IndexFileMagic) {
LoggingService.Warn("Index cache has wrong file magic");
return list;
}
if (reader.ReadInt16() != FileVersion) {
LoggingService.Warn("Index cache has wrong file version");
return list;
}
int count = reader.ReadInt32();
for (int i = 0; i < count; i++) {
string key = reader.ReadString();
list[key] = reader.ReadString();
}
}
}
} catch (IOException ex) {
LoggingService.Warn("Error reading DomPersistance cache index", ex);
}
}
return list;
}
static void SaveCacheIndex(Dictionary<string, string> cacheIndex)
{
string indexFile = GetIndexFileName();
using (FileStream fs = new FileStream(indexFile, FileMode.Create, FileAccess.Write)) {
using (BinaryWriter writer = new BinaryWriter(fs)) {
writer.Write(IndexFileMagic);
writer.Write(FileVersion);
writer.Write(cacheIndex.Count);
foreach (KeyValuePair<string, string> e in cacheIndex) {
writer.Write(e.Key);
writer.Write(e.Value);
}
}
}
}
static void AddFileNameToCacheIndex(string cacheFile, ReflectionProjectContent pc)
{
Dictionary<string, string> l = LoadCacheIndex();
l[pc.AssemblyLocation] = cacheFile;
string txt = pc.AssemblyFullName;
l[txt] = cacheFile;
int pos = txt.LastIndexOf(',');
do {
txt = txt.Substring(0, pos);
if (l.ContainsKey(txt))
break;
l[txt] = cacheFile;
pos = txt.LastIndexOf(',');
} while (pos >= 0);
SaveCacheIndex(l);
cacheIndex = l;
}
#endregion
private struct ClassNameTypeCountPair {
public readonly string ClassName;
public readonly byte TypeParameterCount;
public ClassNameTypeCountPair(IClass c) {
this.ClassName = c.FullyQualifiedName;
this.TypeParameterCount = (byte)c.TypeParameters.Count;
}
public ClassNameTypeCountPair(IReturnType rt) {
this.ClassName = rt.FullyQualifiedName;
this.TypeParameterCount = (byte)rt.TypeParameterCount;
}
public override bool Equals(object obj) {
if (!(obj is ClassNameTypeCountPair)) return false;
ClassNameTypeCountPair myClassNameTypeCountPair = (ClassNameTypeCountPair)obj;
if (!ClassName.Equals(myClassNameTypeCountPair.ClassName, StringComparison.InvariantCultureIgnoreCase)) return false;
if (TypeParameterCount != myClassNameTypeCountPair.TypeParameterCount) return false;
return true;
}
public override int GetHashCode() {
return StringComparer.InvariantCultureIgnoreCase.GetHashCode(ClassName) ^ ((int)TypeParameterCount * 5);
}
}
public sealed class ReadWriteHelper
{
ReflectionProjectContent pc;
readonly BinaryWriter writer;
readonly Dictionary<ClassNameTypeCountPair, int> classIndices = new Dictionary<ClassNameTypeCountPair, int>();
readonly Dictionary<string, int> stringDict = new Dictionary<string, int>();
readonly BinaryReader reader;
IReturnType[] types;
string[] stringArray;
#region Write/Read ProjectContent
public ReadWriteHelper(BinaryWriter writer)
{
this.writer = writer;
}
public void WriteProjectContent(ReflectionProjectContent pc)
{
this.pc = pc;
writer.Write(FileMagic);
writer.Write(FileVersion);
writer.Write(pc.AssemblyFullName);
writer.Write(pc.AssemblyLocation);
long time = 0;
try {
time = File.GetLastWriteTimeUtc(pc.AssemblyLocation).ToFileTime();
} catch {}
writer.Write(time);
writer.Write(pc.ReferencedAssemblies.Length);
foreach (AssemblyName name in pc.ReferencedAssemblies) {
writer.Write(name.FullName);
}
WriteClasses();
}
public ReadWriteHelper(BinaryReader reader)
{
this.reader = reader;
}
public ReflectionProjectContent ReadProjectContent()
{
if (reader.ReadInt64() != FileMagic) {
LoggingService.Warn("Read dom: wrong magic");
return null;
}
if (reader.ReadInt16() != FileVersion) {
LoggingService.Warn("Read dom: wrong version");
return null;
}
string assemblyName = reader.ReadString();
string assemblyLocation = reader.ReadString();
long time = 0;
try {
time = File.GetLastWriteTimeUtc(assemblyLocation).ToFileTime();
} catch {}
if (reader.ReadInt64() != time) {
LoggingService.Warn("Read dom: assembly changed since cache was created");
return null;
}
AssemblyName[] referencedAssemblies = new AssemblyName[reader.ReadInt32()];
for (int i = 0; i < referencedAssemblies.Length; i++) {
referencedAssemblies[i] = new AssemblyName(reader.ReadString());
}
this.pc = new ReflectionProjectContent(assemblyName, assemblyLocation, referencedAssemblies);
if (ReadClasses()) {
return pc;
} else {
LoggingService.Warn("Read dom: error in file (invalid control mark)");
return null;
}
}
void WriteClasses()
{
ICollection<IClass> classes = pc.Classes;
classIndices.Clear();
stringDict.Clear();
int i = 0;
foreach (IClass c in classes) {
classIndices[new ClassNameTypeCountPair(c)] = i;
i += 1;
}
List<ClassNameTypeCountPair> externalTypes = new List<ClassNameTypeCountPair>();
List<string> stringList = new List<string>();
CreateExternalTypeList(externalTypes, stringList, classes.Count, classes);
writer.Write(classes.Count);
writer.Write(externalTypes.Count);
foreach (IClass c in classes) {
writer.Write(c.FullyQualifiedName);
}
foreach (ClassNameTypeCountPair type in externalTypes) {
writer.Write(type.ClassName);
writer.Write(type.TypeParameterCount);
}
writer.Write(stringList.Count);
foreach (string text in stringList) {
writer.Write(text);
}
foreach (IClass c in classes) {
WriteClass(c);
// BinaryReader easily reads junk data when the file does not have the
// expected format, so we put a checking byte after each class.
writer.Write((byte)64);
}
}
bool ReadClasses()
{
int classCount = reader.ReadInt32();
int externalTypeCount = reader.ReadInt32();
types = new IReturnType[classCount + externalTypeCount];
DefaultClass[] classes = new DefaultClass[classCount];
for (int i = 0; i < classes.Length; i++) {
DefaultClass c = new DefaultClass(pc.AssemblyCompilationUnit, reader.ReadString());
classes[i] = c;
types[i] = c.DefaultReturnType;
}
for (int i = classCount; i < types.Length; i++) {
string name = reader.ReadString();
types[i] = new GetClassReturnType(pc, name, reader.ReadByte());
}
stringArray = new string[reader.ReadInt32()];
for (int i = 0; i < stringArray.Length; i++) {
stringArray[i] = reader.ReadString();
}
for (int i = 0; i < classes.Length; i++) {
ReadClass(classes[i]);
pc.AddClassToNamespaceList(classes[i]);
if (reader.ReadByte() != 64) {
return false;
}
}
return true;
}
#endregion
#region Write/Read Class
IClass currentClass;
void WriteClass(IClass c)
{
this.currentClass = c;
WriteTemplates(c.TypeParameters);
writer.Write(c.BaseTypes.Count);
foreach (IReturnType type in c.BaseTypes) {
WriteType(type);
}
writer.Write((int)c.Modifiers);
if (c is DefaultClass) {
writer.Write(((DefaultClass)c).Flags);
} else {
writer.Write((byte)0);
}
writer.Write((byte)c.ClassType);
WriteAttributes(c.Attributes);
writer.Write(c.InnerClasses.Count);
foreach (IClass innerClass in c.InnerClasses) {
writer.Write(innerClass.FullyQualifiedName);
WriteClass(innerClass);
}
this.currentClass = c;
writer.Write(c.Methods.Count);
foreach (IMethod method in c.Methods) {
WriteMethod(method);
}
writer.Write(c.Properties.Count);
foreach (IProperty property in c.Properties) {
WriteProperty(property);
}
writer.Write(c.Events.Count);
foreach (IEvent evt in c.Events) {
WriteEvent(evt);
}
writer.Write(c.Fields.Count);
foreach (IField field in c.Fields) {
WriteField(field);
}
this.currentClass = null;
}
void WriteTemplates(IList<ITypeParameter> list)
{
// read code exists twice: in ReadClass and ReadMethod
writer.Write((byte)list.Count);
foreach (ITypeParameter typeParameter in list) {
WriteString(typeParameter.Name);
}
foreach (ITypeParameter typeParameter in list) {
writer.Write(typeParameter.Constraints.Count);
foreach (IReturnType type in typeParameter.Constraints) {
WriteType(type);
}
}
}
void ReadClass(DefaultClass c)
{
this.currentClass = c;
int count;
count = reader.ReadByte();
for (int i = 0; i < count; i++) {
c.TypeParameters.Add(new DefaultTypeParameter(c, ReadString(), i));
}
if (count > 0) {
foreach (ITypeParameter typeParameter in c.TypeParameters) {
count = reader.ReadInt32();
for (int i = 0; i < count; i++) {
typeParameter.Constraints.Add(ReadType());
}
}
} else {
c.TypeParameters = DefaultTypeParameter.EmptyTypeParameterList;
}
count = reader.ReadInt32();
for (int i = 0; i < count; i++) {
c.BaseTypes.Add(ReadType());
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -