Merge pull request #187 from dwango/optimize_ListTreeNode_Children

optimize ListTreeNode Children for large JSON
This commit is contained in:
ousttrue 2019-02-28 03:14:13 +09:00 committed by GitHub
commit 04b1acab9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 154 additions and 96 deletions

View File

@ -20,5 +20,7 @@ namespace UniJSON
public interface IListTreeItem
{
int ParentIndex { get; }
int ChildCount { get; }
void SetChildCount(int count);
}
}

View File

@ -24,6 +24,7 @@ namespace UniJSON
T Key(Utf8String key, int parentIndex);
ValueNodeType ValueType { get; }
ArraySegment<Byte> Bytes { get; }
void SetBytesCount(int count);
Boolean GetBoolean();
String GetString();
Utf8String GetUtf8String();

View File

@ -1,14 +1,8 @@
using System;
using System.Collections.Generic;
namespace UniJSON
{
public class JsonParseResult
{
public List<JsonValue> Values = new List<JsonValue>();
}
public static class JsonParser
{
static ValueNodeType GetValueType(Utf8String segment)
@ -21,7 +15,7 @@ namespace UniJSON
case 't': return ValueNodeType.Boolean;
case 'f': return ValueNodeType.Boolean;
case 'n':
if (segment.ByteLength >= 2 && Char.ToLower((char) segment[1]) == 'a')
if (segment.ByteLength >= 2 && Char.ToLower((char)segment[1]) == 'a')
{
return ValueNodeType.NaN;
}
@ -32,7 +26,7 @@ namespace UniJSON
return ValueNodeType.Infinity;
case '-':
if (segment.ByteLength >= 2 && Char.ToLower((char) segment[1]) == 'i')
if (segment.ByteLength >= 2 && Char.ToLower((char)segment[1]) == 'i')
{
return ValueNodeType.MinusInfinity;
}
@ -70,7 +64,7 @@ namespace UniJSON
/// <param name="valueType"></param>
/// <param name="parentIndex"></param>
/// <returns></returns>
static JsonValue ParsePrimitive(Utf8String segment, ValueNodeType valueType, int parentIndex)
static ListTreeNode<JsonValue> ParsePrimitive(ListTreeNode<JsonValue> tree, Utf8String segment, ValueNodeType valueType)
{
int i = 1;
for (; i < segment.ByteLength; ++i)
@ -85,15 +79,15 @@ namespace UniJSON
break;
}
}
return new JsonValue(segment.Subbytes(0, i), valueType, parentIndex);
return tree.AddValue(segment.Subbytes(0, i).Bytes, valueType);
}
static JsonValue ParseString(Utf8String segment, int parentIndex)
static ListTreeNode<JsonValue> ParseString(ListTreeNode<JsonValue> tree, Utf8String segment)
{
int pos;
if (segment.TrySearchAscii((Byte)'"', 1, out pos))
{
return new JsonValue(segment.Subbytes(0, pos + 1), ValueNodeType.String, parentIndex);
return tree.AddValue(segment.Subbytes(0, pos + 1).Bytes, ValueNodeType.String);
}
else
{
@ -101,8 +95,10 @@ namespace UniJSON
}
}
static Utf8String ParseArray(Utf8String segment, List<JsonValue> values, int parentIndex)
static ListTreeNode<JsonValue> ParseArray(ListTreeNode<JsonValue> tree, Utf8String segment)
{
var array = tree.AddValue(segment.Bytes, ValueNodeType.Array);
var closeChar = ']';
bool isFirst = true;
var current = segment.Subbytes(1);
@ -152,15 +148,21 @@ namespace UniJSON
}
// value
var value = Parse(current, values, parentIndex);
current = current.Subbytes(value.Segment.ByteLength);
var child = Parse(array, current);
current = current.Subbytes(child.Value.Segment.ByteLength);
}
return current;
// fix array range
var count = current.Bytes.Offset + 1 - segment.Bytes.Offset;
array.SetValueBytesCount(count);
return array;
}
static Utf8String ParseObject(Utf8String segment, List<JsonValue> values, int parentIndex)
static ListTreeNode<JsonValue> ParseObject(ListTreeNode<JsonValue> tree, Utf8String segment)
{
var obj = tree.AddValue(segment.Bytes, ValueNodeType.Object);
var closeChar = '}';
bool isFirst = true;
var current = segment.Subbytes(1);
@ -209,12 +211,12 @@ namespace UniJSON
}
// key
var key = Parse(current, values, parentIndex);
if (key.ValueType != ValueNodeType.String)
var key = Parse(obj, current);
if (!key.IsString())
{
throw new ParserException("object key must string: " + key.Segment);
throw new ParserException("object key must string: " + key.Value.Segment);
}
current = current.Subbytes(key.Segment.ByteLength);
current = current.Subbytes(key.Value.Segment.ByteLength);
// search ':'
int valuePos;
@ -235,14 +237,18 @@ namespace UniJSON
}
// value
var value = Parse(current, values, parentIndex);
current = current.Subbytes(value.Segment.ByteLength);
var value = Parse(obj, current);
current = current.Subbytes(value.Value.Segment.ByteLength);
}
return current;
// fix obj range
var count = current.Bytes.Offset + 1 - segment.Bytes.Offset;
obj.SetValueBytesCount(count);
return obj;
}
static JsonValue Parse(Utf8String segment, List<JsonValue> values, int parentIndex)
public static ListTreeNode<JsonValue> Parse(ListTreeNode<JsonValue> tree, Utf8String segment)
{
// skip white space
int pos;
@ -262,38 +268,16 @@ namespace UniJSON
case ValueNodeType.NaN:
case ValueNodeType.Infinity:
case ValueNodeType.MinusInfinity:
{
var value= ParsePrimitive(segment, valueType, parentIndex);
values.Add(value);
return value;
}
return ParsePrimitive(tree, segment, valueType);
case ValueNodeType.String:
{
var value= ParseString(segment, parentIndex);
values.Add(value);
return value;
}
return ParseString(tree, segment);
case ValueNodeType.Array: // fall through
{
var index = values.Count;
values.Add(new JsonValue()); // placeholder
var current = ParseArray(segment, values, index);
values[index] = new JsonValue(segment.Subbytes(0, current.Bytes.Offset + 1 - segment.Bytes.Offset),
ValueNodeType.Array, parentIndex);
return values[index];
}
return ParseArray(tree, segment);
case ValueNodeType.Object: // fall through
{
var index = values.Count;
values.Add(new JsonValue()); // placeholder
var current=ParseObject(segment, values, index);
values[index] = new JsonValue(segment.Subbytes(0, current.Bytes.Offset + 1 - segment.Bytes.Offset),
ValueNodeType.Object, parentIndex);
return values[index];
}
return ParseObject(tree, segment);
default:
throw new NotImplementedException();
@ -307,17 +291,7 @@ namespace UniJSON
public static ListTreeNode<JsonValue> Parse(Utf8String json)
{
var result = new List<JsonValue>();
var value = Parse(json, result, -1);
if (value.ValueType != ValueNodeType.Array && value.ValueType != ValueNodeType.Object)
{
result.Add(value);
return new ListTreeNode<JsonValue>(result);
}
else
{
return new ListTreeNode<JsonValue>(result);
}
return Parse(default(ListTreeNode<JsonValue>), json);
}
}
}

