diff --git a/src/SharpConnect.Data/Es/EsDocParser.cs b/src/SharpConnect.Data/Es/EsDocParser.cs index 4dbac46..fe68e31 100644 --- a/src/SharpConnect.Data/Es/EsDocParser.cs +++ b/src/SharpConnect.Data/Es/EsDocParser.cs @@ -2,27 +2,218 @@ namespace SharpConnect.Data { - class EaseDocParser : EsParserBase + class EaseDocParser : EsParserBase { EaseDocument _easeDoc; + + int _ext_n_keyIndex; + int _ext_c_keyIndex; public EaseDocParser(EaseDocument blankdoc) { _easeDoc = blankdoc; + _ext_n_keyIndex = RegisterKey("!n"); + _ext_c_keyIndex = RegisterKey("!c"); } + public bool EnableExtension { get; set; } + protected override EaseElement CreateElement() => _easeDoc.CreateElement(); - protected override EsElem CreateElement() => _easeDoc.CreateElement(); + protected override EaseArray CreateArray() => _easeDoc.CreateArray(); - protected override EsArr CreateArray() => _easeDoc.CreateArray(); + + protected override void AddElementAttribute(EaseElement targetElem, int key, EaseArray value) + { + if (EnableExtension) + { + if (key == _ext_c_keyIndex) + { + //name + int j = value.Count; + for (int i = 0; i < j; ++i) + { + targetElem.AppendChild(value[i]); + } + return; + } + } - protected override void AddElementAttribute(EsElem targetElem, string key, object value) + targetElem.AppendAttribute(GetKeyAsStringByIndex(key), value); + } + protected override void AddElementAttribute(EaseElement targetElem, int key, EaseElement value) { - targetElem.AppendAttribute(key, value); + targetElem.AppendAttribute(GetKeyAsStringByIndex(key), value); } + protected override void AddElementAttribute(EaseElement targetElem, int key, EsValueHint valueHint) + { + //read key and its value + if (EnableExtension) + { + if (key == _ext_n_keyIndex && valueHint == EsValueHint.StringLiteral) + { + //name + ((EaseElement)targetElem).Name = GetValueAsString(); + return; + } + } + + switch (valueHint) + { + default: + { - protected override void AddArrayElement(EsArr targetArray, object value) + } + break; + case EsValueHint.Null: + targetElem.AppendAttribute(GetKeyAsStringByIndex(key), null); + break; + case EsValueHint.True: + targetElem.AppendAttribute(GetKeyAsStringByIndex(key), true); + break; + case EsValueHint.False: + { + targetElem.AppendAttribute(GetKeyAsStringByIndex(key), false); + } + break; + case EsValueHint.StringLiteral: + { + string str = GetValueAsString(); + targetElem.AppendAttribute(GetKeyAsStringByIndex(key), str); + } + break; + case EsValueHint.StringLiteralWithSomeEscape: + { + string str = GetValueAsStringWithEscape(); + targetElem.AppendAttribute(GetKeyAsStringByIndex(key), str); + } + break; + case EsValueHint.NegativeIntegerNumber: + { + if (ConcatValueLen > 9) + { + long value = GetValueAsInt64(); + targetElem.AppendAttribute(GetKeyAsStringByIndex(key), value); + } + else + { + int value = GetValueAsInt32(); + targetElem.AppendAttribute(GetKeyAsStringByIndex(key), value); + } + + } + break; + case EsValueHint.IntegerNumber: + { + //int64 + if (ConcatValueLen > 9) + { + long value = GetValueAsInt64(); + targetElem.AppendAttribute(GetKeyAsStringByIndex(key), value); + } + else + { + int value = GetValueAsInt32(); + targetElem.AppendAttribute(GetKeyAsStringByIndex(key), value); + } + } + break; + case EsValueHint.NumberWithExponentialPart: + { + double num = GetValueAsDouble(); + targetElem.AppendAttribute(GetKeyAsStringByIndex(key), num); + } + break; + case EsValueHint.NumberWithFractionPart: + { + double num = GetValueAsDouble(); + targetElem.AppendAttribute(GetKeyAsStringByIndex(key), num); + } + break; + } + } + + protected override void AddArrayElement(EaseArray targetArray, EaseArray value) { targetArray.AddItem(value); } + protected override void AddArrayElement(EaseArray targetArray, EaseElement value) + { + targetArray.AddItem(value); + } + + protected override void AddArrayElement(EaseArray targetArray, EsValueHint valueHint) + { + switch (valueHint) + { + default: + { + + } + break; + case EsValueHint.Null: + targetArray.AddItem(null); + break; + case EsValueHint.True: + targetArray.AddItem(true); + break; + case EsValueHint.False: + targetArray.AddItem(false); + break; + case EsValueHint.StringLiteral: + { + string str = GetValueAsString(); + targetArray.AddItem(str); + } + break; + case EsValueHint.StringLiteralWithSomeEscape: + { + string str = GetValueAsString(); + targetArray.AddItem(str); + } + break; + case EsValueHint.NegativeIntegerNumber: + { + if (ConcatValueLen > 9) + { + long value = GetValueAsInt64(); + targetArray.AddItem(value); + } + else + { + int value = GetValueAsInt32(); + targetArray.AddItem(value); + } + + } + break; + case EsValueHint.IntegerNumber: + { + //int64 + if (ConcatValueLen > 9) + { + long value = GetValueAsInt64(); + targetArray.AddItem(value); + } + else + { + int value = GetValueAsInt32(); + targetArray.AddItem(value); + } + } + break; + case EsValueHint.NumberWithExponentialPart: + { + double num = GetValueAsDouble(); + targetArray.AddItem(num); + } + break; + case EsValueHint.NumberWithFractionPart: + { + double num = GetValueAsDouble(); + targetArray.AddItem(num); + } + break; + } + } + } } diff --git a/src/SharpConnect.Data/TreeModel/EsDataImpl.cs b/src/SharpConnect.Data/TreeModel/EsDataImpl.cs index cbb9b8d..5298446 100644 --- a/src/SharpConnect.Data/TreeModel/EsDataImpl.cs +++ b/src/SharpConnect.Data/TreeModel/EsDataImpl.cs @@ -7,57 +7,43 @@ namespace SharpConnect.Data { public class EaseDocument : EsDoc { - //Dictionary _stringTable = new Dictionary(); public EaseDocument() { } - public EsElem CreateElement(string elementName) + public EaseElement CreateElement(string elementName) { var elem = new EaseElement(); elem.Name = elementName; return elem; } - - public EsElem CreateElement() => new EaseElement(); - - public EsArr CreateArray() => new EaseArray(); - - //public int GetStringIndex(string str) - //{ - // _stringTable.TryGetValue(str, out int found); - // return found; - //} - + public bool EnableExtension { get; set; } + public EaseElement CreateElement() => new EaseElement(); + public EaseArray CreateArray() => new EaseArray(); public EsElem DocumentElement { get; set; } + public EaseAttribute CreateAttribute(string key, object value) => new EaseAttribute(key, value); + + } - public EsElem Parse(string jsonstr) + public static class EaseDocumentExtensions + { + public static object Parse(this EaseDocument doc, string jsonstr) { - return Parse(jsonstr.ToCharArray()); + return Parse(doc, jsonstr.ToCharArray()); } - public EsElem Parse(char[] jsonstr) + public static object Parse(this EaseDocument doc, char[] jsonstr) { - var parser = new EaseDocParser(this); + var parser = new EaseDocParser(doc); + parser.EnableExtension = doc.EnableExtension; parser.Parse(jsonstr); - return parser.CurrentElement as EsElem; + return parser.CurrentValue; } - public EsAttr CreateAttribute(string key, object value) => new EaseAttribute(key, value); } - - static class EsElemHelper - { - public static EsElem CreateXmlElementForDynamicObject(EsDoc doc) - { - var elem = new EaseElement(); - elem.Name = "!j"; - return elem; - } - } - class EaseArray : EsArr + public sealed class EaseArray : EsArr { List _member = new List(); - + internal EaseArray() { } public object this[int index] { get => _member[index]; @@ -85,16 +71,26 @@ public IEnumerable GetIter() } } - class EaseElement : EsElem + public sealed class EaseElement : EsElem { - List _childNodes; + List _childNodes; Dictionary _attrs = new Dictionary(); List _attrsValues = new List(); - public EaseElement() +#if DEBUG + static int s_dbugTotal; + public readonly int dbugId = s_dbugTotal++; +#endif + internal EaseElement() { Name = ""; +#if DEBUG + if (dbugId == 409) + { + + } +#endif } public string Name { get; set; } public int AttributeCount => _attrs.Count; @@ -111,11 +107,11 @@ public IEnumerable GetAttributeIterForward() } } - public void AppendChild(EsElem element) + public void AppendChild(object element) { if (_childNodes == null) { - _childNodes = new List(); + _childNodes = new List(); } _childNodes.Add(element); } @@ -165,11 +161,15 @@ public EsAttr GetAttribute(string key) return null; } public object UserData { get; set; } + +#if DEBUG + public override string ToString() => Name; +#endif } - class EaseAttribute : EsAttr + public sealed class EaseAttribute : EsAttr { - public EaseAttribute(string name, object value) + internal EaseAttribute(string name, object value) { Name = name; Value = value; @@ -179,6 +179,11 @@ public EaseAttribute(string name, object value) public override string ToString() => Name + ":" + Value; } + public class EsElemJsonWriteParameters + { + public bool WithElemName { get; set; } + public StringBuilder Output { get; set; } + } public static class EsElemExtensionMethods { public static string GetAttrValueOrDefaultAsString(this EsElem esElem, string attrName) @@ -256,18 +261,24 @@ public static EsElem GetAttributeValueAsElem(this EsElem esElem, string attrName } //----------------------------------------------------------------------- - public static void WriteJson(this EsDoc doc, StringBuilder sb) + public static void WriteJson(this EsDoc doc, EsElemJsonWriteParameters writePars) { //write to var docElem = doc.DocumentElement; if (docElem != null) { - WriteJson(docElem, sb); + WriteJson(docElem, writePars); } } - - static void WriteJson(EsArr esArr, StringBuilder sb) + public static void WriteJson(this EsDoc doc, StringBuilder stbuilder) + { + EsElemJsonWriteParameters pars = new EsElemJsonWriteParameters(); + pars.Output = stbuilder; + WriteJson(doc, pars); + } + static void WriteJson(EsArr esArr, EsElemJsonWriteParameters pars) { + StringBuilder sb = pars.Output; sb.Append('['); int j = esArr.Count; for (int i = 0; i < j; ++i) @@ -276,13 +287,18 @@ static void WriteJson(EsArr esArr, StringBuilder sb) { sb.Append(','); } - WriteJson(esArr[i], sb); + WriteJson(esArr[i], pars); } sb.Append(']'); } public static void WriteJson(this EsElem esElem, StringBuilder sb) { + WriteJson(esElem, new EsElemJsonWriteParameters { Output = sb }); + } + public static void WriteJson(this EsElem esElem, EsElemJsonWriteParameters pars) + { + StringBuilder sb = pars.Output; EaseElement elem = (EaseElement)esElem; sb.Append('{'); //check docattr= @@ -292,18 +308,26 @@ public static void WriteJson(this EsElem esElem, StringBuilder sb) { //TODO: review here if we want auto element name or not? //use specific name - //stBuilder.Append("\"!n\":\""); - //stBuilder.Append(leqE.Name); - //stBuilder.Append('"'); + if (pars.WithElemName && elem.Name != null) + { + sb.Append("\"!n\":\""); + //TODO: review string escape here *** + sb.Append(elem.Name); + sb.Append('"'); + attrCount = 1; + } } else { //use default elementname - sb.Append("\"!n\":\""); - //TODO: review string escape here *** - sb.Append(elem.Name); - sb.Append('"'); - attrCount = 1; + if (elem.Name != null) + { + sb.Append("\"!n\":\""); + //TODO: review string escape here *** + sb.Append(elem.Name); + sb.Append('"'); + attrCount = 1; + } } foreach (EsAttr attr in elem.GetAttributeIterForward()) { @@ -319,7 +343,7 @@ public static void WriteJson(this EsElem esElem, StringBuilder sb) sb.Append(attr.Name); //TODO: review escape string here sb.Append('"'); sb.Append(':'); - WriteJson(attr.Value, sb); + WriteJson(attr.Value, pars); attrCount++; } //------------------- @@ -339,7 +363,7 @@ public static void WriteJson(this EsElem esElem, StringBuilder sb) { sb.Append(','); } - WriteJson(elem.GetChild(i), sb); + WriteJson(elem.GetChild(i), pars); } sb.Append(']'); } @@ -347,21 +371,24 @@ public static void WriteJson(this EsElem esElem, StringBuilder sb) sb.Append('}'); } - + public static string ToJsonString(this EsElem esElem) { + //TODO: review here again var stbuilder = new StringBuilder(); - esElem.WriteJson(stbuilder); + var pars = new EsElemJsonWriteParameters(); + pars.Output = stbuilder; + esElem.WriteJson(pars); return stbuilder.ToString(); } - - public static void WriteJson(object elem, StringBuilder sb) + public static void WriteJson(object elem, EsElemJsonWriteParameters pars) { //recursive #if DEBUG Type t = elem.GetType(); #endif + StringBuilder sb = pars.Output; if (elem == null) { sb.Append("null"); @@ -394,17 +421,17 @@ public static void WriteJson(object elem, StringBuilder sb) { sb.Append(','); } - WriteJson(a.GetValue(i), sb); + WriteJson(a.GetValue(i), pars); } sb.Append(']'); } else if (elem is EaseElement ease_elem) { - WriteJson(ease_elem, sb); + WriteJson(ease_elem, pars); } else if (elem is EsArr es_arr) { - WriteJson(es_arr, sb); + WriteJson(es_arr, pars); } else if (elem is DateTime d) { diff --git a/src/SharpConnect.Data/TreeModel/EsDataInterface.cs b/src/SharpConnect.Data/TreeModel/EsDataInterface.cs index 1a6a2da..6223e1c 100644 --- a/src/SharpConnect.Data/TreeModel/EsDataInterface.cs +++ b/src/SharpConnect.Data/TreeModel/EsDataInterface.cs @@ -2,16 +2,15 @@ using System.Collections.Generic; namespace SharpConnect.Data { + + /// /// ease element /// public interface EsElem { - string Name { get; set; } + string Name { get; } IEnumerable GetAttributeIterForward(); - void RemoveAttribute(string key); - void AppendChild(EsElem element); - void AppendAttribute(string key, object value); object GetAttributeValue(string key); EsAttr GetAttribute(string key); EsAttr GetAttribute(int index); @@ -19,8 +18,10 @@ public interface EsElem int ChildCount { get; } int AttributeCount { get; } object GetChild(int index); - object UserData { get; set; } + object UserData { get; set; } //TODO review this again } + + /// /// ease attribute /// @@ -34,11 +35,11 @@ public interface EsAttr /// public interface EsArr { - void AddItem(object item); + IEnumerable GetIter(); void Clear(); int Count { get; } - object this[int index] { get; set; } + object this[int index] { get; } } /// @@ -46,9 +47,24 @@ public interface EsArr /// public interface EsDoc { - EsElem CreateElement(); - EsElem CreateElement(string name); - EsArr CreateArray(); + //EsElem CreateElement(); + //EsElem CreateElement(string name); + //EsArr CreateArray(); EsElem DocumentElement { get; set; } } + + public static class EsElemExtensions + { + public static IEnumerable GetChildNodeIter(this EsElem elem) + { + int n = elem.ChildCount; + for (int i = 0; i < n; ++i) + { + if (elem.GetChild(i) is EsElem childElem) + { + yield return childElem; + } + } + } + } } \ No newline at end of file diff --git a/src/SharpConnect.Data/TreeModel/EsParser.cs b/src/SharpConnect.Data/TreeModel/EsParser.cs index 72a5dfc..28db8b1 100644 --- a/src/SharpConnect.Data/TreeModel/EsParser.cs +++ b/src/SharpConnect.Data/TreeModel/EsParser.cs @@ -5,49 +5,99 @@ namespace SharpConnect.Data { + public enum EsValueHint : byte + { + Unknown, + None, //None => no attribute here (!= null) + + True, False, Null, + + + StringLiteral, + StringLiteralWithSomeEscape, + IntegerNumber, + NegativeIntegerNumber, + + NumberWithFractionPart, + NumberWithExponentialPart, + + Identifier, + Comment,//extension + Object, + Array, + } + /// /// event-driven json-like parser /// public abstract class EsParserBase { + public class TextSourceProvider + { + public char[] Buffer { get; set; } + public int StartAt { get; set; } + public int Len { get; set; } + + public int TotalOffset { get; set; } + } + public struct BuffRange + { + public readonly EsValueHint hint; + public readonly int startAt; + public readonly int len; + public BuffRange(int startAt, int len) + { + this.startAt = startAt; + this.len = len; + hint = EsValueHint.Unknown; + } + public BuffRange(int startAt, int len, EsValueHint hint) + { + this.startAt = startAt; + this.len = len; + this.hint = hint; + } + } + enum EsElementKind { Unknown, Object, Array } + enum ParsingState { - _0_Init, - _1_ObjectKey, - _2_CollectStringLiteral, - _3_StringEscape, - _4_FinishKeyPartWaitForSemiColon, - _5_ExpectObjectValueOrArrayElement, - _6_AfterObjectValueOrArrayElement, - _7_CollectNumberLiteral, - _8_CollectIdentifier, + _1_ExpectObjectValueOrArrayElement, + _2_ExpectObjectKey, + _3_WaitForColon, + _4_WaitForCommaOrEnd, } - enum NumberPart + + enum NumberPart : byte { IntegerPart, + IntegerPart2, FractionPart, - E, //e or E - ESign,//e and sign ExponentialPart, } - protected enum ValueHint + + + public struct NumberParts { - Unknown, - StringLiteral, - IntegerNumber, - NumberWithFractionPart, - NumberWithExponentialPart, - NumberWithSignedExponentialPart, - Identifier, - Comment,//extension + public bool integer_minus; + public int integer_at; + public byte integer_len; //255 + // + public ushort fraction_offset; //offset from intger_at + public byte fraction_len; //255 + // + public bool exponent_minus; + public ushort exponent_offset; //offset from intger_at + public byte exponent_len; //255 } + #if DEBUG public static bool dbug_EnableLogParser = false; public static int dbug_file_count = 0; @@ -79,19 +129,21 @@ protected virtual void EndArray() { } - protected virtual void NewKey(StringBuilder tmpBuffer, ValueHint valueHint) + + protected virtual void NewConcatKey(int len) { + } + protected virtual void NewKey(int start, int len) + { } - protected virtual void NewValue(StringBuilder tmpBuffer, ValueHint valueHint) + protected virtual void NewValue(int start, int len) { - } - protected virtual void OnError(ref int currentIndex) + protected virtual void NewConcatValue(int len) { - - } + protected virtual void Comma() { } protected virtual void OnParseEnd() { @@ -103,8 +155,521 @@ protected virtual void OnParseStart() protected virtual void NotifyError() { } - static void ReadSingleLineComment(char[] sourceBuffer, int startAt, ref int latestIndex) + + static bool ReadIdentifier(EsParserBase p, int startAt, out int len) + { + p.CollectedValueHint = EsValueHint.Identifier; + + int pos = startAt; + char[] sourceBuffer = p._sourceBuffer; + int lim = sourceBuffer.Length; + + len = 0; + if (pos >= lim) + { //may not end of the iden + + p._collectingState = CollectingState.Iden; + return false; + } + + do + { + char c = sourceBuffer[pos]; + if (c == '_' || char.IsLetterOrDigit(c)) + { + //collect + pos++; + len++; + } + else + { + //finish + p._collectingState = CollectingState.None; + return true; + } + + } while (pos < lim); + + //may not end of the iden + p._collectingState = CollectingState.Iden; + return false; + } + + static bool ReadStringLiteral2(EsParserBase p, char escapeChar, int startAt, out int collected_len) + { + + char[] sourceBuffer = p._sourceBuffer; + p.CollectedValueHint = EsValueHint.StringLiteral; + collected_len = 0; + + int pos = startAt; + char c = sourceBuffer[pos]; + + switch (p._collectingState) + { + case CollectingState.Escape_U1: + { + p._collectingState = CollectingState.Char; + pos += 3; + collected_len += 3; + } + break; + case CollectingState.Escape_U2: + { + p._collectingState = CollectingState.Char; + pos += 2; + collected_len += 2; + } + break; + case CollectingState.Escape_U3: + { + p._collectingState = CollectingState.Char; + pos++; + collected_len++; + } + break; + case CollectingState.Escape: + { + //after escape + switch (c) + { + default: + throw new NotSupportedException(); + case 'u': + { + //uint c_uint = ParseUnicode( + //sourceBuffer[pos + 1], + //sourceBuffer[pos + 2], + //sourceBuffer[pos + 3], + //sourceBuffer[pos + 4]); + pos += 4; + collected_len += 4; + p._collectingState = CollectingState.Char; + } + break; + case '\\': + case '/': + case 'r': + case 'n': + case 't': + case 'b': + case 'f': + case '"': + { + pos++; + collected_len++; + p._collectingState = CollectingState.Char; + } + break; + } + } + break; + } + + if (!ReadStringLiteral(p, escapeChar, pos, out int collectedLen2)) + { + return false; + } + else + { + collected_len += collectedLen2; + return true; + } + } + + static bool ReadStringLiteral(EsParserBase p, char escapeChar, int startAt, out int collected_len) + { + char[] sourceBuffer = p._sourceBuffer; + + p.CollectedValueHint = EsValueHint.StringLiteral; + p._collectingState = CollectingState.Char;//** + + collected_len = 0; + int pos = startAt; + if (pos >= sourceBuffer.Length) + { + return false; + } + + char c = sourceBuffer[pos]; + int lim = sourceBuffer.Length; + + bool complete = false; + if (escapeChar == '"') + { + while (pos < lim) + { + if (c == '"') + { + //stop here + complete = true; + p._collectingState = CollectingState.None; + collected_len++; + break; + } + //read until stop + if (c == '\\') //escape + { + collected_len++; + p._collectingState = CollectingState.Escape; + p.CollectedValueHint = EsValueHint.StringLiteralWithSomeEscape; + //escape mode 1 char + if (pos + 1 < lim) + { + //read next char + char c2 = sourceBuffer[pos + 1]; + switch (c2) + { + default: + //error + throw new NotSupportedException(); + break; + //case '\'': //extension + // pos++; + // break; + case '"': + case '/': + case '\\': + case 'b': // backspace + case 'f': //form feed + case 'n': //newline + case 'r': //carriage return + case 't'://t + collected_len++; + pos++; + p._collectingState = CollectingState.Char; + break; + case 'u': + if (pos < lim - 4) + { + //json spec + //this follow by 4 chars + //for extension we check if it match with 4 chars or not + p._collectingState = CollectingState.Char; + + //uint c_uint = ParseUnicode( + // sourceBuffer[pos + 1], + // sourceBuffer[pos + 2], + // sourceBuffer[pos + 3], + // sourceBuffer[pos + 4]); + + pos += 4; + collected_len += 4; + } + else + { + //incomplete + + int waiting = lim - pos - 1; + switch (waiting) + { + case 3: + pos += 3; + collected_len += 3; + p._collectingState = CollectingState.Escape_U3; + break; + case 2: + pos += 2; + collected_len += 2; + p._collectingState = CollectingState.Escape_U2; + break; + + case 1: + pos += 1; + collected_len += 1; + p._collectingState = CollectingState.Escape_U1; + break; + default: + throw new NotSupportedException(); + } + } + break; + } + } + else + { + //collect more + p._collectingState = CollectingState.Escape; + } + } + else + { + collected_len++; + } + + pos++; + if (pos < lim) + { + c = sourceBuffer[pos]; + } + else + { + break; + } + } + } + else if (escapeChar == '\'') + { + throw new NotSupportedException(); + } + + if (!complete) + { + + } + + return complete; + } + + /// + /// save latest read pos on current buffer + /// + /// + protected virtual void SaveLatestReadPos(int startAt) + { + + } + + static bool ReadNumberLiteral(EsParserBase p, int startAt, out int collected_len) + { + char[] sourceBuffer = p._sourceBuffer; + NumberPart state = NumberPart.IntegerPart; + int lim = sourceBuffer.Length; + + int i = startAt; + collected_len = 0; + //10-based + + NumberParts numParts = new NumberParts(); + numParts.integer_at = startAt; + + int integer_part_count = 0; + int fraction_part_count = 0; + int exponent_part_count = 0; + bool finish = false; + + CollectingState collectingState = p._collectingState;//** + switch (collectingState) + { + default: + break; + case CollectingState.Num_Exponent_NumPart: + state = NumberPart.ExponentialPart; + break; + case CollectingState.Num_Exponent_E: + state = NumberPart.ExponentialPart; + break; + case CollectingState.Num_Fraction: + state = NumberPart.FractionPart; + break; + case CollectingState.Num_Integer: + state = NumberPart.IntegerPart2; + break; + case CollectingState.None: + break; + } + for (; i < lim; ++i) + { + char c = sourceBuffer[i]; + switch (state) + { + case NumberPart.IntegerPart: + { + if (c == '-') + { + //start integer with minus + collectingState = CollectingState.Num_Integer; + state = NumberPart.IntegerPart2;//after minus + numParts.integer_minus = true; + numParts.integer_at++; + collected_len++; + } + else if (char.IsDigit(c)) + { + //collect more + //accum + collectingState = CollectingState.Num_Integer; + state = NumberPart.IntegerPart2;//after minus + integer_part_count++; + collected_len++; + } + + else + { + //this char is not part of the literal number + collectingState = CollectingState.None;//finish + finish = true; + goto EXIT; + //break + //summary and return + } + } + break; + case NumberPart.IntegerPart2: + { + if (char.IsDigit(c)) + { + //collect more + //accum + integer_part_count++; + collected_len++; + } + else if (c == 'e' || c == 'E') + { + collectingState = CollectingState.Num_Exponent_E; + state = NumberPart.ExponentialPart; + collected_len++;//collect this e or E + if (i + 1 < lim) + { + i++; //consume e or E + c = sourceBuffer[i + 1]; + if (c == '+') + { + //ok + collectingState = CollectingState.Num_Exponent_NumPart; + collected_len++;//collect this e or E + numParts.exponent_offset = (ushort)((i + 2) - numParts.integer_at); + } + else if (c == '-') + { + collectingState = CollectingState.Num_Exponent_NumPart; + numParts.exponent_minus = true; + collected_len++;//collect this e or E + numParts.exponent_offset = (ushort)((i + 2) - numParts.integer_at); + } + else + { + //must be number + numParts.exponent_offset = (ushort)((i + 1) - numParts.integer_at); + goto case NumberPart.ExponentialPart;// + } + } + } + else if (c == '.') + { + //fraction + collected_len++; + numParts.fraction_offset = (ushort)((i + 1) - numParts.integer_at); + state = NumberPart.FractionPart; + collectingState = CollectingState.Num_Fraction; + } + else + { + //this char is not part of the literal number + collectingState = CollectingState.None;//finish + finish = true; + goto EXIT; + //break + //summary and return + } + } + break; + case NumberPart.FractionPart: + { + //fraction part + if (char.IsDigit(c)) + { + //same state + //collect more + fraction_part_count++; + collected_len++; + } + else if (c == 'e' || c == 'E') + { + //exponent part + //base 10 + collectingState = CollectingState.Num_Exponent_E; + state = NumberPart.ExponentialPart; + collected_len++;//collect this e or E + if (i + 1 < lim) + { + i++; //consume e or E + c = sourceBuffer[i + 1]; + if (c == '+') + { + //ok + collectingState = CollectingState.Num_Exponent_NumPart; + collected_len++;//collect this e or E + numParts.exponent_offset = (ushort)((i + 2) - numParts.integer_at); + } + else if (c == '-') + { + collectingState = CollectingState.Num_Exponent_NumPart; + numParts.exponent_minus = true; + collected_len++;//collect this e or E + numParts.exponent_offset = (ushort)((i + 2) - numParts.integer_at); + } + else + { + //must be number + numParts.exponent_offset = (ushort)((i + 1) - numParts.integer_at); + goto case NumberPart.ExponentialPart;// + } + } + } + else + { + //this char is not part of the literal number + collectingState = CollectingState.None;//finish + finish = true; + goto EXIT; + } + } + break; + case NumberPart.ExponentialPart: + { + //after exponent part + if (char.IsDigit(c)) + { + //collect more + collectingState = CollectingState.Num_Exponent_NumPart; + exponent_part_count++; + collected_len++; + } + else + { + + //this char is not part of the literal number + collectingState = CollectingState.None;//finish + finish = true; + goto EXIT; + } + } + break; + } + } + + EXIT: + //-------------------- + //summary + numParts.integer_len = (byte)integer_part_count; + numParts.fraction_len = (byte)fraction_part_count; + numParts.exponent_len = (byte)exponent_part_count; + + switch (state) + { + case NumberPart.IntegerPart2: + case NumberPart.IntegerPart: + { + p.CollectedValueHint = numParts.integer_minus ? EsValueHint.NegativeIntegerNumber : EsValueHint.IntegerNumber; + } + break; + case NumberPart.FractionPart: + p.CollectedValueHint = EsValueHint.NumberWithFractionPart; + break; + case NumberPart.ExponentialPart: + p.CollectedValueHint = EsValueHint.NumberWithExponentialPart; + break; + } + p.CollectedNumberParts = numParts; + p._collectingState = collectingState; + + + return finish; + } + static void ReadSingleLineComment(EsParserBase p, int startAt, out int latestIndex) { + char[] sourceBuffer = p._sourceBuffer; latestIndex = startAt; for (; latestIndex < sourceBuffer.Length; ++latestIndex) { @@ -137,8 +702,9 @@ static void ReadSingleLineComment(char[] sourceBuffer, int startAt, ref int late } } } - static void ReadBlockComment(char[] sourceBuffer, int startAt, ref int latestIndex) + static void ReadBlockComment(EsParserBase p, int startAt, out int latestIndex) { + char[] sourceBuffer = p._sourceBuffer; latestIndex = startAt; for (; latestIndex < sourceBuffer.Length; ++latestIndex) { @@ -184,840 +750,539 @@ bool IsSuccess } } + protected char[] _sourceBuffer; + protected EsValueHint CollectedValueHint { get; set; } + protected NumberParts CollectedNumberParts { get; private set; } + readonly Stack _elemKindStack = new Stack(); + ParsingState _curr_state = ParsingState._1_ExpectObjectValueOrArrayElement; + EsElementKind _curr_elemKind = EsElementKind.Unknown; + int _latestIndex = 0; - public virtual void Parse(char[] sourceBuffer) + public void Parse(char[] buff) { - OnParseStart(); - //-------------------------------------------------------------- - EsElementKind currentElementKind = EsElementKind.Unknown; - Stack elemKindStack = new Stack(); - //-------------------------------------------------------------- + TextSourceProvider p = new TextSourceProvider(); + p.Buffer = buff; + p.Len = buff.Length; + Parse(p); + } + public void Reset() + { + _sourceBuffer = null; IsSuccess = true; + _elemKindStack.Clear(); + _curr_state = ParsingState._1_ExpectObjectValueOrArrayElement; + _curr_elemKind = EsElementKind.Unknown; + _elemKindStack.Clear(); + } - StringBuilder myBuffer = new StringBuilder(); - //string lastestKey = ""; - ParsingState currentState = ParsingState._0_Init; - int j = sourceBuffer.Length; + public bool IsFinish() + { + return _elemKindStack.Count == 0; + } + void NotifyErrorAndBreak(ref int read_index) + { + IsSuccess = false; + read_index = _stopBefore + 1; + NotifyError(); + } - bool isInKeyPart = false; - NumberPart numberPart = NumberPart.IntegerPart; + int _stopBefore; - //WARNING: custom version, about ending with comma - //we may use implicit comma feature, - //in case we start new line but forget a comma, - //we auto add comma - //bool implicitComma = false; - char openStringWithChar = '"'; - int i = 0; - ValueHint currentValueHint = ValueHint.Unknown; - for (i = 0; i < j; i++) - { + CollectingState _collectingState; - if (!IsSuccess) - { - OnError(ref i); - //handle the error **** - //#if DEBUG - // if (dbug_EnableLogParser) - // { - // dbugDataFormatParser.IndentLevel = myKeyStack.Count; - // dbugDataFormatParser.WriteLine("fail at pos=" + i + " on " + currentState); - // } - //#endif - break; //break from loop - } + enum CollectingState + { + None, + Char, + Escape, + Escape_U3, + Escape_U2, + Escape_U1, - //-------------------------- - char c = sourceBuffer[i]; + Iden, + + Num_Integer, + Num_Fraction, + Num_Exponent_E, + Num_Exponent_NumPart, + + } +#if DEBUG + public int dbug_charIndex = 0; + public int _start_index = 0; +#endif + void SkipWhitespace(char[] buff, int startAt, out int collected_len) + { + int i = startAt; + int collected = 0; + for (; i < buff.Length; ++i) + { + char c = buff[i]; + if (!char.IsWhiteSpace(c)) + { + //stop here + collected_len = collected; + return; + } + else + { + collected++; + } + } + collected_len = collected; + } + + + public void Parse(TextSourceProvider source) + { + char[] buff = source.Buffer; + int i = source.StartAt; + int len = source.Len; + + int stopBefore = i + len; + _stopBefore = stopBefore; + _sourceBuffer = buff; + + int totalStartOffset = source.TotalOffset; + + + EsElementKind currElemKind = _curr_elemKind;//from latest session + ParsingState currentState = _curr_state; + + //---------- + + //[A] check collecting state from previous session + switch (_collectingState) + { + default: + { + throw new NotSupportedException(); + } + case CollectingState.None: + //nothing from prev session + break; + case CollectingState.Iden: + { + + if (!ReadIdentifier(this, i, out int collected_len)) + { + throw new NotSupportedException();//TODO: review here + } + else + { + + //accept new index + //accept + _collectingState = CollectingState.None;//reset + + //int global_len = totalStartOffset + collected_len; + NewConcatValue(collected_len); + + i += collected_len; + currentState = ParsingState._4_WaitForCommaOrEnd; + } + } + break; + case CollectingState.Num_Integer: + case CollectingState.Num_Fraction: + case CollectingState.Num_Exponent_NumPart: + case CollectingState.Num_Exponent_E: + { + //parse literal number + if (!ReadNumberLiteral(this, i, out int collected_len)) + { + throw new NotSupportedException();//TODO: review here + } + else + { + + _collectingState = CollectingState.None;//reset + + //int global_len = totalStartOffset + collected_len; + //NewValueFromGlobalOffset(_latestIndex + i, global_len - _latestIndex); + NewConcatValue(collected_len); + i += collected_len; + currentState = ParsingState._4_WaitForCommaOrEnd; + } + } + break; + case CollectingState.Escape_U3: + case CollectingState.Escape_U2: + case CollectingState.Escape_U1: + case CollectingState.Escape: + case CollectingState.Char: + { + + if (!ReadStringLiteral2(this, '"', i, out int collected_len)) + { + //not complete + //not acccept latest index + i = stopBefore + 1;//force stop + + //save latest parse state + _curr_elemKind = currElemKind; + _curr_state = currentState; + return;//long string + } + + //accept latest index + _collectingState = CollectingState.None; //reset + + //finish + int global_len = totalStartOffset + collected_len; + switch (currentState) + { + default: + throw new NotSupportedException(); + case ParsingState._1_ExpectObjectValueOrArrayElement: + { + //NewValueFromGlobalOffset(_latestIndex + i, global_len - _latestIndex); + NewConcatValue(collected_len); + i += collected_len; + currentState = ParsingState._4_WaitForCommaOrEnd; + } + break; + case ParsingState._2_ExpectObjectKey: + { + //new key from literal string, not include escape char on start and begin + //NewKeyFromGlobalOffset(_latestIndex + i, global_len - _latestIndex); + NewConcatKey(collected_len); + i += collected_len; + currentState = ParsingState._3_WaitForColon; + } + break; + } + + } + break; + } + + //---------- + + //[B] + for (; i < stopBefore;) + { #if DEBUG - if (dbug_EnableLogParser) + dbug_charIndex++; + if (dbug_charIndex >= 44) { - dbugEsParserLogger.WriteLine(new string('\t', elemKindStack.Count) + i + " ," + c.ToString() + "," + currentState); + + } +#endif + char c = buff[i]; + + if (char.IsWhiteSpace(c)) + { + SkipWhitespace(buff, i + 1, out int collected_len); + i += 1 + collected_len; + continue; + } + else if (c == '/') + { + throw new NotSupportedException(); + + ////extension: comment syntax + //if (i < stopBefore - 1) //has next + //{ + // char next_c = buff[i + 1]; + // if (next_c == '/') + // { + // ReadSingleLineComment(this, i, out latestIndex); + // i = latestIndex; + // continue; + // } + // else if (next_c == '*') + // { + // //inline comment + // ReadBlockComment(this, i, out latestIndex); + // i = latestIndex; + // continue; + // } + // else + // { + // NotifyErrorAndBreak(ref i);//*** + // continue; + // } + //} + //else + //{ + // NotifyErrorAndBreak(ref i);//*** + // continue; + //} } + //----------------------- +#if DEBUG + //System.Diagnostics.Debug.WriteLine(i + ":" + c + ":" + currentState); + //if (i == 10) + //{ + + //} #endif - //-------------------------- + switch (currentState) { - case ParsingState._0_Init: + + case ParsingState._1_ExpectObjectValueOrArrayElement: { switch (c) { case '{': - BeginObject(); - //change current element kind after notification - elemKindStack.Push(currentElementKind); - currentElementKind = EsElementKind.Object; - - myBuffer.Length = 0;//clear - isInKeyPart = true; - currentState = ParsingState._1_ObjectKey; + { + _elemKindStack.Push(currElemKind); + BeginObject(); //event + + currentState = ParsingState._2_ExpectObjectKey; + currElemKind = EsElementKind.Object; + i++; + } break; - case '/': + case '[': { - //----------------------- - //comment syntax - if (i < j - 1) + _elemKindStack.Push(currElemKind); + + BeginArray();//event + currentState = ParsingState._1_ExpectObjectValueOrArrayElement; //on the same state -- value state + currElemKind = EsElementKind.Array; + i++; + } + break; + case ']': + { + if (currElemKind == EsElementKind.Array) { - char next_c = sourceBuffer[i + 1]; - if (next_c == '/') - { - int latestIndex = i + 1; - ReadSingleLineComment(sourceBuffer, latestIndex + 1, ref latestIndex); - i = latestIndex; - } - else if (next_c == '*') + //empty arr + EndArray(); + currElemKind = _elemKindStack.Pop(); + + currentState = ParsingState._4_WaitForCommaOrEnd; + i++; + continue; + } + else + { + NotifyErrorAndBreak(ref i);//*** + continue; + } + } + case '"': //standard + case '\''://extension + { + //TODO: string escape here + if (!ReadStringLiteral(this, c, i + 1, out int collected_len)) + { + //not complete + //not acccept latest index + SaveLatestReadPos(i); + _latestIndex = i + totalStartOffset;//save before exit + i = stopBefore + 1;//force stop + continue; + } + else + { + //accept latest index + _collectingState = CollectingState.None; + collected_len++; + } + + NewValue(i, collected_len); + i += collected_len; + + currentState = ParsingState._4_WaitForCommaOrEnd; + } + break; + default: + { + //iden + if (char.IsLetter(c) || c == '_') + { + //parse as true, false, null + //or other iden + //parse as idenitifer + if (!ReadIdentifier(this, i + 1, out int collected_len)) { - //inline comment - int latestIndex = i + 1; - ReadBlockComment(sourceBuffer, latestIndex + 1, ref latestIndex); - i = latestIndex; + SaveLatestReadPos(i);//*** + _latestIndex = i + totalStartOffset;//save start index + i = stopBefore + 1;//force stop } else { - IsSuccess = false; - NotifyError(); + NewValue(i, collected_len + 1); + i += 1 + collected_len; + currentState = ParsingState._4_WaitForCommaOrEnd; } } - //----------------------- - } - break; - default: - { - if (char.IsWhiteSpace(c)) + else if (char.IsDigit(c) || (c == '-')) { - //same state - continue; + //number + if (!ReadNumberLiteral(this, i, out int collected_len)) + { + SaveLatestReadPos(i);//before + _latestIndex = i + totalStartOffset; + i = stopBefore + 1;//force stop + } + else + { + NewValue(i, collected_len); + i += collected_len; + currentState = ParsingState._4_WaitForCommaOrEnd; + } } else { - IsSuccess = false; - NotifyError(); + NotifyErrorAndBreak(ref i);//*** + continue; } } break; } } break; - case ParsingState._1_ObjectKey: + case ParsingState._2_ExpectObjectKey: { - //TODO: review here again - //in json spec, not support '\"' in keypart - + //literal string + //+ our extension=> iden if (c == '"' || c == '\'') { - //** - openStringWithChar = c; - currentValueHint = ValueHint.StringLiteral; - currentState = ParsingState._2_CollectStringLiteral; - } - else if (char.IsWhiteSpace(c)) - { - continue; - } - else if (c == '}') - { - //this is empty - if (currentElementKind != EsElementKind.Object) + + if (!ReadStringLiteral(this, c, i + 1, out int collected_len)) { - NotifyError(); - IsSuccess = false; + //not complete + //not acccept latest index + SaveLatestReadPos(i); + _latestIndex = i + totalStartOffset; + i = stopBefore + 1;//force stop + continue; } else { -#if DEBUG - if (myBuffer.Length > 0) - { - //error at this state - } -#endif - isInKeyPart = false; - EndObject();//end current object - //1. close current object - //2. pop current object and switch back - //to prev state *** - if (elemKindStack.Count > 0) - { - //switch back - currentElementKind = elemKindStack.Pop(); - } - currentState = ParsingState._6_AfterObjectValueOrArrayElement; + //accept latest index + _collectingState = CollectingState.None; + collected_len++; } + //new key from literal string, not include escape char on start and begin + NewKey(i, collected_len); + i += collected_len; + currentState = ParsingState._3_WaitForColon; } else if (char.IsLetter(c) || c == '_') { - currentValueHint = ValueHint.Identifier; - myBuffer.Append(c); //collect identifier - currentState = ParsingState._8_CollectIdentifier; //collect identifier + throw new NotSupportedException(); + ////extension? + //if (!ReadIdentifier(this, i + 1, out latestIndex)) + //{ + + //} + ////new key from literal string + //NewKey(i, latestIndex - i + 1);//event + //i = latestIndex; + //currentState = ParsingState._3_WaitForColon; } - else if (c == '/') + else if (c == '}') { - //----------------------- - //comment syntax - if (i < j - 1) - { - char next_c = sourceBuffer[i + 1]; - if (next_c == '/') - { - int latestIndex = i + 1; - ReadSingleLineComment(sourceBuffer, latestIndex + 1, ref latestIndex); - i = latestIndex; - } - else if (next_c == '*') - { - //inline comment - int latestIndex = i + 1; - ReadBlockComment(sourceBuffer, latestIndex + 1, ref latestIndex); - i = latestIndex; - } - else - { - IsSuccess = false; - NotifyError(); - } - } - //----------------------- + //no key + //this is empty object + + i++; + EndObject(); + currElemKind = _elemKindStack.Pop(); + currentState = ParsingState._4_WaitForCommaOrEnd; } else { - //extension*** - // - - //number or other token will error in keypart*** - NotifyError(); - IsSuccess = false; - break; + NotifyErrorAndBreak(ref i);//*** + continue; } } break; - case ParsingState._2_CollectStringLiteral: + case ParsingState._3_WaitForColon: { - //collecting string - if (c == '\\') - { - currentState = ParsingState._3_StringEscape; - } - else if (c == openStringWithChar) + if (c == ':') { - //close current string collection - currentValueHint = ValueHint.StringLiteral; - - if (isInKeyPart) - { - NewKey(myBuffer, currentValueHint); - myBuffer.Length = 0;//clear - currentState = ParsingState._4_FinishKeyPartWaitForSemiColon; - } - else - { - NewValue(myBuffer, currentValueHint); - myBuffer.Length = 0;//clear - currentState = ParsingState._6_AfterObjectValueOrArrayElement; + //value of the key - } + i++; + currentState = ParsingState._1_ExpectObjectValueOrArrayElement; } else { - myBuffer.Append(c); - } - } - break; - case ParsingState._3_StringEscape: - { - switch (c) - { - case '"': - { - myBuffer.Append('\"'); - } - break; - case '\'': - { - myBuffer.Append('\''); - } - break; - case '/': - { - myBuffer.Append('/'); - } - break; - case '\\': - { - myBuffer.Append('\\'); - } - break; - case 'b': - { - myBuffer.Append('\b'); - } - break; - case 'f': - { - myBuffer.Append('\f'); - } - break; - case 'r': - { - myBuffer.Append('\r'); - } - break; - case 'n': - { - myBuffer.Append('\n'); - } - break; - case 't': - { - myBuffer.Append('\t'); - } - break; - case 'u': - { - //unicode char in hexa digit - //TODO: review here if we have enough char to parse *** - if (i < j - 4) - { - //json spec - //this follow by 4 chars - //for extension we check if it match with 4 chars or not - uint c_uint = ParseUnicode( - sourceBuffer[i + 1], - sourceBuffer[i + 2], - sourceBuffer[i + 3], - sourceBuffer[i + 4]); - myBuffer.Append((char)c_uint); - i += 4; - } - else - { - //error - IsSuccess = false; - NotifyError(); - } - } - break; - default: - { - NotifyError(); - IsSuccess = false; - } - break; + NotifyErrorAndBreak(ref i);//*** + continue; } - //switch back to state 2_collectStringLiteral - currentState = ParsingState._2_CollectStringLiteral; } break; - case ParsingState._4_FinishKeyPartWaitForSemiColon: + case ParsingState._4_WaitForCommaOrEnd: { - //wait for : - if (c == ':') + //after literal string, literal number, array, object + + + if (c == ',') { - myBuffer.Length = 0;//clear - isInKeyPart = false; - currentState = ParsingState._5_ExpectObjectValueOrArrayElement; //object's value part + Comma(); + if (currElemKind == EsElementKind.Object) + { + currentState = ParsingState._2_ExpectObjectKey; + + } + else if (currElemKind == EsElementKind.Array) + { + currentState = ParsingState._1_ExpectObjectValueOrArrayElement; + + } + else + { + NotifyErrorAndBreak(ref i);//*** + continue; + } + i++; } - else if (char.IsWhiteSpace(c)) + else if (c == '}') { - continue; + + i++; + EndObject(); + currElemKind = _elemKindStack.Pop(); } - else if (c == '/') + else if (c == ']') { - //----------------------- - //comment syntax - if (i < j - 1) - { - char next_c = sourceBuffer[i + 1]; - if (next_c == '/') - { - int latestIndex = i + 1; - ReadSingleLineComment(sourceBuffer, latestIndex + 1, ref latestIndex); - i = latestIndex; - } - else if (next_c == '*') - { - //inline comment - int latestIndex = i + 1; - ReadBlockComment(sourceBuffer, latestIndex + 1, ref latestIndex); - i = latestIndex; - } - else - { - IsSuccess = false; - NotifyError(); - } - } - //----------------------- + + i++; + EndArray(); + currElemKind = _elemKindStack.Pop(); } else { - //TODO: add recovery extension here - NotifyError(); - IsSuccess = false; - break; + NotifyErrorAndBreak(ref i);//*** + continue; } } break; - case ParsingState._5_ExpectObjectValueOrArrayElement: - { - //in value part *** - //of object or array + } + } - if (c == '"' || c == '\'') - { - //TODO: string escape here - openStringWithChar = c; - //string val - currentState = ParsingState._2_CollectStringLiteral; - } - else if (char.IsDigit(c) || (c == '-')) - { - //TODO: - //support extension + - myBuffer.Append(c); - //number - currentValueHint = ValueHint.IntegerNumber; - numberPart = NumberPart.IntegerPart; - currentState = ParsingState._7_CollectNumberLiteral; - } - else if (c == '{') - { - //store current object in stack - BeginObject(); - elemKindStack.Push(currentElementKind); - currentElementKind = EsElementKind.Object; - isInKeyPart = true; - currentState = ParsingState._1_ObjectKey; - } - else if (c == '[') - { - BeginArray(); - elemKindStack.Push(currentElementKind); - currentElementKind = EsElementKind.Array; - isInKeyPart = false; - currentState = ParsingState._5_ExpectObjectValueOrArrayElement; //on the same state -- value state - } - else if (c == ']') - { - if (currentElementKind != EsElementKind.Array) - { - NotifyError(); - IsSuccess = false; - } - else - { - EndArray();//end current array - - if (elemKindStack.Count > 0) - { - - currentElementKind = elemKindStack.Pop(); - } - } - currentState = ParsingState._6_AfterObjectValueOrArrayElement; - } - else if (c == '/') - { - //----------------------- - //comment syntax - if (i < j - 1) - { - char next_c = sourceBuffer[i + 1]; - if (next_c == '/') - { - int latestIndex = i + 1; - ReadSingleLineComment(sourceBuffer, latestIndex + 1, ref latestIndex); - i = latestIndex; - } - else if (next_c == '*') - { - //inline comment - int latestIndex = i + 1; - ReadBlockComment(sourceBuffer, latestIndex + 1, ref latestIndex); - i = latestIndex; - } - else - { - IsSuccess = false; - NotifyError(); - } - } - //----------------------- - } - else if (char.IsWhiteSpace(c)) - { - continue; - } - else - { - //we collect other character into buffer - //so we can collect - //null, true, false - //or other identifier *** - currentState = ParsingState._8_CollectIdentifier; - myBuffer.Append(c); - } - } - break; - case ParsingState._6_AfterObjectValueOrArrayElement: - { - switch (c) - { - case ',': - switch (currentElementKind) - { - default: throw new NotSupportedException(); - case EsElementKind.Object: - currentState = ParsingState._1_ObjectKey; - isInKeyPart = true; - break; - case EsElementKind.Array: - //array - currentState = ParsingState._5_ExpectObjectValueOrArrayElement; - break; - } - break; - case ']': - if (currentElementKind != EsElementKind.Array) - { - //error - throw new NotSupportedException(); - } - EndArray(); - - //close current array - //then push value back to prev stored value - if (elemKindStack.Count > 0) - { - - //current value must be array - currentElementKind = elemKindStack.Pop(); - } - break; - case '}': - if (currentElementKind != EsElementKind.Object) - { - //error - throw new NotSupportedException(); - } - EndObject(); - - if (elemKindStack.Count > 0) - { - currentElementKind = elemKindStack.Pop(); - } - currentState = ParsingState._6_AfterObjectValueOrArrayElement; - break; - case '/': - { - //----------------------- - //comment syntax - if (i < j - 1) - { - char next_c = sourceBuffer[i + 1]; - if (next_c == '/') - { - int latestIndex = i + 1; - ReadSingleLineComment(sourceBuffer, latestIndex + 1, ref latestIndex); - i = latestIndex; - } - else if (next_c == '*') - { - //inline comment - int latestIndex = i + 1; - ReadBlockComment(sourceBuffer, latestIndex + 1, ref latestIndex); - i = latestIndex; - } - else - { - IsSuccess = false; - NotifyError(); - } - } - //----------------------- - } - break; - default: - //? - //TODO: error recovery / or handle error with some extension - break; - } - } - break; - case ParsingState._7_CollectNumberLiteral: - { - //------------------------------------------------------ - //TODO: review pass sign state of number literal - //check if we support hex liternal or binary literal - //this is extension to normal json *** - //------------------------------------------------------ + //*** + //save latest parse state + _curr_elemKind = currElemKind; + _curr_state = currentState; - if (char.IsDigit(c)) - { - myBuffer.Append(c); - } - else if (c == '.') - { - if (numberPart == NumberPart.IntegerPart) - { - myBuffer.Append(c); - numberPart = NumberPart.FractionPart; - currentValueHint = ValueHint.NumberWithFractionPart; - } - else - { - NotifyError(); - IsSuccess = false; - break; - } - } - else if (c == 'e' || c == 'E') - { - myBuffer.Append(c); - switch (numberPart) - { - case NumberPart.IntegerPart: - numberPart = NumberPart.E; - currentValueHint = ValueHint.NumberWithExponentialPart; - break; - case NumberPart.FractionPart: - numberPart = NumberPart.E; - currentValueHint = ValueHint.NumberWithExponentialPart; - break; - default: - NotifyError(); - IsSuccess = false; - break; - } - } - else if (c == '-' || c == '+') - { - myBuffer.Append(c); - switch (numberPart) - { - case NumberPart.E://after e - numberPart = NumberPart.ESign; - break; - default: - NotifyError(); - IsSuccess = false; - break; - } - } - else if (c == ']') - { - NewValue(myBuffer, currentValueHint); - //-------------------------- - myBuffer.Length = 0; //clear - EndArray(); - - if (elemKindStack.Count > 0) - { - - currentElementKind = elemKindStack.Pop(); - } - currentState = ParsingState._6_AfterObjectValueOrArrayElement; - } - else if (c == '}') - { - NewValue(myBuffer, currentValueHint); - myBuffer.Length = 0; //clear - EndObject(); - - if (elemKindStack.Count > 0) - { - currentElementKind = elemKindStack.Pop(); - } - currentState = ParsingState._6_AfterObjectValueOrArrayElement; - } - else if (c == ',') - { - NewValue(myBuffer, currentValueHint); - //clear - myBuffer.Length = 0; - switch (currentElementKind) - { - default: throw new NotSupportedException(); - case EsElementKind.Array: - isInKeyPart = false; - currentState = ParsingState._5_ExpectObjectValueOrArrayElement; - break; - case EsElementKind.Object: - isInKeyPart = true; - currentState = ParsingState._1_ObjectKey; - break; - } - } - else if (c == '\r') - { - //stop here - if (i < j - 1) - { - if (sourceBuffer[i + 1] == '\n') - { - //\r\n - i++; - - NewValue(myBuffer, currentValueHint); - //clear - myBuffer.Length = 0; - - switch (currentElementKind) - { - default: throw new NotSupportedException(); - case EsElementKind.Array: - isInKeyPart = false; - currentState = ParsingState._5_ExpectObjectValueOrArrayElement; - break; - case EsElementKind.Object: - isInKeyPart = true; - currentState = ParsingState._1_ObjectKey; - break; - } - } - else - { - //only R - } - } - } - else if (c == '\n') - { - //stop - NewValue(myBuffer, currentValueHint); - //clear - myBuffer.Length = 0; - - switch (currentElementKind) - { - default: throw new NotSupportedException(); - case EsElementKind.Array: - isInKeyPart = false; - currentState = ParsingState._5_ExpectObjectValueOrArrayElement; - break; - case EsElementKind.Object: - isInKeyPart = true; - currentState = ParsingState._1_ObjectKey; - break; - } - } - else - { - - IsSuccess = false; - NotifyError(); - } - } - break; - case ParsingState._8_CollectIdentifier: - { - - currentValueHint = ValueHint.Identifier; - switch (c) - { - case ':': - //stop collect identifier and - if (isInKeyPart) - { - NewKey(myBuffer, currentValueHint); - myBuffer.Length = 0;//clear - currentState = ParsingState._5_ExpectObjectValueOrArrayElement; //object's value part - isInKeyPart = false; - } - else - { - NotifyError(); - IsSuccess = false; - } - break; - case '}': - if (isInKeyPart) - { - NotifyError(); - IsSuccess = false; - } - else - { - NewValue(myBuffer, currentValueHint); - myBuffer.Length = 0; //clear - EndObject(); - - if (elemKindStack.Count > 0) - { - currentElementKind = elemKindStack.Pop(); - } - currentState = ParsingState._6_AfterObjectValueOrArrayElement; - } - break; - case ']': - if (isInKeyPart) - { - NotifyError(); - IsSuccess = false; - } - else - { - NewValue(myBuffer, currentValueHint); - myBuffer.Length = 0; //clear - EndArray(); - - if (elemKindStack.Count > 0) - { - - currentElementKind = elemKindStack.Pop(); - } - currentState = ParsingState._6_AfterObjectValueOrArrayElement; - } - break; - case ',': - if (isInKeyPart) - { - NotifyError(); - IsSuccess = false; - } - else - { - NewValue(myBuffer, currentValueHint); - myBuffer.Length = 0; //clear - switch (currentElementKind) - { - default: throw new NotSupportedException(); - case EsElementKind.Array: - isInKeyPart = false; - currentState = ParsingState._5_ExpectObjectValueOrArrayElement; - break; - case EsElementKind.Object: - isInKeyPart = true; - currentState = ParsingState._1_ObjectKey; - break; - } - } - break; - default: - if (char.IsWhiteSpace(c)) - { - //stop collect identifier*** - //wait for : - currentState = ParsingState._4_FinishKeyPartWaitForSemiColon; - } - else - { - myBuffer.Append(c); - } - break; - } - } - break; - } + //check if finish or not + if (_elemKindStack.Count > 0) + { + //document is not complete } - OnParseEnd(); + //if (latestIndex != stopBefore - 1) + //{ + //} } + //public int LatestIndex => _latestIndex; - static uint ParseSingleChar(char c1) + + static uint ParseHex(char c1) { switch (c1) { @@ -1040,184 +1305,871 @@ static uint ParseSingleChar(char c1) default: return 0; } } - static uint ParseUnicode(char c1, char c2, char c3, char c4) + static uint ParseUnicode(char c1, char c2, char c3, char c4) + { + return (ParseHex(c1) << 12) | + (ParseHex(c2) << 8) | + (ParseHex(c3) << 4) | + (ParseHex(c4) << 0); + } + } + + + class StringDic + { + Dictionary _keyDic = new Dictionary(); + List _keyList = new List(); + + public StringDic() + { + Register(new char[0], 0, 0);//empty string + } + public int Register(char[] buffer, int start, int len) + { + //calculate has for specific region + ulong hash_value = CalculateHash(buffer, start, len); + if (!_keyDic.TryGetValue(hash_value, out int index)) + { + index = _keyList.Count;//** + _keyDic.Add(hash_value, _keyDic.Count); + _keyList.Add(new string(buffer, start, len)); + } + return index; + } + public int Count => _keyList.Count; + public string GetKey(int index) => _keyList[index]; + static ulong CalculateHash(char[] buffer, int start, int len) + { + //https://stackoverflow.com/questions/9545619/a-fast-hash-function-for-string-in-c-sharp + ulong hashedValue = 3074457345618258791ul; + int end = start + len; + for (int i = start; i < end; i++) + { + hashedValue += buffer[i]; + hashedValue *= 3074457345618258799ul; + } + return hashedValue; + } + } + + public abstract class EsParserBase : EsParserBase + where E : class + where A : class + { + struct CurrentObject + { + public E elem; + public A arr; + public bool isArr; + } + + readonly Stack _keyStack = new Stack(); + readonly Stack _elemStack = new Stack(); + + StringDic _keyDic = new StringDic(); + bool _emptyKey = true; + + int _currentKey; + //--- + + + bool _emptyCurrentValue = true; + CurrentObject _currValue; + + + + public EsParserBase() + { + + } + protected abstract E CreateElement(); + protected abstract A CreateArray(); + + protected abstract void AddElementAttribute(E targetElem, int key, A value); + protected abstract void AddElementAttribute(E targetElem, int key, E value); + protected abstract void AddElementAttribute(E targetElem, int key, EsValueHint valueHint); + + protected abstract void AddArrayElement(A targetArray, A value); + protected abstract void AddArrayElement(A targetArray, E value); + protected abstract void AddArrayElement(A targetArray, EsValueHint valueHint); + + protected override void OnParseStart() + { + + } + + protected int GetPrevKeyIndex() + { + if (_keyStack.Count < 1) + { + return -1; + } + else + { + return _keyStack.Peek(); + } + } + protected int CurrentKeyIndex => _currentKey; + + protected override void BeginObject() + { + if (!_emptyKey) + { + //copy old key + + _keyStack.Push(_currentKey); + } + _emptyKey = true; + + if (!_emptyCurrentValue) + { + _elemStack.Push(_currValue); + } + _currValue = new CurrentObject(); + _currValue.elem = CreateElement(); + _emptyCurrentValue = false; + } + void InternalPopCurrentObjectAndPushToPrevContext() + { + //current element should be object + CurrentObject c_object = _currValue; + if (_elemStack.Count > 0) + { + //pop from stack + _currValue = _elemStack.Pop(); + _emptyKey = true; + + if (!_currValue.isArr) + { + if (_keyStack.Count > 0) + { + _currentKey = _keyStack.Pop(); + if (c_object.isArr) + { + AddElementAttribute(_currValue.elem, _currentKey, c_object.arr); + } + else + { + AddElementAttribute(_currValue.elem, _currentKey, c_object.elem); + } + } + else + { + //? + } + } + else + { + if (c_object.isArr) + { + AddArrayElement(_currValue.arr, c_object.arr); + } + else + { + AddArrayElement(_currValue.arr, c_object.elem); + } + + } + } + } + protected override void EndObject() + { + InternalPopCurrentObjectAndPushToPrevContext(); + } + protected override void BeginArray() + { + if (!_emptyKey) + { + _keyStack.Push(_currentKey); + } + _emptyKey = true; + + + if (!_emptyCurrentValue) + { + _elemStack.Push(_currValue); + } + + _currValue = new CurrentObject(); + _currValue.arr = CreateArray(); + _currValue.isArr = true; + _emptyCurrentValue = false; + } + protected override void EndArray() + { + InternalPopCurrentObjectAndPushToPrevContext(); + } + protected override void OnParseEnd() + { + + } + + public string GetKeyAsStringByIndex(int index) => _keyDic.GetKey(index); + + + const int KEY_BUFFER_SIZE = 1024; + TempSavedBuffer _keyBuffer = new TempSavedBuffer(new char[KEY_BUFFER_SIZE], 0); + + protected int RegisterKey(string key) + { + char[] buffer = key.ToCharArray(); + return _keyDic.Register(buffer, 0, buffer.Length); + } + protected override void NewKey(int start, int len) + { + //implement key + //key trend + _emptyKey = false; + + _currentKey = _keyDic.Register(_sourceBuffer, start + 1, len - 2); + + if (GetKeyAsStringByIndex(_currentKey) == "results") + { + + } + + } + protected override void NewConcatKey(int len) + { + //create new key + //in this version we handle a short key + //that can be fill inside 1 buffer + + if (_tmpSavedBufferList.Count > 1) + { + throw new NotSupportedException(); + } + + int dstPos = 0; + for (int i = 0; i < _tmpSavedBufferList.Count; ++i) + { + TempSavedBuffer tmpBuffer = _tmpSavedBufferList[i]; + Array.Copy(tmpBuffer.buffer, 0, _keyBuffer.buffer, dstPos, tmpBuffer.len); + dstPos += tmpBuffer.len; + ReleaseFreeTempBuffer(tmpBuffer); + } + _tmpSavedBufferList.Clear(); + + if (len > 0) + { + Array.Copy(_sourceBuffer, 0, _keyBuffer.buffer, dstPos, len); + dstPos += len; + } + + _emptyKey = false; + _currentKey = _keyDic.Register(_keyBuffer.buffer, 1, dstPos - 2); + _concat_value_len = 0; + } + + struct TempSavedBuffer + { + public char[] buffer; + public int len; + public TempSavedBuffer(char[] buffer, int len) + { + this.buffer = buffer; + this.len = len; + } +#if DEBUG + public override string ToString() + { + return len.ToString(); + } +#endif + } + + Stack _pool = new Stack(); + TempSavedBuffer GetFreeTempBuffer(int size) + { + if (_pool.Count == 0) + { + return new TempSavedBuffer(new char[size], 0); + } + else + { + return _pool.Pop(); + } + } + void ReleaseFreeTempBuffer(TempSavedBuffer tmpBuffer) { - return (ParseSingleChar(c1) << 16) | - (ParseSingleChar(c2) << 8) | - (ParseSingleChar(c3) << 4) | - (ParseSingleChar(c4) << 0); + _pool.Push(tmpBuffer); } +#if DEBUG + int _count = 0; +#endif - } + List _tmpSavedBufferList = new List(); + int _concat_value_len = 0; + protected override void SaveLatestReadPos(int startAt) + { +#if DEBUG + _count++; + //backup current buffer +#endif + int copy_len = _sourceBuffer.Length - startAt; + //then select proper buffer + if (startAt == 0) + { - public abstract class EsParserBase : EsParserBase - where E : class - where A : class - { - enum CurrentObject - { - Object, - Array + } + + TempSavedBuffer tmpBuffer = GetFreeTempBuffer(_sourceBuffer.Length); + //temp copy data to here //reuse buffer? + Array.Copy(_sourceBuffer, startAt, tmpBuffer.buffer, 0, copy_len); + tmpBuffer.len = copy_len; + _tmpSavedBufferList.Add(tmpBuffer); + _concat_value_len += copy_len; + base.SaveLatestReadPos(startAt); } - Stack _keyStack = new Stack(); - Stack _elemStack = new Stack(); - object _currentElem = null; - string _currentKey = null; + static int ParseInt32(char[] buffer, int start, int len) + { - public EsParserBase() + int result = 0; + int i = start; + int sign = 1; + for (int n = 0; n < len; ++n) + { + char c = buffer[i]; + i++; + result *= 10; + + switch (c) + { + case '-': + sign = -1; + break; + case '0': + break; + case '1': + result += 1; + break; + case '2': + result += 2; + break; + case '3': + result += 3; + break; + case '4': + result += 4; + break; + case '5': + result += 5; + break; + case '6': + result += 6; + break; + case '7': + result += 7; + break; + case '8': + result += 8; + break; + case '9': + result += 9; + break; + case '.': + throw new NotSupportedException(); + break; + } + } + return result * sign; + } + static long ParseInt64(char[] buffer, int start, int len) { + long result = 0; + bool negative = false; + int i = start; + + for (int n = 0; n < len; ++n) + { + char c = buffer[i]; + i++; + result *= 10L; + + switch (c) + { + case '-': + negative = true; + break; + case '0': + break; + case '1': + result += 1; + break; + case '2': + result += 2; + break; + case '3': + result += 3; + break; + case '4': + result += 4; + break; + case '5': + result += 5; + break; + case '6': + result += 6; + break; + case '7': + result += 7; + break; + case '8': + result += 8; + break; + case '9': + result += 9; + break; + case '.': + throw new NotSupportedException(); + break; + } + } + if (negative) + { + return -1L * result; + } + else + { + return result; + } } - protected abstract E CreateElement(); - protected abstract A CreateArray(); - protected abstract void AddElementAttribute(E targetElem, string key, object value); - protected abstract void AddArrayElement(A targetArray, object value); - protected override void OnParseStart() + protected int GetValueAsInt32() { + if (_isConcatValue) + { + TempSavedBuffer temp_buffer2 = GetFreeTempBuffer(_sourceBuffer.Length); + char[] bb = temp_buffer2.buffer; + int totalLen = ConcatSmallValue(bb, _local_value_len); + int result = ParseInt32(bb, 0, totalLen); + ReleaseFreeTempBuffer(temp_buffer2); + return result; + } + else + { + //only current value + if (_local_value_len > 10) + { + //may be long/ulong + throw new NotSupportedException(); + } + else + { + return ParseInt32(_sourceBuffer, _value_start, _local_value_len); + } + } + return 0; } - protected override void BeginObject() + protected long GetValueAsInt64() { - if (_currentKey != null) + if (_isConcatValue) { - _keyStack.Push(_currentKey); + TempSavedBuffer temp_buffer2 = GetFreeTempBuffer(_sourceBuffer.Length); + char[] bb = temp_buffer2.buffer; + int totalLen = ConcatSmallValue(bb, _local_value_len); + long result = ParseInt64(bb, 0, totalLen); + ReleaseFreeTempBuffer(temp_buffer2); + return result; } - _currentKey = null; - if (_currentElem != null) + else { - _elemStack.Push(_currentElem); + //only current value + return ParseInt64(_sourceBuffer, _value_start, _local_value_len); } - _currentElem = CreateElement(); } - void InternalPopCurrentObjectAndPushToPrevContext() + + StringBuilder _sb = new StringBuilder(); + + static void AppendStringWithSomeEscape(StringBuilder sb, char[] source, int start, int len) { - //current element should be object - object c_object = _currentElem; - if (_elemStack.Count > 0) + int i = start; + + for (int n = 0; n < len; ++n) { - //pop from stack - _currentElem = _elemStack.Pop(); - _currentKey = null; - if (c_object == _currentElem) + char c = source[i]; + i++; + if (c == '\\') { - throw new System.Exception(); - } + //escape + char c2 = source[i]; + n++; + i++; + switch (c2) + { + default: - E c_elem = null; - A c_arr = null; - if ((c_elem = _currentElem as E) != null) - { - _currentKey = _keyStack.Pop(); - AddElementAttribute(c_elem, _currentKey, c_object); - } - else if ((c_arr = _currentElem as A) != null) - { - AddArrayElement(c_arr, c_object); + break; + case '"': + sb.Append('"'); + break; + case 'u': + { + + } + break; + case '\\': + sb.Append('\\'); + break; + case 'f': + sb.Append('\f'); + break; + case '/': + sb.Append('/');//? + break; + case 'b': + sb.Append('\b'); + break; + case 't': + sb.Append('\t'); + break; + case 'r': + sb.Append('\r'); + break; + case 'n': + sb.Append('\n'); + break; + } } else { - throw new System.NotSupportedException(); + sb.Append(c); } } } - protected override void EndObject() + protected string GetValueAsStringWithEscape() { - InternalPopCurrentObjectAndPushToPrevContext(); + if (_isConcatValue) + { + //need to merge + _sb.Length = 0;//clear + for (int i = 0; i < _tmpSavedBufferList.Count; ++i) + { + TempSavedBuffer bb = _tmpSavedBufferList[i]; + if (i == 0) + { + AppendStringWithSomeEscape(_sb, bb.buffer, 1, bb.len - 1); + } + else + { + AppendStringWithSomeEscape(_sb, bb.buffer, 0, bb.len); + } + } + if (_local_value_len > 1) + { + _sb.Append(_sourceBuffer, 0, _local_value_len - 1); + } + return _sb.ToString(); + } + else + { + //not a concat value + _sb.Length = 0;//clear + AppendStringWithSomeEscape(_sb, _sourceBuffer, _value_start + 1, _local_value_len - 2); + return _sb.ToString(); + } } - protected override void BeginArray() + protected string GetValueAsString() { - if (_currentKey != null) + if (_isConcatValue) { - _keyStack.Push(_currentKey); + //need to merge + _sb.Length = 0;//clear + for (int i = 0; i < _tmpSavedBufferList.Count; ++i) + { + TempSavedBuffer bb = _tmpSavedBufferList[i]; + if (i == 0) + { + _sb.Append(bb.buffer, 1, bb.len - 1); + } + else + { + _sb.Append(bb.buffer, 0, bb.len); + } + } + if (_local_value_len > 1) + { + _sb.Append(_sourceBuffer, 0, _local_value_len - 1); + } + return _sb.ToString(); } - _currentKey = null; - if (_currentElem != null) + else { - _elemStack.Push(_currentElem); + //not a concat value + if (_local_value_len < 2) + { + + } + return new string(_sourceBuffer, _value_start + 1, _local_value_len - 2); } - _currentElem = CreateArray(); } - protected override void EndArray() + protected double GetValueAsDouble() { - InternalPopCurrentObjectAndPushToPrevContext(); + + if (_isConcatValue) + { + TempSavedBuffer temp_buffer2 = GetFreeTempBuffer(_sourceBuffer.Length); + char[] bb = temp_buffer2.buffer; + int totalLen = ConcatSmallValue(bb, _local_value_len); + string dd = new string(bb, 0, totalLen); + //int result = ParseInt32(bb, 0, totalLen); + ReleaseFreeTempBuffer(temp_buffer2); + //return result; + return double.Parse(dd); + } + else + { + string dd = new string(_sourceBuffer, _value_start, _local_value_len); + return double.Parse(dd); + } } - protected override void OnParseEnd() + protected void ReadValueAsByteBuffer(System.IO.StreamWriter w) { + //apply to string value only } - protected override void NewKey(StringBuilder tmpBuffer, ValueHint valueHint) + + int _value_start; + int _local_value_len; + bool _isConcatValue; + + protected int ConcatValueLen => _concat_value_len; + int ConcatSmallValue(char[] tmp_buffer, int localLen) { - _currentKey = tmpBuffer.ToString(); + int dstPos = 0; + for (int i = 0; i < _tmpSavedBufferList.Count; ++i) + { + TempSavedBuffer tmpBuffer = _tmpSavedBufferList[i]; + Array.Copy(tmpBuffer.buffer, 0, tmp_buffer, dstPos, tmpBuffer.len); + dstPos += tmpBuffer.len; + } + + if (localLen > 0) + { + Array.Copy(_sourceBuffer, 0, tmp_buffer, dstPos, localLen); + dstPos += localLen; + } + return dstPos; } - protected override void NewValue(StringBuilder tmpBuffer, ValueHint valueHint) + protected override void NewConcatValue(int len) { - object c_object = null; - switch (valueHint) + _isConcatValue = true; + _value_start = 0; + _concat_value_len += len; + _local_value_len = len; + + switch (CollectedValueHint) { - default: - case ValueHint.Comment: - throw new System.NotSupportedException(); - case ValueHint.Identifier: - string iden = tmpBuffer.ToString(); - switch (iden) + case EsValueHint.IntegerNumber: { - case "true": - c_object = true; - break; - case "false": - c_object = false; - break; - case "null": - c_object = null; - break; - default: - c_object = iden; - break; +#if DEBUG + if (_concat_value_len >= 9) + { + //hint to long + TempSavedBuffer temp_buffer2 = GetFreeTempBuffer(_sourceBuffer.Length); + char[] bb = temp_buffer2.buffer; + int int_len = ConcatSmallValue(bb, len); + ReleaseFreeTempBuffer(temp_buffer2); + } +#endif + } break; - case ValueHint.StringLiteral: - c_object = tmpBuffer.ToString(); - break; - case ValueHint.IntegerNumber: - c_object = int.Parse(tmpBuffer.ToString()); - break; - case ValueHint.NumberWithFractionPart: - case ValueHint.NumberWithSignedExponentialPart: - case ValueHint.NumberWithExponentialPart: - c_object = double.Parse(tmpBuffer.ToString()); + case EsValueHint.Identifier: + { + //special + switch (_concat_value_len) + { + default: + { + //TempSavedBuffer temp_buffer2 = GetFreeTempBuffer(_sourceBuffer.Length); + //char[] bb = temp_buffer2.buffer; + //ConcatSmallValue(bb, len); + //ReleaseFreeTempBuffer(temp_buffer2); + } + break; + case 4://true //null + { + + TempSavedBuffer temp_buffer2 = GetFreeTempBuffer(_sourceBuffer.Length); + char[] bb = temp_buffer2.buffer; + + ConcatSmallValue(bb, len); + if (bb[0] == 'n' && + bb[0 + 1] == 'u' && + bb[0 + 2] == 'l' && + bb[0 + 3] == 'l') + { + CollectedValueHint = EsValueHint.Null; + } + else if (bb[0] == 't' && + bb[0 + 1] == 'r' && + bb[0 + 2] == 'u' && + bb[0 + 3] == 'e') + { + CollectedValueHint = EsValueHint.True; + } + else + { + + } + + ReleaseFreeTempBuffer(temp_buffer2); + } + break; + case 5: + { + + TempSavedBuffer temp_buffer2 = GetFreeTempBuffer(_sourceBuffer.Length); + char[] bb = temp_buffer2.buffer; + ConcatSmallValue(bb, len); + + if (bb[0] == 'f' && + bb[0 + 1] == 'a' && + bb[0 + 2] == 'l' && + bb[0 + 3] == 's' && + bb[0 + 4] == 'e') + { + CollectedValueHint = EsValueHint.False; + } + else + { + + } + + ReleaseFreeTempBuffer(temp_buffer2); + } + + break; + } + } break; + } + + + if (!_currValue.isArr) + { + AddElementAttribute(_currValue.elem, _currentKey, CollectedValueHint); + } + else + { + AddArrayElement(_currValue.arr, CollectedValueHint); + } + //clear buffer + for (int i = 0; i < _tmpSavedBufferList.Count; ++i) + { + ReleaseFreeTempBuffer(_tmpSavedBufferList[i]); } - E c_elem = null; - A c_arr = null; - if ((c_elem = _currentElem as E) != null) + _concat_value_len = 0; + _tmpSavedBufferList.Clear(); + } + protected override void NewValue(int start, int len) + { + _isConcatValue = false; + _value_start = start; + _local_value_len = _concat_value_len = len; +#if DEBUG + if (CollectedValueHint == EsValueHint.StringLiteral && _local_value_len < 2) + { + + } +#endif + if (CollectedValueHint == EsValueHint.Identifier) { - AddElementAttribute(c_elem, _currentKey, c_object); + //special + switch (len) + { + default: + { + + } + break; + case 3: + { +#if DEBUG + char c0 = _sourceBuffer[start]; + char c1 = _sourceBuffer[start + 1]; + char c2 = _sourceBuffer[start + 2]; +#endif + + } + break; + case 4://true //null + if (_sourceBuffer[start] == 'n' && + _sourceBuffer[start + 1] == 'u' && + _sourceBuffer[start + 2] == 'l' && + _sourceBuffer[start + 3] == 'l') + { + CollectedValueHint = EsValueHint.Null; + } + else if (_sourceBuffer[start] == 't' && + _sourceBuffer[start + 1] == 'r' && + _sourceBuffer[start + 2] == 'u' && + _sourceBuffer[start + 3] == 'e') + { + CollectedValueHint = EsValueHint.True; + } + else + { + + } + break; + case 5: + //false + if (_sourceBuffer[start] == 'f' && + _sourceBuffer[start + 1] == 'a' && + _sourceBuffer[start + 2] == 'l' && + _sourceBuffer[start + 3] == 's' && + _sourceBuffer[start + 4] == 'e') + { + CollectedValueHint = EsValueHint.False; + } + else + { + + } + break; + } } - else if ((c_arr = _currentElem as A) != null) + + if (!_currValue.isArr) { - AddArrayElement(c_arr, c_object); + AddElementAttribute(_currValue.elem, _currentKey, CollectedValueHint); } else { - throw new System.NotSupportedException(); + AddArrayElement(_currValue.arr, CollectedValueHint); } + _concat_value_len = 0;//reset } + protected override void NotifyError() { base.NotifyError(); } - protected override void OnError(ref int currentIndex) + + public object CurrentValue { - base.OnError(ref currentIndex); + get + { + if (_currValue.isArr) + { + return _currValue.arr; + } + else + { + return _currValue.elem; + } + } } - public object CurrentElement => _currentElem; } } \ No newline at end of file diff --git a/src/SharpConnect.Data/TreeModel/EsUniqueStringTable.cs b/src/SharpConnect.Data/TreeModel/EsUniqueStringTable.cs index fdd7053..87be21f 100644 --- a/src/SharpConnect.Data/TreeModel/EsUniqueStringTable.cs +++ b/src/SharpConnect.Data/TreeModel/EsUniqueStringTable.cs @@ -25,15 +25,12 @@ public int GetStringIndex(string str) { return 0; } - int foundIndex; - if (_dic.TryGetValue(str, out foundIndex)) - { - return foundIndex; - } - else + + if (!_dic.TryGetValue(str, out int foundIndex)) { - return -1; + foundIndex = -1; } + return foundIndex; } public int AddStringIfNotExist(string str) @@ -43,9 +40,8 @@ public int AddStringIfNotExist(string str) { return 0; } - //--------------------------------------- - int foundIndex; - if (_dic.TryGetValue(str, out foundIndex)) + + if (_dic.TryGetValue(str, out int foundIndex)) { return foundIndex; } @@ -64,17 +60,6 @@ public int AddStringIfNotExist(string str) public string GetString(int index) => _list[index]; - public IEnumerable WordIter - { - get - { - foreach (string str in _dic.Keys) - { - yield return str; - } - } - } - public List GetStringList() => _list; public EsUniqueStringTable Clone()