Browse Source

XML Parser: Element caching

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4666 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
David Srbecký 17 years ago
parent
commit
7f6a0fd9c4
  1. 27
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/RawObjects.cs
  2. 60
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/XmlParser.cs

27
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/RawObjects.cs

@ -586,7 +586,11 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -586,7 +586,11 @@ namespace ICSharpCode.AvalonEdit.XmlParser
public string ClosingBracket { get; internal set; }
/// <summary> True if tag starts with "&lt;" </summary>
public bool IsStartTag { get { return OpeningBracket == "<"; } }
public bool IsStartOrEmptyTag { get { return OpeningBracket == "<"; } }
/// <summary> True if tag starts with "&lt;" and ends with "&gt;" </summary>
public bool IsStartTag { get { return OpeningBracket == "<" && ClosingBracket == ">"; } }
/// <summary> True if tag starts with "&lt;" and does not end with "&gt;" </summary>
public bool IsEmptyTag { get { return OpeningBracket == "<" && ClosingBracket != ">" ; } }
/// <summary> True if tag starts with "&lt;/" </summary>
public bool IsEndTag { get { return OpeningBracket == "</"; } }
/// <summary> True if tag starts with "&lt;?" </summary>
@ -600,18 +604,6 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -600,18 +604,6 @@ namespace ICSharpCode.AvalonEdit.XmlParser
/// <summary> True if tag starts with "&lt;!" </summary>
public bool IsUnknownBang { get { return OpeningBracket == "<!"; } }
// TODO: Remove
internal static RawTag MakeEmpty(int offset)
{
return new RawTag() {
OpeningBracket = string.Empty,
Name = string.Empty,
ClosingBracket = string.Empty,
StartOffset = offset,
EndOffset = offset,
};
}
internal override void DebugCheckConsistency(bool allowNullParent)
{
Assert(OpeningBracket != null, "Null OpeningBracket");
@ -659,6 +651,13 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -659,6 +651,13 @@ namespace ICSharpCode.AvalonEdit.XmlParser
/// </summary>
public class RawElement: RawContainer
{
/// <summary> No tags are missing anywhere within this element (recursive) </summary>
public bool IsProperlyNested { get; set; }
/// <returns> True in wellformed XML </returns>
public bool HasStartOrEmptyTag { get; set; }
/// <returns> True in wellformed XML </returns>
public bool HasEndTag { get; set; }
/// <summary> StartTag of an element. </summary>
public RawTag StartTag {
get {
@ -811,7 +810,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -811,7 +810,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser
/// <inheritdoc/>
public override string ToString()
{
return string.Format("[{0} '{1}{2}{3}' Attr:{4} Chld:{5}]", base.ToString(), this.StartTag.OpeningBracket, this.StartTag.Name, this.StartTag.ClosingBracket, this.StartTag.Children.Count, this.Children.Count);
return string.Format("[{0} '{1}{2}{3}' Attr:{4} Chld:{5} Nest:{6}]", base.ToString(), this.StartTag.OpeningBracket, this.StartTag.Name, this.StartTag.ClosingBracket, this.StartTag.Children.Count, this.Children.Count, this.IsProperlyNested ? "Ok" : "Bad");
}
}

60
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/XmlParser.cs

@ -296,22 +296,22 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -296,22 +296,22 @@ namespace ICSharpCode.AvalonEdit.XmlParser
Dictionary<string, string> stringCache = new Dictionary<string, string>();
int stringCacheRequestedCount;
int stringCacheRequestedSize;
int stringCacheSavedCount;
int stringCacheSavedSize;
int stringCacheStoredCount;
int stringCacheStoredSize;
string GetCachedString(string cached)
{
stringCacheRequestedCount += 1;
stringCacheRequestedSize += 8 + 2 * cached.Length;
// Do not bother with long strings
//if (cached.Length <= 32) return cached;
if (cached.Length <= 32) return cached;
if (stringCache.ContainsKey(cached)) {
// Get the instance from the cache instead
stringCacheSavedCount += 1;
stringCacheSavedSize += 8 + 2 * cached.Length;
return stringCache[cached];
} else {
// Add to cache
stringCacheStoredCount += 1;
stringCacheStoredSize += 8 + 2 * cached.Length;
stringCache.Add(cached, cached);
return cached;
}
@ -319,7 +319,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -319,7 +319,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser
void PrintStringCacheStats()
{
Log("String cache: Requested {0} ({1} bytes); Saved {2} ({3} bytes); {4}% Saved", stringCacheRequestedCount, stringCacheRequestedSize, stringCacheSavedCount, stringCacheSavedSize, stringCacheRequestedSize == 0 ? 0 : stringCacheSavedSize * 100 / stringCacheRequestedSize);
Log("String cache: Requested {0} ({1} bytes); Actaully stored {2} ({3} bytes); {4}% stored", stringCacheRequestedCount, stringCacheRequestedSize, stringCacheStoredCount, stringCacheStoredSize, stringCacheRequestedSize == 0 ? 0 : stringCacheStoredSize * 100 / stringCacheRequestedSize);
}
void OnSyntaxError(RawObject obj, string message, params object[] args)
@ -594,7 +594,12 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -594,7 +594,12 @@ namespace ICSharpCode.AvalonEdit.XmlParser
if (IsEndOfFile()) {
break;
} else if (TryPeek('<')) {
stream.Add(ReadTag());
RawElement elem;
if (TryReadFromCacheOrNew(e => e.IsProperlyNested, out elem)) {
stream.Add(elem);
} else {
stream.Add(ReadTag());
}
} else {
stream.AddRange(ReadText(RawTextType.CharacterData));
}
@ -619,7 +624,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -619,7 +624,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser
// It identifies the type of tag and parsing behavior for the rest of it
tag.OpeningBracket = ReadOpeningBracket();
if (tag.IsStartTag || tag.IsEndTag || tag.IsProcessingInstruction) {
if (tag.IsStartOrEmptyTag || tag.IsEndTag || tag.IsProcessingInstruction) {
// Read the name
string name;
if (TryReadName(out name)) {
@ -632,7 +637,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -632,7 +637,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser
tag.Name = name;
}
if (tag.IsStartTag || tag.IsEndTag) {
if (tag.IsStartOrEmptyTag || tag.IsEndTag) {
// Read attributes for the tag
while(true) {
// Chech for all forbiden 'name' charcters first - see ReadName
@ -682,7 +687,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -682,7 +687,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser
int brStart = currentLocation - (tag.ClosingBracket ?? string.Empty).Length;
if (tag.Name == null) {
// One error was reported already
} else if (tag.IsStartTag) {
} else if (tag.IsStartOrEmptyTag) {
if (tag.ClosingBracket != ">" && tag.ClosingBracket != "/>") OnSyntaxError(tag, brStart, currentLocation, "'>' or '/>' expected");
} else if (tag.IsEndTag) {
if (tag.ClosingBracket != ">") OnSyntaxError(tag, brStart, currentLocation, "'>' expected");
@ -1217,8 +1222,6 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -1217,8 +1222,6 @@ namespace ICSharpCode.AvalonEdit.XmlParser
#region Object stream reading
// TODO: Caching, RawElement.IsPropertlyNested
string PrintObjects(IEnumerable<RawObject> objs)
{
StringBuilder sb = new StringBuilder();
@ -1293,7 +1296,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -1293,7 +1296,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser
RawTag currTag = (RawTag)curr;
if (currTag == StartTagPlaceholder) {
return ReadElement(objStream);
} else if (currTag.IsStartTag) {
} else if (currTag.IsStartOrEmptyTag) {
return ReadElement(objStream);
} else {
return ReadSingleObject(objStream);
@ -1306,29 +1309,32 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -1306,29 +1309,32 @@ namespace ICSharpCode.AvalonEdit.XmlParser
AssertIsEndOfFile();
RawElement element = new RawElement();
element.IsProperlyNested = true;
// Read start tag
RawTag startTag = ReadSingleObject(objStream) as RawTag;
DebugAssert(startTag != null, "Start tag expected");
DebugAssert(startTag.IsStartTag || startTag == StartTagPlaceholder, "Start tag expected");
DebugAssert(startTag.IsStartOrEmptyTag || startTag == StartTagPlaceholder, "Start tag expected");
if (startTag == StartTagPlaceholder) {
int offset = objStream.Current.StartOffset; // This the next object
element.AddChild(RawTag.MakeEmpty(offset));
element.HasStartOrEmptyTag = false;
element.IsProperlyNested = false;
OnSyntaxError(element, objStream.Current.StartOffset, objStream.Current.EndOffset,
"Matching openning tag was not found");
} else {
element.HasStartOrEmptyTag = true;
element.AddChild(startTag);
}
// Read content and end tag
if (element.StartTag.ClosingBracket == ">" || startTag == StartTagPlaceholder) {
if (element.StartTag.IsStartTag || startTag == StartTagPlaceholder) {
while(true) {
RawTag currTag = objStream.Current as RawTag; // Peek
if (currTag == EndTagPlaceholder) {
OnSyntaxError(element, element.LastChild.EndOffset, element.LastChild.EndOffset,
"Expected '</{0}>'", element.StartTag.Name);
ReadSingleObject(objStream);
element.AddChild(RawTag.MakeEmpty(element.LastChild.EndOffset));
element.HasEndTag = false;
element.IsProperlyNested = false;
break;
} else if (currTag != null && currTag.IsEndTag) {
if (currTag.Name != element.StartTag.Name) {
@ -1336,15 +1342,20 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -1336,15 +1342,20 @@ namespace ICSharpCode.AvalonEdit.XmlParser
"Expected '{0}'. End tag must have same name as start tag.", element.StartTag.Name);
}
element.AddChild(ReadSingleObject(objStream));
element.HasEndTag = true;
break;
}
RawObject nested = ReadTextOrElement(objStream);
if (nested is RawElement) {
if (!((RawElement)nested).IsProperlyNested)
element.IsProperlyNested = false;
element.AddChildren(Split((RawElement)nested).ToList());
} else {
element.AddChild(nested);
}
}
} else {
element.HasEndTag = false;
}
element.StartOffset = element.FirstChild.StartOffset;
@ -1358,7 +1369,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -1358,7 +1369,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser
{
int myIndention = GetIndentLevel(elem);
// If has virtual end and is indented
if (string.IsNullOrEmpty(((RawTag)elem.LastChild).ClosingBracket) && myIndention != -1) {
if (!elem.HasEndTag && myIndention != -1) {
int lastAccepted = 0; // Accept start tag
while (lastAccepted + 1 < elem.Children.Count - 1 /* no end tag */) {
RawObject nextItem = elem.Children[lastAccepted + 1];
@ -1380,8 +1391,9 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -1380,8 +1391,9 @@ namespace ICSharpCode.AvalonEdit.XmlParser
}
Log("Splitting {0} - take {1} of {2} nested", elem, lastAccepted, elem.Children.Count - 2);
RawElement topHalf = new RawElement();
topHalf.HasStartOrEmptyTag = elem.HasStartOrEmptyTag;
topHalf.HasEndTag = elem.HasEndTag;
topHalf.AddChildren(elem.Children.Take(lastAccepted + 1)); // Start tag + nested
topHalf.AddChild(RawTag.MakeEmpty(topHalf.LastChild.EndOffset)); // End tag
topHalf.StartOffset = topHalf.FirstChild.StartOffset;
topHalf.EndOffset = topHalf.LastChild.EndOffset;
OnSyntaxError(topHalf, topHalf.LastChild.EndOffset, topHalf.LastChild.EndOffset,
@ -1522,9 +1534,9 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -1522,9 +1534,9 @@ namespace ICSharpCode.AvalonEdit.XmlParser
Log("Processing {0}", obj);
RawTag tag = obj as RawTag;
if (!(obj is RawTag || obj is RawText || obj is RawElement))
throw new Exception(obj.GetType().Name + " not expected");
// TODO: Assert Element properly nested
Assert(obj is RawTag || obj is RawText || obj is RawElement, obj.GetType().Name + " not expected");
if (obj is RawElement)
Assert(((RawElement)obj).IsProperlyNested, "Element not proprly nested");
Configurations newConfigs = new Configurations();
@ -1534,7 +1546,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -1534,7 +1546,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser
var oldDocument = oldConfig.Document;
int oldCost = oldConfig.Cost;
if (tag != null && tag.IsStartTag && tag.ClosingBracket == ">") {
if (tag != null && tag.IsStartTag) {
newConfigs.Add(new Configuration { // Push start-tag (cost 0)
StartTags = oldStartTags.Push(tag),
Document = oldDocument.Push(tag),

Loading…
Cancel
Save