View File

@ -7,6 +7,10 @@ namespace UniJSON
{
public Utf8String Segment;
public ArraySegment<Byte> Bytes { get { return Segment.Bytes; } }
public void SetBytesCount(int count)
{
Segment = new Utf8String(new ArraySegment<byte>(Bytes.Array, Bytes.Offset, count));
}
public ValueNodeType ValueType
{
@ -20,6 +24,16 @@ namespace UniJSON
private set;
}
int _childCount;
public int ChildCount
{
get { return _childCount; }
}
public void SetChildCount(int count)
{
_childCount = count;
}
public JsonValue(Utf8String segment, ValueNodeType valueType, int parentIndex) : this()
{
Segment = segment;

View File

@ -303,10 +303,14 @@ namespace UniJSON
/// <summary>
/// This node index
/// </summary>
int _valueIndex;
public int ValueIndex
{
get;
private set;
get
{
if (m_Values == null) return -1;
return _valueIndex;
}
}
public ListTreeNode<T> Prev
@ -334,14 +338,21 @@ namespace UniJSON
}
#region Children
public int ChildCount
{
get { return Value.ChildCount; }
}
public IEnumerable<ListTreeNode<T>> Children
{
get
{
for (int i = 0; i < m_Values.Count; ++i)
int count = 0;
for (int i = ValueIndex; count < ChildCount && i < m_Values.Count; ++i)
{
if (m_Values[i].ParentIndex == ValueIndex)
{
++count;
yield return new ListTreeNode<T>(m_Values, i);
}
}
@ -372,7 +383,6 @@ namespace UniJSON
}
}
#endregion
public bool HasParent
{
get
@ -399,18 +409,49 @@ namespace UniJSON
public ListTreeNode(List<T> values, int index = 0) : this()
{
m_Values = values;
ValueIndex = index;
_valueIndex = index;
}
#region JsonPointer
public void AddKey(Utf8String key)
public ListTreeNode<T> AddKey(Utf8String key)
{
m_Values.Add(default(T).Key(key, ValueIndex));
return AddValue(default(T).Key(key, ValueIndex));
}
public void AddValue(ArraySegment<byte> bytes, ValueNodeType valueType)
public ListTreeNode<T> AddValue(ArraySegment<byte> bytes, ValueNodeType valueType)
{
m_Values.Add(default(T).New(bytes, valueType, ValueIndex));
return AddValue(default(T).New(bytes, valueType, ValueIndex));
}
public ListTreeNode<T> AddValue(T value)
{
if (m_Values == null)
{
// initialize empty tree
m_Values = new List<T>();
_valueIndex = -1;
}
else
{
IncrementChildCount();
}
var index = m_Values.Count;
m_Values.Add(value);
return new ListTreeNode<T>(m_Values, index);
}
void IncrementChildCount()
{
var value = Value;
value.SetChildCount(value.ChildCount + 1);
SetValue(value);
}
public void SetValueBytesCount(int count)
{
var value = Value;
value.SetBytesCount(count);
SetValue(value);
}
#endregion
}

View File

@ -357,65 +357,60 @@ namespace UniJSON
}
}
static ArraySegment<Byte> _Parse(ArraySegment<Byte> bytes, List<MsgPackValue> values, int parentIndex)
static ListTreeNode<MsgPackValue> _Parse(ListTreeNode<MsgPackValue> tree, ArraySegment<Byte> bytes)
{
MsgPackType formatType = GetFormat(bytes);
if (formatType.IsArray())
{
var index = values.Count;
var offset = bytes.Offset;
values.Add(new MsgPackValue(bytes, parentIndex));
var array = tree.AddValue(bytes, ValueNodeType.Array);
uint count;
bytes = GetItemCount(bytes, formatType, out count);
for (var i = 0; i < count; ++i)
{
bytes = _Parse(bytes, values, index);
var child = _Parse(array, bytes);
bytes = bytes.Advance(child.Value.Bytes.Count);
}
values[index] = new MsgPackValue(
new ArraySegment<byte>(bytes.Array,
offset, bytes.Offset - offset),
parentIndex);
array.SetValueBytesCount(bytes.Offset - array.Value.Bytes.Offset);
return array;
}
else if (formatType.IsMap())
{
var index = values.Count;
var offset = bytes.Offset;
values.Add(new MsgPackValue(bytes, parentIndex));
var obj = tree.AddValue(bytes, ValueNodeType.Object);
uint count;
bytes = GetItemCount(bytes, formatType, out count);
for (var i = 0; i < count; ++i)
{
// key
bytes = _Parse(bytes, values, index);
var key = _Parse(obj, bytes);
bytes = bytes.Advance(key.Value.Bytes.Count);
// value
bytes = _Parse(bytes, values, index);
var value = _Parse(obj, bytes);
bytes = bytes.Advance(value.Value.Bytes.Count);
}
values[index] = new MsgPackValue(
new ArraySegment<byte>(bytes.Array,
offset, bytes.Offset - offset),
parentIndex);
obj.SetValueBytesCount(bytes.Offset - obj.Value.Bytes.Offset);
return obj;
}
else
{
var body = GetBody(bytes, formatType);
var headerSize = body.Offset - bytes.Offset;
var size = headerSize + body.Count;
values.Add(new MsgPackValue(bytes.Take(size), parentIndex));
bytes = bytes.Advance(size);
var value = tree.AddValue(bytes.Take(size), ValueNodeType.Null);
return value;
}
return bytes;
}
public static ListTreeNode<MsgPackValue> Parse(ArraySegment<Byte> bytes)
{
var values = new List<MsgPackValue>();
_Parse(bytes, values, -1);
return new ListTreeNode<MsgPackValue>(values);
return _Parse(default(ListTreeNode<MsgPackValue>), bytes);
}
}
}

