changeset 229:5f7a3e1d32b9 v2

JsonXmlReader performance tuning JsonScanner now operates strings and doesn't parses number and literals. Added SerializationHelpers to common serialize/deserialize operations
author cin
date Tue, 12 Sep 2017 19:07:42 +0300
parents 6fa235c5a760
children 3e26338eb977 3eaa9372c754 d6fe09f5592c
files .hgignore Implab.Format.Test/JsonTests.cs Implab.Playground/App.config Implab.Playground/Implab.Playground.csproj Implab.Playground/Program.cs Implab.Playground/Properties/AssemblyInfo.cs Implab.Playground2.psess Implab.sln Implab/Automaton/DFATable.cs Implab/Formats/InputScanner.cs Implab/Formats/JSON/JsonParser.cs Implab/Formats/JSON/JsonReader.cs Implab/Formats/JSON/JsonScanner.cs Implab/Formats/JSON/JsonTextScanner.cs Implab/Implab.csproj Implab/Xml/JsonXmlReader.cs Implab/Xml/JsonXmlReaderPosition.cs Implab/Xml/SerializationHelpers.cs Implab/Xml/SerializersPool.cs Implab/Xml/XmlNameContext.cs
diffstat 20 files changed, 839 insertions(+), 394 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Tue Sep 12 01:19:12 2017 +0300
+++ b/.hgignore	Tue Sep 12 19:07:42 2017 +0300
@@ -21,3 +21,5 @@
 Implab.Format.Test/bin/
 Implab.Format.Test/obj/
 packages/
+Implab.Playground/obj/
+Implab.Playground/bin/
--- a/Implab.Format.Test/JsonTests.cs	Tue Sep 12 01:19:12 2017 +0300
+++ b/Implab.Format.Test/JsonTests.cs	Tue Sep 12 19:07:42 2017 +0300
@@ -5,6 +5,7 @@
 using System.Xml;
 using Implab.Formats;
 using Implab.Formats.Json;
