diff --git a/Implab.Format.Test/JsonTests.cs b/Implab.Format.Test/JsonTests.cs --- a/Implab.Format.Test/JsonTests.cs +++ b/Implab.Format.Test/JsonTests.cs @@ -9,8 +9,8 @@ using System.IO; namespace Implab.Format.Test { [TestFixture] - public class JsonTests { - + public class JsonTests { + [Test] public void TestScannerValidTokens() { using (var scanner = JsonStringScanner.Create(@"9123, -123, 0, 0.1, -0.2, -0.1e3, 1.3E-3, ""some \t\n\u0020 text"", literal []{}:")) { @@ -114,10 +114,36 @@ namespace Implab.Format.Test { DumpJsonParse("[{\"info\": [7,8,9]}]"); DumpJsonFlatParse("[1,2,\"\",[3,4],{\"info\": [5,6]},{\"num\": [7,8,null]}, null,[null]]"); } - + + [Test] + public void JsonBenchmark() { + var t = Environment.TickCount; + using (var reader = new JsonXmlReader(JsonReader.Create("e:\\citylots.json"), new JsonXmlReaderOptions { NamespaceUri = "XmlReaderSimpleTest", RootName = "data" })) { + while (reader.Read()) { + } + } + Console.WriteLine($"JsonXmlReader: {Environment.TickCount - t} ms"); + + t = Environment.TickCount; + using(var reader = JsonReader.Create("e:\\citylots.json")) { + while(reader.Read()) { + } + } + + Console.WriteLine($"JsonReader: {Environment.TickCount - t} ms"); + + t = Environment.TickCount; + using (var reader = XmlReader.Create("file:///e:\\citylots.xml")) { + while (reader.Read()) { + } + } + + Console.WriteLine($"XmlReader: {Environment.TickCount - t} ms"); + } + void AssertRead(XmlReader reader, XmlNodeType expected) { Assert.IsTrue(reader.Read()); - Console.WriteLine($"{new string(' ', reader.Depth*2)}{reader}"); + Console.WriteLine($"{new string(' ', reader.Depth * 2)}{reader}"); Assert.AreEqual(expected, reader.NodeType); } diff --git a/Implab.Playground/Program.cs b/Implab.Playground/Program.cs --- a/Implab.Playground/Program.cs +++ b/Implab.Playground/Program.cs @@ -78,103 +78,13 @@ namespace Implab.Playground { static void Main(string[] args) { - //var queue = new ConcurrentQueue(); - var queue = new AsyncQueue(); - //var queue = new SimpleAsyncQueue(); - - const int wBatch = 32; - const long wCount = 1000000; - const long total = wBatch * wCount * 3; - - long r1 = 0, r2 = 0, r3 = 0; - const int rBatch = 1000; - long read = 0; - - var t1 = Environment.TickCount; - - AsyncPool.RunThread( - () => { - var buffer = new int[wBatch]; - for (int i = 0; i < wBatch; i++) - buffer[i] = 1; - - for (int i = 0; i < wCount; i++) - EnqueueRange(queue, buffer, 0, wBatch); - Console.WriteLine("done writer #1: {0} ms", Environment.TickCount - t1); - }, - () => { - var buffer = new int[wBatch]; - for (int i = 0; i < wBatch; i++) - buffer[i] = 1; - - for (int i = 0; i < wCount; i++) - EnqueueRange(queue, buffer, 0, wBatch); - Console.WriteLine("done writer #2: {0} ms", Environment.TickCount - t1); - }, - () => { - var buffer = new int[wBatch]; - for (int i = 0; i < wBatch; i++) - buffer[i] = 1; - - for (int i = 0; i < wCount; i++) - EnqueueRange(queue, buffer, 0, wBatch); - Console.WriteLine("done writer #3: {0} ms", Environment.TickCount - t1); - }, - () => { - var buffer = new int[rBatch]; - - while (read < total) { - int actual; - if (TryDequeueRange(queue, buffer, 0, rBatch, out actual)) { - for (int i = 0; i < actual; i++) - r1 += buffer[i]; - Interlocked.Add(ref read, actual); - } - } - - Console.WriteLine("done reader #1: {0} ms", Environment.TickCount - t1); - }/*, - () => { - var buffer = new int[rBatch]; - - while (read < total) { - int actual; - if (TryDequeueRange(queue, buffer, 0, rBatch, out actual)) { - for (int i = 0; i < actual; i++) - r2 += buffer[i]; - Interlocked.Add(ref read, actual); - } - } - - Console.WriteLine("done reader #2: {0} ms", Environment.TickCount - t1); - }*//*, - () => { - var buffer = new int[rBatch]; - - while (read < total) { - int actual; - if (TryDequeueRange(queue, buffer, 0, rBatch, out actual)) { - for (int i = 0; i < actual; i++) - r3 += buffer[i]; - Interlocked.Add(ref read, actual); - } - } - - Console.WriteLine("done reader #3: {0} ms", Environment.TickCount - t1); - }*/ - ) - .PromiseAll() - .Join(); - - - Console.WriteLine( - "done: {0} ms, summ#1: {1}, summ#2: {2}, total: {3}, count: {4}", - Environment.TickCount - t1, - r1, - r2, - r1 + r2 + r3, - total - ); + var t = Environment.TickCount; + using (var reader = JsonReader.Create("e:\\citylots.json")) { + while (reader.Read()) { + } + } + + Console.WriteLine($"JsonReader: {Environment.TickCount - t} ms"); Console.WriteLine("done"); } diff --git a/Implab/Automaton/AutomatonConst.cs b/Implab/Automaton/AutomatonConst.cs --- a/Implab/Automaton/AutomatonConst.cs +++ b/Implab/Automaton/AutomatonConst.cs @@ -1,9 +1,9 @@  namespace Implab.Automaton { public static class AutomatonConst { - public const int UNREACHABLE_STATE = -1; + public const int UnreachableState = -1; - public const int UNCLASSIFIED_INPUT = 0; + public const int UnclassifiedInput = 0; } } diff --git a/Implab/Automaton/DFATable.cs b/Implab/Automaton/DFATable.cs --- a/Implab/Automaton/DFATable.cs +++ b/Implab/Automaton/DFATable.cs @@ -116,7 +116,7 @@ namespace Implab.Automaton { for (int i = 0; i < StateCount; i++) for (int j = 0; j < AlphabetSize; j++) - table[i, j] = AutomatonConst.UNREACHABLE_STATE; + table[i, j] = AutomatonConst.UnreachableState; foreach (var t in this) table[t.s1,t.edge] = (byte)t.s2; @@ -290,11 +290,11 @@ namespace Implab.Automaton { var nextCls = 0; foreach (var item in minClasses) { - if (nextCls == AutomatonConst.UNCLASSIFIED_INPUT) + if (nextCls == AutomatonConst.UnclassifiedInput) nextCls++; // сохраняем DFAConst.UNCLASSIFIED_INPUT - var cls = item.Contains(AutomatonConst.UNCLASSIFIED_INPUT) ? AutomatonConst.UNCLASSIFIED_INPUT : nextCls++; + var cls = item.Contains(AutomatonConst.UnclassifiedInput) ? AutomatonConst.UnclassifiedInput : nextCls++; optimalDFA.AddSymbol(cls); foreach (var a in item) @@ -326,7 +326,7 @@ namespace Implab.Automaton { data.Add(String.Format( "{0} -> {2} [label={1}];", String.Join("", stateAlphabet.GetSymbols(t.s1)), - ToLiteral(ToLiteral(String.Join("", t.edge == AutomatonConst.UNCLASSIFIED_INPUT ? new [] { "@" } : inputAlphabet.GetSymbols(t.edge).Select(x => x.ToString())))), + ToLiteral(ToLiteral(String.Join("", t.edge == AutomatonConst.UnclassifiedInput ? new [] { "@" } : inputAlphabet.GetSymbols(t.edge).Select(x => x.ToString())))), String.Join("", stateAlphabet.GetSymbols(t.s2)) )); data.Add("}"); diff --git a/Implab/Automaton/MapAlphabet.cs b/Implab/Automaton/MapAlphabet.cs --- a/Implab/Automaton/MapAlphabet.cs +++ b/Implab/Automaton/MapAlphabet.cs @@ -54,7 +54,7 @@ namespace Implab.Automaton { return cls; if (!m_supportUnclassified) throw new ArgumentOutOfRangeException("symbol", "The specified symbol isn't in the alphabet"); - return AutomatonConst.UNCLASSIFIED_INPUT; + return AutomatonConst.UnclassifiedInput; } public int Count { diff --git a/Implab/Automaton/RegularExpressions/RegularExpressionVisitor.cs b/Implab/Automaton/RegularExpressions/RegularExpressionVisitor.cs --- a/Implab/Automaton/RegularExpressions/RegularExpressionVisitor.cs +++ b/Implab/Automaton/RegularExpressions/RegularExpressionVisitor.cs @@ -129,7 +129,7 @@ namespace Implab.Automaton.RegularExpres if (m_root == null) m_root = token; m_idx++; - m_indexes[m_idx] = AutomatonConst.UNCLASSIFIED_INPUT; + m_indexes[m_idx] = AutomatonConst.UnclassifiedInput; m_firstpos = new HashSet(new[] { m_idx }); m_lastpos = new HashSet(new[] { m_idx }); Followpos(m_idx); diff --git a/Implab/Formats/CharAlphabet.cs b/Implab/Formats/CharAlphabet.cs --- a/Implab/Formats/CharAlphabet.cs +++ b/Implab/Formats/CharAlphabet.cs @@ -4,7 +4,7 @@ using Implab.Automaton; using System; namespace Implab.Formats { - public class CharAlphabet: IndexedAlphabetBase { + public class CharAlphabet : IndexedAlphabetBase { public override int GetSymbolIndex(char symbol) { return symbol; diff --git a/Implab/Formats/CharMap.cs b/Implab/Formats/CharMap.cs --- a/Implab/Formats/CharMap.cs +++ b/Implab/Formats/CharMap.cs @@ -25,7 +25,7 @@ namespace Implab.Formats { } public bool Contains(char symbol) { - return symbol >= m_min && symbol <= m_max && m_map[symbol-m_min] != AutomatonConst.UNCLASSIFIED_INPUT; + return symbol >= m_min && symbol <= m_max && m_map[symbol-m_min] != AutomatonConst.UnclassifiedInput; } public IEnumerable GetSymbols(int cls) { @@ -36,7 +36,7 @@ namespace Implab.Formats { [MethodImpl(MethodImplOptions.AggressiveInlining)] public int Translate(char symbol) { - return symbol >= m_min && symbol <= m_max ? m_map[symbol-m_min] : AutomatonConst.UNCLASSIFIED_INPUT; + return symbol >= m_min && symbol <= m_max ? m_map[symbol-m_min] : AutomatonConst.UnclassifiedInput; } } } diff --git a/Implab/Formats/Grammar.cs b/Implab/Formats/Grammar.cs --- a/Implab/Formats/Grammar.cs +++ b/Implab/Formats/Grammar.cs @@ -16,7 +16,7 @@ namespace Implab.Formats { } protected SymbolToken UnclassifiedToken() { - return new SymbolToken(AutomatonConst.UNCLASSIFIED_INPUT); + return new SymbolToken(AutomatonConst.UnclassifiedInput); } protected void DefineAlphabet(IEnumerable alphabet) { @@ -42,7 +42,7 @@ namespace Implab.Formats { int TranslateOrAdd(TSymbol ch) { var t = AlphabetBuilder.Translate(ch); - if (t == AutomatonConst.UNCLASSIFIED_INPUT) + if (t == AutomatonConst.UnclassifiedInput) t = AlphabetBuilder.DefineSymbol(ch); return t; } @@ -53,7 +53,7 @@ namespace Implab.Formats { int TranslateOrDie(TSymbol ch) { var t = AlphabetBuilder.Translate(ch); - if (t == AutomatonConst.UNCLASSIFIED_INPUT) + if (t == AutomatonConst.UnclassifiedInput) throw new ApplicationException(String.Format("Symbol '{0}' is UNCLASSIFIED", ch)); return t; } diff --git a/Implab/Formats/InputScanner.cs b/Implab/Formats/InputScanner.cs --- a/Implab/Formats/InputScanner.cs +++ b/Implab/Formats/InputScanner.cs @@ -69,7 +69,7 @@ namespace Implab.Formats { while(offset < max) { next = m_dfa[next, m_alphabet.Translate(data[offset])]; - if (next == AutomatonConst.UNREACHABLE_STATE) { + if (next == AutomatonConst.UnreachableState) { // scanner stops on the next position after last recognized symbol m_position = offset; return false; diff --git a/Implab/Formats/Json/JsonGrammar.cs b/Implab/Formats/Json/JsonGrammar.cs --- a/Implab/Formats/Json/JsonGrammar.cs +++ b/Implab/Formats/Json/JsonGrammar.cs @@ -31,8 +31,8 @@ namespace Implab.Formats.Json { get { return _instance.Value; } } - readonly InputScanner m_jsonExpression; - readonly InputScanner m_stringExpression; + readonly FastInputScanner m_jsonExpression; + readonly FastInputScanner m_stringExpression; readonly CharAlphabet m_defaultAlphabet = new CharAlphabet(); public CharAlphabet DefaultAlphabet { get { return m_defaultAlphabet; } } @@ -87,15 +87,15 @@ namespace Implab.Formats.Json { .Or(unescaped.Closure().Tag(TokenType.UnescapedChar)); - m_jsonExpression = BuildScanner(jsonExpression); - m_stringExpression = BuildScanner(jsonStringExpression); + m_jsonExpression = BuildFastScanner(jsonExpression); + m_stringExpression = BuildFastScanner(jsonStringExpression); } - public static InputScanner CreateJsonExpressionScanner() { + public static FastInputScanner CreateJsonExpressionScanner() { return Instance.m_jsonExpression.Clone(); } - public static InputScanner CreateStringExpressionScanner() { + public static FastInputScanner CreateStringExpressionScanner() { return Instance.m_stringExpression.Clone(); } @@ -109,7 +109,7 @@ namespace Implab.Formats.Json { return SymbolToken(Enumerable.Range(start, stop - start + 1).Select(x => (char)x)); } - public InputScanner BuildScanner(Token regexp) { + public FastInputScanner BuildFastScanner(Token regexp) { var dfa = new RegularDFA(AlphabetBuilder); var visitor = new RegularExpressionVisitor(dfa); @@ -122,12 +122,12 @@ namespace Implab.Formats.Json { var ab = new CharAlphabet(); var optimal = dfa.Optimize(ab); - return new InputScanner( + return new FastInputScanner( optimal.CreateTransitionTable(), optimal.CreateFinalStateTable(), NormalizeTags(optimal.CreateTagTable()), optimal.InitialState, - ab.CreateCharMap() + ab.GetTranslationMap() ); } diff --git a/Implab/Formats/Json/JsonReader.cs b/Implab/Formats/Json/JsonReader.cs --- a/Implab/Formats/Json/JsonReader.cs +++ b/Implab/Formats/Json/JsonReader.cs @@ -48,7 +48,7 @@ namespace Implab.Formats.Json { public bool Move(JsonTokenType token) { var next = m_dfa[m_state, (int)token]; - if (next == AutomatonConst.UNREACHABLE_STATE) + if (next == AutomatonConst.UnreachableState) return false; m_state = next; return true; @@ -116,7 +116,7 @@ namespace Implab.Formats.Json { MemberContext m_memberContext = MemberContext.MemberValue; JsonElementType m_elementType; - object m_elementValue; + string m_elementValue; string m_memberName = String.Empty; Stack m_stack = new Stack(); @@ -152,7 +152,7 @@ namespace Implab.Formats.Json { /// /// Значение элемента. Только для элементов типа , для остальных null /// - public object ElementValue { + public string ElementValue { get { return m_elementValue; } } @@ -213,11 +213,11 @@ namespace Implab.Formats.Json { return true; case JsonTokenType.Number: m_elementType = JsonElementType.Value; - m_elementValue = double.Parse(tokenValue, CultureInfo.InvariantCulture); + m_elementValue = tokenValue; return true; case JsonTokenType.Literal: m_elementType = JsonElementType.Value; - m_elementValue = ParseLiteral(tokenValue); + m_elementValue = tokenValue == "null" ? null : tokenValue; return true; case JsonTokenType.NameSeparator: m_memberContext = MemberContext.MemberValue; diff --git a/Implab/Formats/Json/JsonScanner.cs b/Implab/Formats/Json/JsonScanner.cs --- a/Implab/Formats/Json/JsonScanner.cs +++ b/Implab/Formats/Json/JsonScanner.cs @@ -10,8 +10,8 @@ namespace Implab.Formats.Json { /// Сканнер (лексер), разбивающий поток символов на токены JSON. /// public abstract class JsonScanner : Disposable { - readonly InputScanner m_jsonContext = JsonGrammar.CreateJsonExpressionScanner(); - readonly InputScanner m_stringContext = JsonGrammar.CreateStringExpressionScanner(); + readonly FastInputScanner m_jsonContext = JsonGrammar.CreateJsonExpressionScanner(); + readonly FastInputScanner m_stringContext = JsonGrammar.CreateStringExpressionScanner(); readonly char[] m_unescapeBuf = new char[4]; readonly char[] m_buffer; @@ -25,7 +25,7 @@ namespace Implab.Formats.Json { m_length = length; } - bool ReadChunk(InputScanner scanner, out JsonGrammar.TokenType tokenType) { + bool ReadChunk(FastInputScanner scanner, out JsonGrammar.TokenType tokenType) { scanner.ResetState(); while(scanner.Scan(m_buffer, m_pos, m_length)) { @@ -71,7 +71,7 @@ namespace Implab.Formats.Json { return true; } - bool ReadStringChunk(InputScanner scanner, out JsonGrammar.TokenType tokenType) { + bool ReadStringChunk(FastInputScanner scanner, out JsonGrammar.TokenType tokenType) { scanner.ResetState(); while (scanner.Scan(m_buffer, m_pos, m_length)) { @@ -107,7 +107,7 @@ namespace Implab.Formats.Json { // scanner stops as scannerPos if (!scanner.IsFinal) - throw new ParserException($"Unexpected character '{m_buffer[scannerPos + 1]}'"); + throw new ParserException($"Unexpected character '{m_buffer[scannerPos]}'"); if (scannerPos != m_pos) { m_tokenBuilder.Append(m_buffer, m_pos, scannerPos - m_pos); diff --git a/Implab/Implab.csproj b/Implab/Implab.csproj --- a/Implab/Implab.csproj +++ b/Implab/Implab.csproj @@ -55,6 +55,7 @@ + diff --git a/Implab/Xml/JsonXmlReader.cs b/Implab/Xml/JsonXmlReader.cs --- a/Implab/Xml/JsonXmlReader.cs +++ b/Implab/Xml/JsonXmlReader.cs @@ -39,7 +39,7 @@ namespace Implab.Xml { int m_xmlDepth; XmlSimpleAttribute[] m_attributes; - object m_value; + string m_value; bool m_isEmpty; XmlNodeType m_nodeType = XmlNodeType.None; @@ -158,29 +158,13 @@ namespace Implab.Xml { public override string Value { get { - return ConvertValueToString(m_value); + return m_value; } } - - static string ConvertValueToString(object value) { - if (value == null) - return string.Empty; - - switch (Convert.GetTypeCode(value)) { - case TypeCode.Double: - return ((double)value).ToString(CultureInfo.InvariantCulture); - case TypeCode.String: - return (string)value; - case TypeCode.Boolean: - return (bool)value ? "true" : "false"; - default: - return value.ToString(); - } - } - + public override string GetAttribute(int i) { Safe.ArgumentInRange(i, 0, AttributeCount - 1, nameof(i)); - return ConvertValueToString(m_attributes[i].Value); + return m_attributes[i].Value; } public override string GetAttribute(string name) { @@ -188,7 +172,7 @@ namespace Implab.Xml { return null; var qName = m_context.Resolve(name); var attr = Array.Find(m_attributes, x => x.QName == qName); - var value = ConvertValueToString(attr?.Value); + var value = attr?.Value; return value == string.Empty ? null : value; } @@ -197,7 +181,7 @@ namespace Implab.Xml { return null; var qName = new XmlQualifiedName(name, namespaceURI); var attr = Array.Find(m_attributes, x => x.QName == qName); - var value = ConvertValueToString(attr?.Value); + var value = attr?.Value; return value == string.Empty ? null : value; } @@ -319,7 +303,7 @@ namespace Implab.Xml { } } - void ValueNode(object value) { + void ValueNode(string value) { if (!IsSibling()) // the node is nested m_xmlDepth++; @@ -344,11 +328,11 @@ namespace Implab.Xml { if (attr.QName.Name == "xmlns") { if (context == m_context) context = new XmlNameContext(m_context, m_xmlDepth); - context.DefinePrefix(ConvertValueToString(attr.Value), string.Empty); + context.DefinePrefix(attr.Value, string.Empty); } else if (attr.Prefix == m_xmlnsPrefix) { if (context == m_context) context = new XmlNameContext(m_context, m_xmlDepth); - context.DefinePrefix(ConvertValueToString(attr.Value), attr.QName.Name); + context.DefinePrefix(attr.Value, attr.QName.Name); } else { string attrPrefix; if (string.IsNullOrEmpty(attr.QName.Namespace)) @@ -516,7 +500,7 @@ namespace Implab.Xml { m_jsonValueName, m_jsonNamespace, new[] { - new XmlSimpleAttribute("nil", m_xsiNamespace, m_xsiPrefix, true) + new XmlSimpleAttribute("nil", m_xsiNamespace, m_xsiPrefix, "true") }, true ); @@ -607,7 +591,7 @@ namespace Implab.Xml { public override string ToString() { switch (NodeType) { case XmlNodeType.Element: - return $"<{Name} {string.Join(" ", (m_attributes ?? new XmlSimpleAttribute[0]).Select(x => $"{x.Prefix}{(string.IsNullOrEmpty(x.Prefix) ? "" : ":")}{x.QName.Name}='{ConvertValueToString(x.Value)}'"))} {(IsEmptyElement ? "/" : "")}>"; + return $"<{Name} {string.Join(" ", (m_attributes ?? new XmlSimpleAttribute[0]).Select(x => $"{x.Prefix}{(string.IsNullOrEmpty(x.Prefix) ? "" : ":")}{x.QName.Name}='{x.Value}'"))} {(IsEmptyElement ? "/" : "")}>"; case XmlNodeType.Attribute: return $"@{Name}"; case XmlNodeType.Text: diff --git a/Implab/Xml/XmlSimpleAttribute.cs b/Implab/Xml/XmlSimpleAttribute.cs --- a/Implab/Xml/XmlSimpleAttribute.cs +++ b/Implab/Xml/XmlSimpleAttribute.cs @@ -7,7 +7,7 @@ using System.Xml; namespace Implab.Xml { public class XmlSimpleAttribute { - public XmlSimpleAttribute(string name, string ns, string prefix, object value) { + public XmlSimpleAttribute(string name, string ns, string prefix, string value) { QName = new XmlQualifiedName(name, ns); Prefix = prefix; Value = value; @@ -17,6 +17,6 @@ namespace Implab.Xml { public string Prefix { get; set; } - public object Value { get; set; } + public string Value { get; set; } } }