View File

@ -16,6 +16,10 @@ namespace UniJSON
get;
private set;
}
public void SetBytesCount(int count)
{
Bytes = new ArraySegment<byte>(Bytes.Array, Bytes.Offset, count);
}
public MsgPackType Format
{
@ -71,6 +75,16 @@ namespace UniJSON
}
}
int _childCount;
public int ChildCount
{
get { return _childCount; }
}
public void SetChildCount(int count)
{
_childCount = count;
}
public MsgPackValue(ArraySegment<Byte> segment, int parentIndex) : this()
{
Bytes = segment;
@ -79,7 +93,7 @@ namespace UniJSON
public MsgPackValue New(ArraySegment<byte> bytes, ValueNodeType valueType, int parentIndex)
{
throw new NotImplementedException();
return new MsgPackValue(bytes, parentIndex);
}
public MsgPackValue Key(Utf8String key, int parentIndex)

View File

@ -62,7 +62,24 @@ namespace UniJSON
}
Utf8String m_segment;
public ArraySegment<byte> Bytes { get { return m_segment.Bytes; } }
public void SetBytesCount(int count)
{
throw new NotImplementedException();
}
public int ChildCount
{
get
{
throw new NotImplementedException();
}
}
public void SetChildCount(int count)
{
throw new NotImplementedException();
}
public TomlValue(Utf8String segment, TomlValueType valueType, int parentIndex) : this()
{