+using System.IO;
 
 namespace Implab.Format.Test {
     [TestFixture]
@@ -15,19 +16,19 @@
             using (var scanner = JsonStringScanner.Create(@"9123, -123, 0, 0.1, -0.2, -0.1e3, 1.3E-3, ""some \t\n\u0020 text"", literal []{}:")) {
 
                 Tuple<JsonTokenType, object>[] expexted = {
-                    new Tuple<JsonTokenType,object>(JsonTokenType.Number, 9123d),
+                    new Tuple<JsonTokenType,object>(JsonTokenType.Number, "9123"),
                     new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
-                    new Tuple<JsonTokenType,object>(JsonTokenType.Number, -123d),
+                    new Tuple<JsonTokenType,object>(JsonTokenType.Number, "-123"),
                     new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
-                    new Tuple<JsonTokenType,object>(JsonTokenType.Number, 0d),
+                    new Tuple<JsonTokenType,object>(JsonTokenType.Number, "0"),
                     new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
-                    new Tuple<JsonTokenType,object>(JsonTokenType.Number, 0.1d),
+                    new Tuple<JsonTokenType,object>(JsonTokenType.Number, "0.1"),
                     new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
-                    new Tuple<JsonTokenType,object>(JsonTokenType.Number, -0.2d),
+                    new Tuple<JsonTokenType,object>(JsonTokenType.Number, "-0.2"),
                     new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
-                    new Tuple<JsonTokenType,object>(JsonTokenType.Number, -0.1e3d),
+                    new Tuple<JsonTokenType,object>(JsonTokenType.Number, "-0.1e3"),
                     new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
-                    new Tuple<JsonTokenType,object>(JsonTokenType.Number, 1.3E-3d),
+                    new Tuple<JsonTokenType,object>(JsonTokenType.Number, "1.3E-3"),
                     new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
                     new Tuple<JsonTokenType,object>(JsonTokenType.String, "some \t\n  text"),
                     new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
@@ -39,7 +40,7 @@
                     new Tuple<JsonTokenType,object>(JsonTokenType.NameSeparator, null)
                 };
 
-                object value;
+                string value;
                 JsonTokenType tokenType;
                 for (var i = 0; i < expexted.Length; i++) {
 
@@ -73,7 +74,7 @@
             foreach (var json in bad) {
                 using (var scanner = JsonStringScanner.Create(json)) {
                     try {
-                        object value;
+                        string value;
                         JsonTokenType token;
                         scanner.ReadToken(out value, out token);
                         if (!Object.Equals(value, json)) {
@@ -109,11 +110,11 @@
             //DumpJsonParse("{}");
             //DumpJsonParse("[]");
             DumpJsonParse("{\"one\":1, \"two\":2}");
-            DumpJsonParse("[1,2,3]");
+            DumpJsonParse("[1,\"\",2,3]");
             DumpJsonParse("[{\"info\": [7,8,9]}]");
-            DumpJsonFlatParse("[1,2,[3,4],{\"info\": [5,6]},{\"num\": [7,8,null]}, null,[null]]");
+            DumpJsonFlatParse("[1,2,\"\",[3,4],{\"info\": [5,6]},{\"num\": [7,8,null]}, null,[null]]");
         }
-
+        
         void AssertRead(XmlReader reader, XmlNodeType expected) {
             Assert.IsTrue(reader.Read());
             Console.WriteLine($"{new string(' ', reader.Depth*2)}{reader}");
@@ -123,7 +124,7 @@
         void DumpJsonParse(string json) {
             Console.WriteLine($"JSON: {json}");
             Console.WriteLine("XML");
-            using (var xmlReader = new JsonXmlReader(new JsonParser(json), new JsonXmlReaderOptions { NamespaceUri = "JsonXmlReaderSimpleTest", NodesPrefix = "json" })) {
+            using (var xmlReader = new JsonXmlReader(JsonReader.ParseString(json), new JsonXmlReaderOptions { NamespaceUri = "JsonXmlReaderSimpleTest", NodesPrefix = "json" })) {
                 while (xmlReader.Read())
                     Console.WriteLine($"{new string(' ', xmlReader.Depth * 2)}{xmlReader}");
             }
@@ -137,7 +138,7 @@
                 CloseOutput = false,
                 ConformanceLevel = ConformanceLevel.Document
             }))
-            using (var xmlReader = new JsonXmlReader(new JsonParser(json), new JsonXmlReaderOptions { NamespaceUri = "JsonXmlReaderSimpleTest", NodesPrefix = "", FlattenArrays = true })) {
+            using (var xmlReader = new JsonXmlReader(JsonReader.ParseString(json), new JsonXmlReaderOptions { NamespaceUri = "JsonXmlReaderSimpleTest", NodesPrefix = "", FlattenArrays = true })) {
                 xmlWriter.WriteNode(xmlReader, false);
             }
         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab.Playground/App.config	Tue Sep 12 19:07:42 2017 +0300
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+    <startup> 
+        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
+    </startup>
+</configuration>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab.Playground/Implab.Playground.csproj	Tue Sep 12 19:07:42 2017 +0300
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{100DFEB0-75BE-436F-ADDF-1F46EF433F46}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>Implab.Playground</RootNamespace>
+    <AssemblyName>Implab.Playground</AssemblyName>
+    <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <Prefer32Bit>true</Prefer32Bit>
+    <DebugSymbols>true</DebugSymbols>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Program.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="App.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\Implab\Implab.csproj">
+      <Project>{f550f1f8-8746-4ad0-9614-855f4c4b7f05}</Project>
+      <Name>Implab</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab.Playground/Program.cs	Tue Sep 12 19:07:42 2017 +0300
@@ -0,0 +1,42 @@
+using Implab.Formats.Json;
+using Implab.Xml;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml;
+using System.Xml.Serialization;
+
+namespace Implab.Playground {
+    public class Program {
+
+        [XmlRoot(Namespace = "XmlSimpleData")]
+        public class XmlSimpleModel {
+            [XmlElement]
+            public string Name { get; set; }
+
+            [XmlElement]
+            public int Order { get; set; }
+
+            [XmlElement]
+            public string[] Items { get; set; }
+
+        }
+
+        static void Main(string[] args) {
+            var model = new XmlSimpleModel {
+                Name = "Tablet",
+                Order = 10,
+                Items = new string[] { "z1", "z2", "z3" }
+            };
+
+            var doc = SerializationHelpers.SerializeAsXmlDocument(model);
+
+            var m2 = SerializationHelpers.DeserializeFromXmlNode<XmlSimpleModel>(doc.DocumentElement);
+
+            Console.WriteLine("done");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab.Playground/Properties/AssemblyInfo.cs	Tue Sep 12 19:07:42 2017 +0300
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Implab.Playground")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Implab.Playground")]
+[assembly: AssemblyCopyright("Copyright ©  2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("100dfeb0-75be-436f-addf-1f46ef433f46")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab.Playground2.psess	Tue Sep 12 19:07:42 2017 +0300
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<VSPerformanceSession Version="1.00">
+  <Options>
+    <Solution>Implab.sln</Solution>
+    <CollectionMethod>Sampling</CollectionMethod>
+    <AllocationMethod>None</AllocationMethod>
+    <AddReport>true</AddReport>
+    <ResourceBasedAnalysisSelected>true</ResourceBasedAnalysisSelected>
+    <UniqueReport>Timestamp</UniqueReport>
+    <SamplingMethod>Cycles</SamplingMethod>
+    <CycleCount>50000</CycleCount>
+    <PageFaultCount>10</PageFaultCount>
+    <SysCallCount>10</SysCallCount>
+    <SamplingCounter Name="" ReloadValue="00000000000f4240" DisplayName="" />
+    <RelocateBinaries>false</RelocateBinaries>
+    <HardwareCounters EnableHWCounters="false" />
+    <EtwSettings />
+    <PdhSettings>
+      <PdhCountersEnabled>false</PdhCountersEnabled>
+      <PdhCountersRate>500</PdhCountersRate>
+      <PdhCounters>
+        <PdhCounter>\Память\Обмен страниц/с</PdhCounter>
+        <PdhCounter>\Процессор(_Total)\% загруженности процессора</PdhCounter>
+        <PdhCounter>\Физический диск(_Total)\Средняя длина очереди диска</PdhCounter>
+      </PdhCounters>
+    </PdhSettings>
+  </Options>
+  <ExcludeSmallFuncs>true</ExcludeSmallFuncs>
+  <InteractionProfilingEnabled>false</InteractionProfilingEnabled>
+  <JScriptProfilingEnabled>false</JScriptProfilingEnabled>
+  <PreinstrumentEvent>
+    <InstrEventExclude>false</InstrEventExclude>
+  </PreinstrumentEvent>
+  <PostinstrumentEvent>
+    <InstrEventExclude>false</InstrEventExclude>
+  </PostinstrumentEvent>
+  <Binaries>
+    <ProjBinary>
+      <Path>Implab.Playground\obj\Debug\Implab.Playground.exe</Path>
+      <ArgumentTimestamp>01/01/0001 00:00:00</ArgumentTimestamp>
+      <Instrument>true</Instrument>
+      <Sample>true</Sample>
+      <ExternalWebsite>false</ExternalWebsite>
+      <InteractionProfilingEnabled>false</InteractionProfilingEnabled>
+      <IsLocalJavascript>false</IsLocalJavascript>
+      <IsWindowsStoreApp>false</IsWindowsStoreApp>
+      <IsWWA>false</IsWWA>
+      <LaunchProject>true</LaunchProject>
+      <OverrideProjectSettings>false</OverrideProjectSettings>
+      <LaunchMethod>Executable</LaunchMethod>
+      <ExecutablePath>Implab.Playground\bin\Release\Implab.Playground.exe</ExecutablePath>
+      <StartupDirectory>Implab.Playground\bin\Release\</StartupDirectory>
+      <Arguments>
+      </Arguments>
+      <NetAppHost>IIS</NetAppHost>
+      <NetBrowser>InternetExplorer</NetBrowser>
+      <ExcludeSmallFuncs>true</ExcludeSmallFuncs>
+      <JScriptProfilingEnabled>false</JScriptProfilingEnabled>
+      <PreinstrumentEvent>
+        <InstrEventExclude>false</InstrEventExclude>
+      </PreinstrumentEvent>
+      <PostinstrumentEvent>
+        <InstrEventExclude>false</InstrEventExclude>
+      </PostinstrumentEvent>
+      <ProjRef>{100DFEB0-75BE-436F-ADDF-1F46EF433F46}|Implab.Playground\Implab.Playground.csproj</ProjRef>
+      <ProjPath>Implab.Playground\Implab.Playground.csproj</ProjPath>
+      <ProjName>Implab.Playground</ProjName>
+    </ProjBinary>
+  </Binaries>
+  <Launches>
+    <ProjBinary>
+      <Path>:PB:{100DFEB0-75BE-436F-ADDF-1F46EF433F46}|Implab.Playground\Implab.Playground.csproj</Path>
+    </ProjBinary>
+  </Launches>
+</VSPerformanceSession>
\ No newline at end of file
--- a/Implab.sln	Tue Sep 12 01:19:12 2017 +0300
+++ b/Implab.sln	Tue Sep 12 19:07:42 2017 +0300
@@ -16,11 +16,14 @@
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.Fx", "Implab.Fx\Implab.Fx.csproj", "{06E706F8-6881-43EB-927E-FFC503AF6ABC}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.Fx.Test", "Implab.Fx.Test\Implab.Fx.Test.csproj", "{2F31E405-E267-4195-A05D-574093C21209}"
-EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.Format.Test", "Implab.Format.Test\Implab.Format.Test.csproj", "{4D364996-7ECD-4193-8F90-F223FFEA49DA}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.Playground", "Implab.Playground\Implab.Playground.csproj", "{100DFEB0-75BE-436F-ADDF-1F46EF433F46}"
+EndProject
 Global
+	GlobalSection(Performance) = preSolution
+		HasPerformanceSessions = true
+	EndGlobalSection
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug 4.5|Any CPU = Debug 4.5|Any CPU
 		Debug|Any CPU = Debug|Any CPU
@@ -52,14 +55,6 @@
 		{06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU
 		{06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release|Any CPU.Build.0 = Release|Any CPU
-		{2F31E405-E267-4195-A05D-574093C21209}.Debug 4.5|Any CPU.ActiveCfg = Debug 4.5|Any CPU
-		{2F31E405-E267-4195-A05D-574093C21209}.Debug 4.5|Any CPU.Build.0 = Debug 4.5|Any CPU
-		{2F31E405-E267-4195-A05D-574093C21209}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{2F31E405-E267-4195-A05D-574093C21209}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{2F31E405-E267-4195-A05D-574093C21209}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU
-		{2F31E405-E267-4195-A05D-574093C21209}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU
-		{2F31E405-E267-4195-A05D-574093C21209}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{2F31E405-E267-4195-A05D-574093C21209}.Release|Any CPU.Build.0 = Release|Any CPU
 		{4D364996-7ECD-4193-8F90-F223FFEA49DA}.Debug 4.5|Any CPU.ActiveCfg = Debug|Any CPU
 		{4D364996-7ECD-4193-8F90-F223FFEA49DA}.Debug 4.5|Any CPU.Build.0 = Debug|Any CPU
 		{4D364996-7ECD-4193-8F90-F223FFEA49DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
@@ -68,6 +63,14 @@
 		{4D364996-7ECD-4193-8F90-F223FFEA49DA}.Release 4.5|Any CPU.Build.0 = Release|Any CPU
 		{4D364996-7ECD-4193-8F90-F223FFEA49DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{4D364996-7ECD-4193-8F90-F223FFEA49DA}.Release|Any CPU.Build.0 = Release|Any CPU
+		{100DFEB0-75BE-436F-ADDF-1F46EF433F46}.Debug 4.5|Any CPU.ActiveCfg = Debug|Any CPU
+		{100DFEB0-75BE-436F-ADDF-1F46EF433F46}.Debug 4.5|Any CPU.Build.0 = Debug|Any CPU
+		{100DFEB0-75BE-436F-ADDF-1F46EF433F46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{100DFEB0-75BE-436F-ADDF-1F46EF433F46}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{100DFEB0-75BE-436F-ADDF-1F46EF433F46}.Release 4.5|Any CPU.ActiveCfg = Release|Any CPU
+		{100DFEB0-75BE-436F-ADDF-1F46EF433F46}.Release 4.5|Any CPU.Build.0 = Release|Any CPU
+		{100DFEB0-75BE-436F-ADDF-1F46EF433F46}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{100DFEB0-75BE-436F-ADDF-1F46EF433F46}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
--- a/Implab/Automaton/DFATable.cs	Tue Sep 12 01:19:12 2017 +0300
+++ b/Implab/Automaton/DFATable.cs	Tue Sep 12 19:07:42 2017 +0300
@@ -119,7 +119,7 @@
                     table[i, j] = AutomatonConst.UNREACHABLE_STATE;
 
             foreach (var t in this)
-                table[t.s1,t.edge] = t.s2;
+                table[t.s1,t.edge] = (byte)t.s2;
 
             return table;
         }
--- a/Implab/Formats/InputScanner.cs	Tue Sep 12 01:19:12 2017 +0300
+++ b/Implab/Formats/InputScanner.cs	Tue Sep 12 19:07:42 2017 +0300
@@ -2,6 +2,7 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Runtime.CompilerServices;
 using System.Text;
 using System.Threading.Tasks;
 
@@ -30,24 +31,28 @@
         }
 
         public TTag Tag {
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
             get {
                 return m_tags[m_state];
             }
         }
 
         public int Position {
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
             get {
                 return m_position;
             }
         }
 
         public bool IsFinal {
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
             get {
                 return m_final[m_state];
             }
         }
 
-        public void Reset() {
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void ResetState() {
             m_state = m_initialState;
         }
 
@@ -58,13 +63,8 @@
             return clone;
         }
 
-        public bool Scan(char[] data, int offset, int length) {
-            if (length <= 0) {
-                m_position = offset;
-                return false; // EOF
-            }
-
-            var max = offset + length;
+        //[MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public bool Scan(char[] data, int offset, int max) {
             var next = m_state;
 
             while(offset < max) {
--- a/Implab/Formats/JSON/JsonParser.cs	Tue Sep 12 01:19:12 2017 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,294 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.IO;
-using Implab.Automaton;
-using Implab.Automaton.RegularExpressions;
-using System.Linq;
-using Implab.Components;
-using System.Collections.Generic;
-
-namespace Implab.Formats.Json {
-    /// <summary>
-    /// Pull парсер JSON данных.
-    /// </summary>
-    /// <remarks>
-    /// Следует отметить отдельную интерпретацию свойства <see cref="Level"/>,
-    /// оно означает текущий уровень вложенности объектов, однако закрывающий
-    /// элемент объекта и массива имеет уровень меньше, чем сам объект.
-    /// <code>
-    /// { // Level = 1
-    ///     "name" : "Peter", // Level = 1
-    ///     "address" : { // Level = 2
-    ///         city : "Stern" // Level = 2
-    ///     } // Level = 1
-    /// } // Level = 0
-    /// </code>
-    /// </remarks>
-    public class JsonParser : Disposable {
-
-        enum MemberContext {
-            MemberName,
-            MemberValue
-        }
-
-        #region Parser rules
-        struct ParserContext {
-            readonly int[,] m_dfa;
-            int m_state;
-
-            readonly JsonElementContext m_elementContext;
-
-            public ParserContext(int[,] dfa, int state, JsonElementContext context) {
-                m_dfa = dfa;
-                m_state = state;
-                m_elementContext = context;
-            }
-
-            public bool Move(JsonTokenType token) {
-                var next = m_dfa[m_state, (int)token];
-                if (next == AutomatonConst.UNREACHABLE_STATE)
-                    return false;
-                m_state = next;
-                return true;
-            }
-
-            public JsonElementContext ElementContext {
-                get { return m_elementContext; }
-            }
-        }
-
-        static readonly ParserContext _jsonContext;
-        static readonly ParserContext _objectContext;
-        static readonly ParserContext _arrayContext;
-
-        static JsonParser() {
-
-            var valueExpression = MakeToken(JsonTokenType.BeginArray, JsonTokenType.BeginObject, JsonTokenType.Literal, JsonTokenType.Number, JsonTokenType.String);
-            var memberExpression = MakeToken(JsonTokenType.String).Cat(MakeToken(JsonTokenType.NameSeparator)).Cat(valueExpression);
-
-            var objectExpression = memberExpression
-                .Cat(
-                    MakeToken(JsonTokenType.ValueSeparator)
-                    .Cat(memberExpression)
-                    .EClosure()
-                )
-                .Optional()
-                .Cat(MakeToken(JsonTokenType.EndObject))
-                .End();
-            
-            var arrayExpression = valueExpression
-                .Cat(
-                    MakeToken(JsonTokenType.ValueSeparator)
-                    .Cat(valueExpression)
-                    .EClosure()
-                )
-                .Optional()
-                .Cat(MakeToken(JsonTokenType.EndArray))
-                .End();
-
-            var jsonExpression = valueExpression.End();
-
-            _jsonContext = CreateParserContext(jsonExpression, JsonElementContext.None);
-            _objectContext = CreateParserContext(objectExpression, JsonElementContext.Object);
-            _arrayContext = CreateParserContext(arrayExpression, JsonElementContext.Array);
-        }
-
-        static Token MakeToken(params JsonTokenType[] input) {
-            return Token.New( input.Select(t => (int)t).ToArray() );
-        }
-
-        static ParserContext CreateParserContext(Token expr, JsonElementContext context) {
-            
-            var dfa = new DFATable();
-            var builder = new RegularExpressionVisitor(dfa);
-            expr.Accept(builder);
-            builder.BuildDFA();
-
-            return new ParserContext(dfa.CreateTransitionTable(), dfa.InitialState, context);
-        }
-
-        #endregion
-
-        readonly JsonScanner m_scanner;
-        // json starts from the value context and may content even a single literal
-        MemberContext m_memberContext = MemberContext.MemberValue;
-
-        JsonElementType m_elementType;
-        object m_elementValue;
-        string m_memberName = String.Empty;
-
-        Stack<ParserContext> m_stack = new Stack<ParserContext>();
-        ParserContext m_context = _jsonContext;
-
-        /// <summary>
-        /// Создает новый парсер на основе строки, содержащей JSON
-        /// </summary>
-        /// <param name="text"></param>
-        public JsonParser(string text) {
-            Safe.ArgumentNotEmpty(text, "text");
-            m_scanner = JsonStringScanner.Create(text);
-        }
-
-        /// <summary>
-        /// Создает новый экземпляр парсера, на основе текстового потока.
-        /// </summary>
-        /// <param name="reader">Текстовый поток.</param>
-        public JsonParser(TextReader reader) {
-            Safe.ArgumentNotNull(reader, "reader");
-            m_scanner = JsonTextScanner.Create(reader);
-        }
-
-        public int Level {
-            get { return m_stack.Count; }
-        }
-
-        /// <summary>
-        /// Тип текущего элемента на котором стоит парсер.
-        /// </summary>
-        public JsonElementType ElementType {
-            get { return m_elementType; }
-        }
-
-        /// <summary>
-        /// Имя элемента - имя свойства родительского контейнера. Для элементов массивов и корневого всегда
-        /// пустая строка.
-        /// </summary>
-        public string ElementName {
-            get { return m_memberName; }
-        }
-
-        /// <summary>
-        /// Значение элемента. Только для элементов типа <see cref="JsonElementType.Value"/>, для остальных <c>null</c>
-        /// </summary>
-        public object ElementValue {
-            get { return m_elementValue; }
-        }
-
-        /// <summary>
-        /// Читает слеюудущий объект из потока
-        /// </summary>
-        /// <returns><c>true</c> - операция чтения прошла успешно, <c>false</c> - конец данных</returns>
-        public bool Read() {
-            object tokenValue;
-            JsonTokenType tokenType;
-
-            m_memberName = String.Empty;
-
-            while (m_scanner.ReadToken(out tokenValue, out tokenType)) {
-                if(!m_context.Move(tokenType))
-                    UnexpectedToken(tokenValue, tokenType);
-
-                switch (tokenType) {
-                    case JsonTokenType.BeginObject:
-                        m_stack.Push(m_context);
-                        m_context = _objectContext;
-
-                        m_elementValue = null;
-                        m_memberContext = MemberContext.MemberName;
-                        m_elementType = JsonElementType.BeginObject;
-                        return true;
-                    case JsonTokenType.EndObject:
-                        if (m_stack.Count == 0)
-                            UnexpectedToken(tokenValue, tokenType);
-                        m_context = m_stack.Pop();
-
-                        m_elementValue = null;
-                        m_elementType = JsonElementType.EndObject;
-                        return true;
-                    case JsonTokenType.BeginArray:
-                        m_stack.Push(m_context);
-                        m_context = _arrayContext;
-
-                        m_elementValue = null;
-                        m_memberContext = MemberContext.MemberValue;
-                        m_elementType = JsonElementType.BeginArray;
-                        return true;
-                    case JsonTokenType.EndArray:
-                        if (m_stack.Count == 0)
-                            UnexpectedToken(tokenValue, tokenType);
-                        m_context = m_stack.Pop();
-
-                        m_elementValue = null;
-                        m_elementType = JsonElementType.EndArray;
-                        return true;
-                    case JsonTokenType.String:
-                        if (m_memberContext == MemberContext.MemberName) {
-                            m_memberName = (string)tokenValue;
-                            break;
-                        }
-                        m_elementType = JsonElementType.Value;
-                        m_elementValue = tokenValue;
-                        return true;
-                    case JsonTokenType.Number:
-                        m_elementType = JsonElementType.Value;
-                        m_elementValue = tokenValue;
-                        return true;
-                    case JsonTokenType.Literal:
-                        m_elementType = JsonElementType.Value;
-                        m_elementValue = ParseLiteral((string)tokenValue);
-                        return true;
-                    case JsonTokenType.NameSeparator:
-                        m_memberContext = MemberContext.MemberValue;
-                        break;
-                    case JsonTokenType.ValueSeparator:
-                        m_memberContext = m_context.ElementContext == JsonElementContext.Object ? MemberContext.MemberName : MemberContext.MemberValue;
-                        break;
-                    default:
-                        UnexpectedToken(tokenValue, tokenType);
-                        break;
-                }
-            }
-            if (m_context.ElementContext != JsonElementContext.None)
-                throw new ParserException("Unexpedted end of data");
-
-            EOF = true;
-
-            return false;
-        }
-
-        object ParseLiteral(string literal) {
-            switch (literal) {
-                case "null":
-                    return null;
-                case "false":
-                    return false;
-                case "true":
-                    return true;
-                default:
-                    UnexpectedToken(literal, JsonTokenType.Literal);
-                    return null; // avoid compliler error
-            }
-        }
-
-        void UnexpectedToken(object value, JsonTokenType tokenType) {
-            throw new ParserException(String.Format("Unexpected token {0}: '{1}'", tokenType, value));
-        }
-
-
-        /// <summary>
-        /// Признак конца потока
-        /// </summary>
-        public bool EOF {
-            get;
-            private set;
-        }
-
-        protected override void Dispose(bool disposing) {
-            if (disposing)
-                m_scanner.Dispose();
-        }
-
-        /// <summary>
-        /// Переходит в конец текущего объекта.
-        /// </summary>
-        public void SeekElementEnd() {
-            var level = Level - 1;
-
-            Debug.Assert(level >= 0);
-
-            while (Level != level)
-                Read();
-        }
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/Formats/JSON/JsonReader.cs	Tue Sep 12 19:07:42 2017 +0300
@@ -0,0 +1,318 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using Implab.Automaton;
+using Implab.Automaton.RegularExpressions;
+using System.Linq;
+using Implab.Components;
+using System.Collections.Generic;
+using System.Text;
+using System.Globalization;
+
+namespace Implab.Formats.Json {
+    /// <summary>
+    /// Pull парсер JSON данных.
+    /// </summary>
+    /// <remarks>
+    /// Следует отметить отдельную интерпретацию свойства <see cref="Level"/>,
+    /// оно означает текущий уровень вложенности объектов, однако закрывающий
+    /// элемент объекта и массива имеет уровень меньше, чем сам объект.
+    /// <code>
+    /// { // Level = 1
+    ///     "name" : "Peter", // Level = 1
+    ///     "address" : { // Level = 2
+    ///         city : "Stern" // Level = 2
+    ///     } // Level = 1
+    /// } // Level = 0
+    /// </code>
+    /// </remarks>
+    public class JsonReader : Disposable {
+
+        enum MemberContext {
+            MemberName,
+            MemberValue
+        }
+
+        #region Parser rules
+        struct ParserContext {
+            readonly int[,] m_dfa;
+            int m_state;
+
+            readonly JsonElementContext m_elementContext;
+
+            public ParserContext(int[,] dfa, int state, JsonElementContext context) {
+                m_dfa = dfa;
+                m_state = state;
+                m_elementContext = context;
+            }
+
+            public bool Move(JsonTokenType token) {
+                var next = m_dfa[m_state, (int)token];
+                if (next == AutomatonConst.UNREACHABLE_STATE)
+                    return false;
+                m_state = next;
+                return true;
+            }
+
+            public JsonElementContext ElementContext {
+                get { return m_elementContext; }
+            }
+        }
+
+        static readonly ParserContext _jsonContext;
+        static readonly ParserContext _objectContext;
+        static readonly ParserContext _arrayContext;
+
+        static JsonReader() {
+
+            var valueExpression = MakeToken(JsonTokenType.BeginArray, JsonTokenType.BeginObject, JsonTokenType.Literal, JsonTokenType.Number, JsonTokenType.String);
+            var memberExpression = MakeToken(JsonTokenType.String).Cat(MakeToken(JsonTokenType.NameSeparator)).Cat(valueExpression);
+
+            var objectExpression = memberExpression
+                .Cat(
+                    MakeToken(JsonTokenType.ValueSeparator)
+                    .Cat(memberExpression)
+                    .EClosure()
+                )
+                .Optional()
+                .Cat(MakeToken(JsonTokenType.EndObject))
+                .End();
+            
+            var arrayExpression = valueExpression
+                .Cat(
+                    MakeToken(JsonTokenType.ValueSeparator)
+                    .Cat(valueExpression)
+                    .EClosure()
+                )
+                .Optional()
+                .Cat(MakeToken(JsonTokenType.EndArray))
+                .End();
+
+            var jsonExpression = valueExpression.End();
+
+            _jsonContext = CreateParserContext(jsonExpression, JsonElementContext.None);
+            _objectContext = CreateParserContext(objectExpression, JsonElementContext.Object);
+            _arrayContext = CreateParserContext(arrayExpression, JsonElementContext.Array);
+        }
+
+        static Token MakeToken(params JsonTokenType[] input) {
+            return Token.New( input.Select(t => (int)t).ToArray() );
+        }
+
+        static ParserContext CreateParserContext(Token expr, JsonElementContext context) {
+            
+            var dfa = new DFATable();
+            var builder = new RegularExpressionVisitor(dfa);
+            expr.Accept(builder);
+            builder.BuildDFA();
+
+            return new ParserContext(dfa.CreateTransitionTable(), dfa.InitialState, context);
+        }
+
+        #endregion
+
+        readonly JsonScanner m_scanner;
+        // json starts from the value context and may content even a single literal
+        MemberContext m_memberContext = MemberContext.MemberValue;
+
+        JsonElementType m_elementType;
+        object m_elementValue;
+        string m_memberName = String.Empty;
+
+        Stack<ParserContext> m_stack = new Stack<ParserContext>();
+        ParserContext m_context = _jsonContext;
+
+        /// <summary>
+        /// Создает новый парсер на основе строки, содержащей JSON
+        /// </summary>
+        /// <param name="text"></param>
+        JsonReader(JsonScanner scanner) {
+            m_scanner = scanner;
+        }
+        
+        public int Level {
+            get { return m_stack.Count; }
+        }
+
+        /// <summary>
+        /// Тип текущего элемента на котором стоит парсер.
+        /// </summary>
+        public JsonElementType ElementType {
+            get { return m_elementType; }
+        }
+
+        /// <summary>
+        /// Имя элемента - имя свойства родительского контейнера. Для элементов массивов и корневого всегда
+        /// пустая строка.
+        /// </summary>
+        public string ElementName {
+            get { return m_memberName; }
+        }
+
+        /// <summary>
+        /// Значение элемента. Только для элементов типа <see cref="JsonElementType.Value"/>, для остальных <c>null</c>
+        /// </summary>
+        public object ElementValue {
+            get { return m_elementValue; }
+        }
+
+        /// <summary>
+        /// Читает слеюудущий объект из потока
+        /// </summary>
+        /// <returns><c>true</c> - операция чтения прошла успешно, <c>false</c> - конец данных</returns>
+        public bool Read() {
+            string tokenValue;
+            JsonTokenType tokenType;
+
+            m_memberName = String.Empty;
+
+            while (m_scanner.ReadToken(out tokenValue, out tokenType)) {
+                if(!m_context.Move(tokenType))
+                    UnexpectedToken(tokenValue, tokenType);
+
+                switch (tokenType) {
+                    case JsonTokenType.BeginObject:
+                        m_stack.Push(m_context);
+                        m_context = _objectContext;
+
+                        m_elementValue = null;
+                        m_memberContext = MemberContext.MemberName;
+                        m_elementType = JsonElementType.BeginObject;
+                        return true;
+                    case JsonTokenType.EndObject:
+                        if (m_stack.Count == 0)
+                            UnexpectedToken(tokenValue, tokenType);
+                        m_context = m_stack.Pop();
+
+                        m_elementValue = null;
+                        m_elementType = JsonElementType.EndObject;
+                        return true;
+                    case JsonTokenType.BeginArray:
+                        m_stack.Push(m_context);
+                        m_context = _arrayContext;
+
+                        m_elementValue = null;
+                        m_memberContext = MemberContext.MemberValue;
+                        m_elementType = JsonElementType.BeginArray;
+                        return true;
+                    case JsonTokenType.EndArray:
+                        if (m_stack.Count == 0)
+                            UnexpectedToken(tokenValue, tokenType);
+                        m_context = m_stack.Pop();
+
+                        m_elementValue = null;
+                        m_elementType = JsonElementType.EndArray;
+                        return true;
+                    case JsonTokenType.String:
+                        if (m_memberContext == MemberContext.MemberName) {
+                            m_memberName = tokenValue;
+                            break;
+                        }
+                        m_elementType = JsonElementType.Value;
+                        m_elementValue = tokenValue;
+                        return true;
+                    case JsonTokenType.Number:
+                        m_elementType = JsonElementType.Value;
+                        m_elementValue = double.Parse(tokenValue, CultureInfo.InvariantCulture);
+                        return true;
+                    case JsonTokenType.Literal:
+                        m_elementType = JsonElementType.Value;
+                        m_elementValue = ParseLiteral(tokenValue);
+                        return true;
+                    case JsonTokenType.NameSeparator:
+                        m_memberContext = MemberContext.MemberValue;
+                        break;
+                    case JsonTokenType.ValueSeparator:
+                        m_memberContext = m_context.ElementContext == JsonElementContext.Object ? MemberContext.MemberName : MemberContext.MemberValue;
+                        break;
+                    default:
+                        UnexpectedToken(tokenValue, tokenType);
+                        break;
+                }
+            }
+            if (m_context.ElementContext != JsonElementContext.None)
+                throw new ParserException("Unexpedted end of data");
+
+            Eof = true;
+
+            return false;
+        }
+
+        object ParseLiteral(string literal) {
+            switch (literal) {
+                case "null":
+                    return null;
+                case "false":
+                    return false;
+                case "true":
+                    return true;
+                default:
+                    UnexpectedToken(literal, JsonTokenType.Literal);
+                    return null; // avoid compliler error
+            }
+        }
+
+        void UnexpectedToken(object value, JsonTokenType tokenType) {
+            throw new ParserException(String.Format("Unexpected token {0}: '{1}'", tokenType, value));
+        }
+
+
+        /// <summary>
+        /// Признак конца потока
+        /// </summary>
+        public bool Eof {
+            get;
+            private set;
+        }
+
+        protected override void Dispose(bool disposing) {
+            if (disposing)
+                m_scanner.Dispose();
+        }
+
+        /// <summary>
+        /// Переходит в конец текущего объекта.
+        /// </summary>
+        public void SeekElementEnd() {
+            var level = Level - 1;
+
+            Debug.Assert(level >= 0);
+
+            while (Level != level)
+                Read();
+        }
+
+        public static JsonReader Create(string file, Encoding encoding) {
+            return new JsonReader(JsonTextScanner.Create(file, encoding));
+        }
+
+        public static JsonReader Create(string file) {
+            return new JsonReader(JsonTextScanner.Create(file));
+        }
+
+        public static JsonReader Create(Stream stream, Encoding encoding) {
+            return new JsonReader(JsonTextScanner.Create(stream, encoding));
+        }
+
+        public static JsonReader Create(Stream stream) {
+            return new JsonReader(JsonTextScanner.Create(stream));
+        }
+
+        public static JsonReader Create(TextReader reader) {
+            return new JsonReader(JsonTextScanner.Create(reader));
+        }
+
+        public static JsonReader ParseString(string data) {
+            return new JsonReader(JsonStringScanner.Create(data));
+        }
+
+        public static JsonReader ParseString(string data, int offset, int length) {
+            return new JsonReader(JsonStringScanner.Create(data, offset, length));
+        }
+
+        public static JsonReader ParseString(char[] data, int offset, int lenght) {
+            return new JsonReader(JsonStringScanner.Create(data, offset, lenght));
+        }
+    }
+
+}
--- a/Implab/Formats/JSON/JsonScanner.cs	Tue Sep 12 01:19:12 2017 +0300
+++ b/Implab/Formats/JSON/JsonScanner.cs	Tue Sep 12 19:07:42 2017 +0300
@@ -25,38 +25,94 @@
             m_length = length;
         }
 
-        bool Read(InputScanner<JsonGrammar.TokenType> scanner, out JsonGrammar.TokenType tokenType) {
-            scanner.Reset();
+        bool ReadChunk(InputScanner<JsonGrammar.TokenType> scanner, out JsonGrammar.TokenType tokenType) {
+            scanner.ResetState();
+
+            while(scanner.Scan(m_buffer, m_pos, m_length)) {
+                // scanner requests new data
 
-            if (m_pos == m_length) {
-                m_pos = 0;
+                if (m_pos != m_length) // capture results for the future
+                    m_tokenBuilder.Append(m_buffer, m_pos, m_length - m_pos);
+                
+                // read next data
                 m_length = Read(m_buffer, 0, m_buffer.Length);
+
                 if (m_length == 0) {
-                    tokenType = JsonGrammar.TokenType.None;
-                    return false; // EOF
+                    // no data is read
+                    if (scanner.Position == m_pos) {
+                        // scanned hasn't moved, that's the end
+                        m_pos = 0;
+                        tokenType = JsonGrammar.TokenType.None;
+                        return false;
+                    }
+
+                    if (scanner.IsFinal) {
+                        m_pos = 0;
+                        tokenType = scanner.Tag;
+                        return true;
+                    } else {
+                        throw new ParserException("Unexpected EOF");
+                    }
                 }
-            }
-            
-            while(scanner.Scan(m_buffer, m_pos, m_length - m_pos)) {
-                m_tokenBuilder.Append(m_buffer, m_pos, m_length - m_pos);
+
                 m_pos = 0;
-                m_length = Read(m_buffer, 0, m_buffer.Length);
             }
             var scannerPos = scanner.Position;
+
+            // scanner stops as scannerPos
+            if (!scanner.IsFinal)
+                throw new ParserException($"Unexpected character '{m_buffer[scannerPos + 1]}'");
+
+            tokenType = scanner.Tag;
+            if (scannerPos != m_pos && tokenType == JsonGrammar.TokenType.Number || tokenType == JsonGrammar.TokenType.Literal)
+                m_tokenBuilder.Append(m_buffer, m_pos, scannerPos - m_pos);
+            
+            m_pos = scannerPos;
+            return true;
+        }
+
+        bool ReadStringChunk(InputScanner<JsonGrammar.TokenType> scanner, out JsonGrammar.TokenType tokenType) {
+            scanner.ResetState();
+
+            while (scanner.Scan(m_buffer, m_pos, m_length)) {
+                // scanner requests new data
+
+                if (m_pos != m_length) // capture results for the future
+                    m_tokenBuilder.Append(m_buffer, m_pos, m_length - m_pos);
+
+                // read next data
+                m_length = Read(m_buffer, 0, m_buffer.Length);
+
+                if (m_length == 0) {
+                    // no data is read
+                    if (scanner.Position == m_pos) {
+                        // scanned hasn't moved, that's the end
+                        m_pos = 0;
+                        tokenType = JsonGrammar.TokenType.None;
+                        return false;
+                    }
+
+                    if (scanner.IsFinal) {
+                        m_pos = 0;
+                        tokenType = scanner.Tag;
+                        return true;
+                    } else {
+                        throw new ParserException("Unexpected EOF");
+                    }
+                }
+
+                m_pos = 0;
+            }
+            var scannerPos = scanner.Position;
+
+            // scanner stops as scannerPos
+            if (!scanner.IsFinal)
+                throw new ParserException($"Unexpected character '{m_buffer[scannerPos + 1]}'");
+
             if (scannerPos != m_pos) {
                 m_tokenBuilder.Append(m_buffer, m_pos, scannerPos - m_pos);
                 m_pos = scannerPos;
             }
-
-            if (!scanner.IsFinal) {
-                if (m_length == 0) {
-                    // unexpected EOF
-                    throw new ParserException("Unexpected EOF");
-                } else {
-                    // unecpected character
-                    throw new ParserException($"Unexpected character '{m_buffer[m_pos + 1]}'");
-                }
-            }
             tokenType = scanner.Tag;
             return true;
         }
@@ -72,17 +128,17 @@
         /// <returns><c>true</c> - чтение произведено успешно. <c>false</c> - достигнут конец входных данных</returns>
         /// <remarks>В случе если токен не распознается, возникает исключение. Значения токенов обрабатываются, т.е.
         /// в строках обрабатываются экранированные символы, числа становтся типа double.</remarks>
-        public bool ReadToken(out object tokenValue, out JsonTokenType tokenType) {
+        public bool ReadToken(out string tokenValue, out JsonTokenType tokenType) {
             JsonGrammar.TokenType tag;
             m_tokenBuilder.Clear();
-            while (Read(m_jsonContext, out tag)) {
+            while (ReadChunk(m_jsonContext, out tag)) {
                 switch (tag) {
                     case JsonGrammar.TokenType.StringBound:
                         tokenValue = ReadString();
                         tokenType = JsonTokenType.String;
                         break;
                     case JsonGrammar.TokenType.Number:
-                        tokenValue = Double.Parse(m_tokenBuilder.ToString(), CultureInfo.InvariantCulture);
+                        tokenValue = m_tokenBuilder.ToString();
                         tokenType = JsonTokenType.Number;
                         break;
                     case JsonGrammar.TokenType.Literal:
@@ -108,7 +164,7 @@
             JsonGrammar.TokenType tag;
             m_tokenBuilder.Clear();
 
-            while (Read(m_stringContext, out tag)) {
+            while (ReadStringChunk(m_stringContext, out tag)) {
                 switch (tag) {
                     case JsonGrammar.TokenType.StringBound:
                         m_tokenBuilder.Length--;
--- a/Implab/Formats/JSON/JsonTextScanner.cs	Tue Sep 12 01:19:12 2017 +0300
+++ b/Implab/Formats/JSON/JsonTextScanner.cs	Tue Sep 12 19:07:42 2017 +0300
@@ -7,7 +7,7 @@
 
 namespace Implab.Formats.Json {
     public class JsonTextScanner : JsonScanner {
-        const int _bufferSize = 4096;
+        const int _bufferSize = 16*4096;
         readonly TextReader m_reader;
 
         JsonTextScanner(TextReader reader, char[] buffer) : base(buffer, 0, 0) {
--- a/Implab/Implab.csproj	Tue Sep 12 01:19:12 2017 +0300
+++ b/Implab/Implab.csproj	Tue Sep 12 19:07:42 2017 +0300
@@ -70,6 +70,7 @@
     <Reference Include="System" />
     <Reference Include="System.Xml" />
     <Reference Include="mscorlib" />
+    <Reference Include="System.Xml.Linq" />
   </ItemGroup>
   <ItemGroup>
     <Compile Include="Components\StateChangeEventArgs.cs" />
@@ -171,7 +172,7 @@
     <Compile Include="Formats\Json\JsonElementContext.cs" />
     <Compile Include="Formats\Json\JsonElementType.cs" />
     <Compile Include="Formats\Json\JsonGrammar.cs" />
-    <Compile Include="Formats\Json\JsonParser.cs" />
+    <Compile Include="Formats\Json\JsonReader.cs" />
     <Compile Include="Formats\Json\JsonScanner.cs" />
     <Compile Include="Formats\Json\JsonTokenType.cs" />
     <Compile Include="Formats\Json\JsonWriter.cs" />
@@ -199,6 +200,8 @@
     <Compile Include="Xml\JsonXmlReader.cs" />
     <Compile Include="Xml\JsonXmlReaderOptions.cs" />
     <Compile Include="Xml\JsonXmlReaderPosition.cs" />
+    <Compile Include="Xml\SerializationHelpers.cs" />
+    <Compile Include="Xml\SerializersPool.cs" />
     <Compile Include="Xml\XmlSimpleAttribute.cs" />
     <Compile Include="Xml\XmlNameContext.cs" />
   </ItemGroup>
--- a/Implab/Xml/JsonXmlReader.cs	Tue Sep 12 01:19:12 2017 +0300
+++ b/Implab/Xml/JsonXmlReader.cs	Tue Sep 12 19:07:42 2017 +0300
@@ -12,7 +12,7 @@
             public bool skip;
         }
 
-        JsonParser m_parser;
+        JsonReader m_parser;
         JsonXmlReaderOptions m_options;
         JsonXmlReaderPosition m_position = JsonXmlReaderPosition.Initial;
         XmlNameTable m_nameTable;
@@ -57,7 +57,7 @@
         readonly string m_xsiNamespace;
 
 
-        public JsonXmlReader(JsonParser parser, JsonXmlReaderOptions options) {
+        public JsonXmlReader(JsonReader parser, JsonXmlReaderOptions options) {
             Safe.ArgumentNotNull(parser, nameof(parser));
             m_parser = parser;
 
@@ -77,7 +77,7 @@
 
             // TODO validate m_jsonRootName, m_jsonArrayItemName
 
-            m_context = new XmlNameContext(null);
+            m_context = new XmlNameContext(null, 0);
         }
 
         public override int AttributeCount {
@@ -314,22 +314,6 @@
                 case XmlNodeType.Element:
                     // if the elemnt is empty the next element will be it's sibling
                     return m_isEmpty;
-
-                case XmlNodeType.Document:
-                case XmlNodeType.DocumentFragment:
-                case XmlNodeType.Entity:
-                case XmlNodeType.Text:
-                case XmlNodeType.CDATA:
-                case XmlNodeType.EntityReference:
-                case XmlNodeType.ProcessingInstruction:
-                case XmlNodeType.Comment:
-                case XmlNodeType.DocumentType:
-                case XmlNodeType.Notation:
-                case XmlNodeType.Whitespace:
-                case XmlNodeType.SignificantWhitespace:
-                case XmlNodeType.EndElement:
-                case XmlNodeType.EndEntity:
-                case XmlNodeType.XmlDeclaration:
                 default:
                     return true;
             }
@@ -351,25 +335,29 @@
             if (!IsSibling()) // the node is nested
                 m_xmlDepth++;
 
-            m_context = new XmlNameContext(m_context);
+            var context = m_context;
             List<XmlSimpleAttribute> definedAttrs = null;
 
             // define new namespaces
             if (attrs != null) {
                 foreach (var attr in attrs) {
                     if (attr.QName.Name == "xmlns") {
-                        m_context.DefinePrefix(ConvertValueToString(attr.Value), string.Empty);
+                        if (context == m_context)
+                             context = new XmlNameContext(m_context, m_xmlDepth);
+                        context.DefinePrefix(ConvertValueToString(attr.Value), string.Empty);
                     } else if (attr.Prefix == m_xmlnsPrefix) {
-                        m_context.DefinePrefix(ConvertValueToString(attr.Value), attr.QName.Name);
+                        if (context == m_context)
+                            context = new XmlNameContext(m_context, m_xmlDepth);
+                        context.DefinePrefix(ConvertValueToString(attr.Value), attr.QName.Name);
                     } else {
                         string attrPrefix;
                         if (string.IsNullOrEmpty(attr.QName.Namespace))
                             continue;
 
                         // auto-define prefixes
-                        if (!m_context.LookupNamespacePrefix(attr.QName.Namespace, out attrPrefix) || string.IsNullOrEmpty(attrPrefix)) {
+                        if (!context.LookupNamespacePrefix(attr.QName.Namespace, out attrPrefix) || string.IsNullOrEmpty(attrPrefix)) {
                             // new namespace prefix added
-                            attrPrefix = m_context.CreateNamespacePrefix(attr.QName.Namespace);
+                            attrPrefix = context.CreateNamespacePrefix(attr.QName.Namespace);
                             attr.Prefix = attrPrefix;
 
                             if (definedAttrs == null)
@@ -383,8 +371,10 @@
 
             string p;
             // auto-define prefixes
-            if (!m_context.LookupNamespacePrefix(ns, out p)) {
-                p = m_context.CreateNamespacePrefix(ns);
+            if (!context.LookupNamespacePrefix(ns, out p)) {
+                if (context == m_context)
+                    context = new XmlNameContext(m_context, m_xmlDepth);
+                p = context.CreateNamespacePrefix(ns);
                 if (definedAttrs == null)
                     definedAttrs = new List<XmlSimpleAttribute>();
 
@@ -397,6 +387,9 @@
                 attrs = definedAttrs.ToArray();
             }
 
+            if (!empty)
+                m_context = context;
+
             m_nodeType = XmlNodeType.Element;
             m_qName = new XmlQualifiedName(name, ns);
             m_prefix = p;
@@ -406,14 +399,18 @@
         }
 
         void EndElementNode(string name, string ns) {
-            if (IsSibling()) // closing the element which has children
+            if (IsSibling()) {
+                // closing the element which has children
                 m_xmlDepth--;
+            }
 
             string p;
             if (!m_context.LookupNamespacePrefix(ns, out p))
                 throw new Exception($"Failed to lookup namespace '{ns}'");
 
-            m_context = m_context.ParentContext;
+            if (m_context.Depth == m_xmlDepth)
+                m_context = m_context.ParentContext;
+
             m_nodeType = XmlNodeType.EndElement;
             m_prefix = p;
             m_qName = new XmlQualifiedName(name, ns);
@@ -456,7 +453,10 @@
                         break;
                     case JsonXmlReaderPosition.ValueElement:
                         if (!m_isEmpty) {
-                            ValueNode(m_parser.ElementValue);
+                            if (m_parser.ElementValue != null && !m_parser.ElementValue.Equals(string.Empty))
+                                ValueNode(m_parser.ElementValue);
+                            else
+                                goto case JsonXmlReaderPosition.ValueContent;
                             m_position = JsonXmlReaderPosition.ValueContent;
                             return true;
                         } else {
@@ -521,7 +521,7 @@
                                     true
                                 );
                             else
-                                ElementNode(m_jsonValueName, m_jsonNamespace, elementAttrs, m_parser.ElementValue as string == string.Empty);
+                                ElementNode(m_jsonValueName, m_jsonNamespace, elementAttrs, m_parser.ElementValue.Equals(string.Empty));
                             break;
                         default:
                             throw new Exception($"Unexpected JSON element {m_parser.ElementType}: {m_parser.ElementName}");
--- a/Implab/Xml/JsonXmlReaderPosition.cs	Tue Sep 12 01:19:12 2017 +0300
+++ b/Implab/Xml/JsonXmlReaderPosition.cs	Tue Sep 12 19:07:42 2017 +0300
@@ -5,7 +5,7 @@
 using System.Threading.Tasks;
 
 namespace Implab.Xml {
-    public enum JsonXmlReaderPosition {
+    enum JsonXmlReaderPosition {
         Initial,
         Declaration,
         BeginArray,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/Xml/SerializationHelpers.cs	Tue Sep 12 19:07:42 2017 +0300
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml;
+using System.Xml.Linq;
+
+namespace Implab.Xml {
+    public static class SerializationHelpers {
+        public static string SerializeAsString<T>(T obj) {
+            return SerializersPool<T>.Instance.SerializeAsString(obj);
+        }
+
+        public static void Serialize<T>(XmlWriter writer, T obj) {
+            SerializersPool<T>.Instance.Serialize(writer, obj);
+        }
+
+        public static XmlDocument SerializeAsXmlDocument<T>(T obj) {
+            var doc = new XmlDocument();
+            using (var writer = doc.CreateNavigator().AppendChild()) {
+                SerializersPool<T>.Instance.Serialize(writer, obj);
+            }
+            return doc;
+        }
+
+        public static XDocument SerializeAsXDocument<T>(T obj) {
+            var doc = new XDocument();
+            using (var writer = doc.CreateWriter()) {
+                SerializersPool<T>.Instance.Serialize(writer, obj);
+            }
+            return doc;
+        }
+
+        public static T DeserializeFromString<T>(string data) {
+            return SerializersPool<T>.Instance.DeserializeFromString(data);
+        }
+
+        public static T DeserializeFromXmlNode<T>(XmlNode node) {
+            Safe.ArgumentNotNull(node, nameof(node));
+            using (var reader = node.CreateNavigator().ReadSubtree())
+                return SerializersPool<T>.Instance.Deserialize(reader);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/Xml/SerializersPool.cs	Tue Sep 12 19:07:42 2017 +0300
@@ -0,0 +1,76 @@
+using Implab.Components;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml;
+using System.Xml.Serialization;
+
+namespace Implab.Xml {
+    public class SerializersPool<T> : ObjectPool<XmlSerializer> {
+
+        static readonly SerializersPool<T> _instance = new SerializersPool<T>();
+
+        public static SerializersPool<T> Instance {
+            get { return _instance; }
+        }
+
+        #region implemented abstract members of ObjectPool
+        protected override XmlSerializer CreateInstance() {
+            return new XmlSerializer(typeof(T));
+        }
+        #endregion
+
+        public T DeserializeFromString(string data) {
+            using (var reader = new StringReader(data)) {
+                return Deserialize(reader);
+            }
+        }
+
+        public T Deserialize(TextReader reader) {
+            var sr = Allocate();
+            try {
+                return (T)sr.Deserialize(reader);
+            } finally {
+                Release(sr);
+            }
+        }
+
+        public T Deserialize(XmlReader reader) {
+            var sr = Allocate();
+            try {
+                return (T)sr.Deserialize(reader);
+            } finally {
+                Release(sr);
+            }
+        }
+
+        public string SerializeAsString(T data) {
+            using (var writer = new StringWriter()) {
+                Serialize(writer, data);
+                return writer.ToString();
+            }
+        }
+
+        public void Serialize(TextWriter writer, T data) {
+            var sr = Allocate();
+            try {
+                sr.Serialize(writer, data);
+            } finally {
+                Release(sr);
+            }
+        }
+
+        public void Serialize(XmlWriter writer, T data) {
+            var sr = Allocate();
+            try {
+                sr.Serialize(writer, data);
+            } finally {
+                Release(sr);
+            }
+        }
+
+    }
+}
--- a/Implab/Xml/XmlNameContext.cs	Tue Sep 12 01:19:12 2017 +0300
+++ b/Implab/Xml/XmlNameContext.cs	Tue Sep 12 19:07:42 2017 +0300
@@ -6,7 +6,7 @@
 using System.Xml;
 
 namespace Implab.Xml {
-    public class XmlNameContext {
+    class XmlNameContext {
         public const string XmlnsNamespace = "http://www.w3.org/2000/xmlns/";
         public const string XmlnsPrefix = "xmlns";
         public const string XmlNamespace = "http://www.w3.org/XML/1998/namespace";
@@ -19,11 +19,17 @@
         Dictionary<string, string> m_ns2prefix;
         Dictionary<string, string> m_prefix2ns;
         int m_nextPrefix = 1;
+        string m_lastNs;
+        string m_lastPrefix;
         
         public XmlNameContext ParentContext { get; private set; }
 
-        public XmlNameContext(XmlNameContext parent) {
+        public int Depth { get; private set; }
+
+        public XmlNameContext(XmlNameContext parent, int depth) {
             ParentContext = parent;
+            Depth = depth;
+
             if (parent == null) {
                 DefinePrefixNoCheck(XmlnsNamespace, XmlnsPrefix);
                 DefinePrefixNoCheck(XmlNamespace, XmlPrefix);
@@ -35,12 +41,17 @@
         public bool LookupNamespacePrefix(string ns, out string prefix) {
             if (ns == null)
                 ns = string.Empty;
+            if (ns == m_lastNs) {
+                prefix = m_lastPrefix;
+                return true;
+            }
+                
 
             prefix = null;
             for (var ctx = this; ctx != null; ctx = ctx.ParentContext) {
-                if (ctx.m_ns2prefix?.TryGetValue(ns, out prefix) == true) {
-                    if (ctx != this) // cache for the future use
-                        DefinePrefixNoCheck(ns, prefix);
+                if (ctx.m_ns2prefix != null && ctx.m_ns2prefix.TryGetValue(ns, out prefix)) {
+                    m_lastNs = ns;
+                    m_lastPrefix = prefix;
                     return true;
                 }
             }
@@ -88,11 +99,8 @@
                 prefix = string.Empty;
             string ns = null;
             for(var ctx = this; ctx != null; ctx = ctx.ParentContext) {
-                if (ctx.m_prefix2ns?.TryGetValue(prefix, out ns) == true) {
-                    if (ctx != this) // cache for the future use
-                        DefinePrefixNoCheck(ns, prefix);
+                if (ctx.m_prefix2ns != null && ctx.m_prefix2ns.TryGetValue(prefix, out ns) == true)
                     return ns;
-                }
             }
             return null;
         }