From 010edb27b7e3f8034d7c1601680a9d175d37cf54 Mon Sep 17 00:00:00 2001 From: Armanbqt Date: Mon, 6 Jan 2020 16:03:19 -0800 Subject: [PATCH 1/9] Ongoing - methods marked with TODO still need implementation. --- IonDotnet/Builders/IonReaderBuilder.cs | 7 + IonDotnet/Internals/Tree/SystemTreeReader.cs | 147 +++++++++++++++++++ IonDotnet/Internals/Tree/UserTreeReader.cs | 21 +++ IonDotnet/Tree/IIonLob.cs | 1 + IonDotnet/Tree/Impl/IonLob.cs | 5 + IonDotnet/Tree/Impl/IonValue.cs | 5 + 6 files changed, 186 insertions(+) create mode 100644 IonDotnet/Internals/Tree/SystemTreeReader.cs create mode 100644 IonDotnet/Internals/Tree/UserTreeReader.cs diff --git a/IonDotnet/Builders/IonReaderBuilder.cs b/IonDotnet/Builders/IonReaderBuilder.cs index 2187bfb7..1de8f169 100644 --- a/IonDotnet/Builders/IonReaderBuilder.cs +++ b/IonDotnet/Builders/IonReaderBuilder.cs @@ -3,6 +3,8 @@ using System.Text; using IonDotnet.Internals.Binary; using IonDotnet.Internals.Text; +using IonDotnet.Internals.Tree; +using IonDotnet.Tree; namespace IonDotnet.Builders { @@ -40,6 +42,11 @@ public static IIonReader Build(string text, ReaderOptions options = default) return new UserTextReader(text, options.Catalog); } + public static IIonReader Build(IIonValue value, ReaderOptions options = default) + { + return new UserTreeReader(value, options.Catalog); + } + public static IIonReader Build(byte[] data, ReaderOptions options = default) { return Build(new MemoryStream(data), options); diff --git a/IonDotnet/Internals/Tree/SystemTreeReader.cs b/IonDotnet/Internals/Tree/SystemTreeReader.cs new file mode 100644 index 00000000..9657f182 --- /dev/null +++ b/IonDotnet/Internals/Tree/SystemTreeReader.cs @@ -0,0 +1,147 @@ +using IonDotnet.Tree; +using System; +using System.Collections.Generic; +using System.Numerics; +using System.Text; + +namespace IonDotnet.Internals.Tree +{ + internal class SystemTreeReader : IIonReader + { + + protected readonly IIonValue _value; + protected readonly ISymbolTable _systemSymbols; + + protected SystemTreeReader(IIonValue value) + { + _value = value; + _systemSymbols = SharedSymbolTable.GetSystem(1); + } + + public int CurrentDepth => throw new NotImplementedException(); + + public IonType CurrentType => throw new NotImplementedException(); + + public string CurrentFieldName => throw new NotImplementedException(); + + public bool CurrentIsNull => throw new NotImplementedException(); + + public bool IsInStruct => throw new NotImplementedException(); + + public BigInteger BigIntegerValue() + { + return _value.BigIntegerValue; + } + + public bool BoolValue() + { + return _value.BoolValue; + } + + public BigDecimal DecimalValue() + { + return _value.BigDecimalValue; + } + + public double DoubleValue() + { + return _value.DoubleValue; + } + + public int GetBytes(Span buffer) + { + var lobSize = GetLobByteSize(); + var bufSize = buffer.Length; + + if (lobSize < 0 || bufSize < 0) + { + return 0; + } + else if (lobSize <= bufSize) + { + _value.Bytes().CopyTo(buffer); + return lobSize; + } + else if (lobSize > bufSize) + { + _value.Bytes() + .Slice(0, bufSize - 1) + .CopyTo(buffer); + return bufSize; + } + + throw new IonException("Problem while copying the current blob value to a buffer"); + } + + public SymbolToken GetFieldNameSymbol() + { + return _value.FieldNameSymbol; + } + + public IntegerSize GetIntegerSize() + { + return _value.IntegerSize; + } + + public int GetLobByteSize() + { + return _value.ByteSize(); + } + + public virtual ISymbolTable GetSymbolTable() => _systemSymbols; + + public IEnumerable GetTypeAnnotations() + { + return _value.GetTypeAnnotations(); + } + + public int IntValue() + { + return _value.IntValue; + } + + public long LongValue() + { + return _value.LongValue; + } + + //TODO + public IonType MoveNext() + { + throw new NotImplementedException(); + } + + public byte[] NewByteArray() + { + return _value.Bytes().ToArray(); + } + + //TODO: + public void StepIn() + { + + throw new NotImplementedException(); + } + + //TODO + public void StepOut() + { + throw new NotImplementedException(); + } + + public string StringValue() + { + return _value.StringValue; + } + + public SymbolToken SymbolValue() + { + return _value.SymbolValue; + } + + public Timestamp TimestampValue() + { + return _value.TimestampValue; + } + } +} diff --git a/IonDotnet/Internals/Tree/UserTreeReader.cs b/IonDotnet/Internals/Tree/UserTreeReader.cs new file mode 100644 index 00000000..0e73aab3 --- /dev/null +++ b/IonDotnet/Internals/Tree/UserTreeReader.cs @@ -0,0 +1,21 @@ +using IonDotnet.Tree; +using System; +using System.Collections.Generic; +using System.Text; + +namespace IonDotnet.Internals.Tree +{ + internal class UserTreeReader : SystemTreeReader + { + private ISymbolTable _currentSymtab; + private readonly ICatalog _catalog; + + public UserTreeReader(IIonValue value, ICatalog catalog) : base(value) + { + _catalog = catalog; + _currentSymtab = _systemSymbols; + } + + public override ISymbolTable GetSymbolTable() => _currentSymtab; + } +} diff --git a/IonDotnet/Tree/IIonLob.cs b/IonDotnet/Tree/IIonLob.cs index 29276a5e..d1d20b9e 100644 --- a/IonDotnet/Tree/IIonLob.cs +++ b/IonDotnet/Tree/IIonLob.cs @@ -5,5 +5,6 @@ public interface IIonLob { ReadOnlySpan Bytes(); void SetBytes(ReadOnlySpan buffer); + int ByteSize(); } } diff --git a/IonDotnet/Tree/Impl/IonLob.cs b/IonDotnet/Tree/Impl/IonLob.cs index 28311e44..9ad0e6be 100644 --- a/IonDotnet/Tree/Impl/IonLob.cs +++ b/IonDotnet/Tree/Impl/IonLob.cs @@ -50,5 +50,10 @@ public override void MakeNull() base.MakeNull(); ByteBuffer = null; } + + public override int ByteSize() + { + return ByteBuffer.Length; + } } } diff --git a/IonDotnet/Tree/Impl/IonValue.cs b/IonDotnet/Tree/Impl/IonValue.cs index e63ac7bb..0dbed742 100644 --- a/IonDotnet/Tree/Impl/IonValue.cs +++ b/IonDotnet/Tree/Impl/IonValue.cs @@ -450,6 +450,11 @@ public virtual ReadOnlySpan Bytes() } public virtual void SetBytes(ReadOnlySpan buffer) + { + throw new InvalidOperationException(GetErrorMessage()); + } + + public virtual int ByteSize() { throw new InvalidOperationException(GetErrorMessage()); } From b0c64f6898683389d2d63f5463884e5251bc44bd Mon Sep 17 00:00:00 2001 From: Armanbqt Date: Tue, 7 Jan 2020 17:06:06 -0800 Subject: [PATCH 2/9] ongoing - completing implementation for Containers and symbol table --- IonDotnet/Internals/Tree/SystemTreeReader.cs | 209 ++++++++++++++++++- IonDotnet/Internals/Tree/UserTreeReader.cs | 17 ++ 2 files changed, 215 insertions(+), 11 deletions(-) diff --git a/IonDotnet/Internals/Tree/SystemTreeReader.cs b/IonDotnet/Internals/Tree/SystemTreeReader.cs index 9657f182..77162108 100644 --- a/IonDotnet/Internals/Tree/SystemTreeReader.cs +++ b/IonDotnet/Internals/Tree/SystemTreeReader.cs @@ -1,5 +1,7 @@ using IonDotnet.Tree; +using IonDotnet.Tree.Impl; using System; +using System.Collections; using System.Collections.Generic; using System.Numerics; using System.Text; @@ -11,22 +13,42 @@ internal class SystemTreeReader : IIonReader protected readonly IIonValue _value; protected readonly ISymbolTable _systemSymbols; + protected IEnumerator _iter; + protected IIonValue _parent; + protected IIonValue _next; + protected IIonValue _current; + protected bool _eof; + protected int _top; + private Object[] _stack = new Object[10]; protected SystemTreeReader(IIonValue value) { - _value = value; _systemSymbols = SharedSymbolTable.GetSystem(1); + _current = null; + _eof = false; + _top = 0; + if (value.Type() == IonType.Datagram) + { + _parent = value; + _next = null; + _iter = value.GetEnumerator(); + } + else + { + _parent = (IIonValue)value.Container; + _next = value; + } } - public int CurrentDepth => throw new NotImplementedException(); + public int CurrentDepth => _top/2; - public IonType CurrentType => throw new NotImplementedException(); + public IonType CurrentType => _value.Type(); public string CurrentFieldName => throw new NotImplementedException(); - public bool CurrentIsNull => throw new NotImplementedException(); + public bool CurrentIsNull => _value.IsNull; - public bool IsInStruct => throw new NotImplementedException(); + public bool IsInStruct => CurrentDepth > 0 && _parent.Type() == IonType.Struct; public BigInteger BigIntegerValue() { @@ -105,10 +127,17 @@ public long LongValue() return _value.LongValue; } - //TODO public IonType MoveNext() { - throw new NotImplementedException(); + if (_next == null && !hasNext()) + { + _current = null; + return IonType.Null; + } + _current = _next; + _next = null; + + return ((IonValue)_current).Type(); } public byte[] NewByteArray() @@ -116,17 +145,88 @@ public byte[] NewByteArray() return _value.Bytes().ToArray(); } - //TODO: public void StepIn() { - - throw new NotImplementedException(); + if (!IsContainer()) + { + throw new IonException("current value must be a container"); + } + + push(); + _parent = _current; + _iter = _current; + _current = null; + } + + private bool IsContainer() + { + return _current.Type() == IonType.Struct + || _current.Type() == IonType.List + || _current.Type() == IonType.Sexp + || _current.Type() == IonType.Datagram; + } + + private void push() + { + int oldlen = _stack.Length; + if (_top + 1 >= oldlen) + { + // we're going to do a "+2" on top so we need extra space + int newlen = oldlen * 2; + Object[] temp = new Object[newlen]; + Array.Copy(_stack, 0, temp, 0, oldlen); + _stack = temp; + } + _stack[_top++] = _parent; + _stack[_top++] = _iter; + } + + private void pop() + { + _top--; + _iter = (IEnumerator)_stack[_top]; + _stack[_top] = null; // Allow iterator to be garbage collected! + + _top--; + _parent = (IIonValue)_stack[_top]; + _stack[_top] = null; + + // We don't know if we're at the end of the container, so check again. + _eof = false; + } + + public bool hasNext() + { + IonType next_type = next_helper_system(); + return (next_type != IonType.Null); + } + + IonType next_helper_system() + { + if (_eof) return IonType.Null; + if (_next != null) return _next.Type(); + + while(_iter != null && _iter.MoveNext()) + { + _next = _iter.Current; + } + + if ((_eof = (_next == null))) + { + return IonType.Null; + } + return _next.Type(); } //TODO public void StepOut() { - throw new NotImplementedException(); + if (_top < 1) + { + throw new IonException("Cannot stepOut any further, already at top level."); + } + pop(); + _current = null; } public string StringValue() @@ -143,5 +243,92 @@ public Timestamp TimestampValue() { return _value.TimestampValue; } + + internal class Children : IEnumerator + { + bool _eof; + int _next_idx; + IIonValue _parent; + IIonValue _curr; + + public Children(IIonValue parent) + { + _parent = parent; + _next_idx = 0; + _curr = null; + if (_parent.IsNull) + { + // otherwise the empty contents member will cause trouble + _eof = true; + } + } + + public IIonValue Current + { + get + { + try + { + return _parent.GetElementAt(_next_idx); + } + catch (IndexOutOfRangeException) + { + throw new InvalidOperationException(); + } + } + } + + object IEnumerator.Current => _curr; + + public void Dispose() + { + throw new NotImplementedException(); + } + + //TODO: FIX THE LOGIC + public bool MoveNext() + { + if (_eof) + { + return false; + } + + int len = _parent.Count; + + if (_next_idx > 0) + { + int ii = _next_idx - 1; + _next_idx = len; + + while (ii < len) + { + if (_curr == _parent.GetElementAt(ii)) + { + _next_idx = ii + 1; + break; + } + } + } + if (_next_idx >= _parent.Count) + { + _eof = true; + } + //return !_eof; + + if (true) + { + _next_idx++; + return true; + } + return false; + // position++; + // return (position < _people.Length); + } + + public void Reset() + { + throw new NotImplementedException(); + } + } } } diff --git a/IonDotnet/Internals/Tree/UserTreeReader.cs b/IonDotnet/Internals/Tree/UserTreeReader.cs index 0e73aab3..0f73f697 100644 --- a/IonDotnet/Internals/Tree/UserTreeReader.cs +++ b/IonDotnet/Internals/Tree/UserTreeReader.cs @@ -17,5 +17,22 @@ public UserTreeReader(IIonValue value, ICatalog catalog) : base(value) } public override ISymbolTable GetSymbolTable() => _currentSymtab; + + public bool hasNext() + { + return next_helper_user(); + } + + public IonType MoveNext() + { + if (!next_helper_user()) + { + _current = null; + return null; + } + _current = _next; + _next = null; + return _current.Type(); + } } } From eb1856e76a897171ec2cd54b2869e6e938afe363 Mon Sep 17 00:00:00 2001 From: Armanbqt Date: Thu, 9 Jan 2020 10:51:48 -0800 Subject: [PATCH 3/9] IonReader.Builder(IonValue) --- IonDotnet/Internals/Tree/SystemTreeReader.cs | 71 +++++------- IonDotnet/Internals/Tree/UserTreeReader.cs | 111 ++++++++++++++++++- 2 files changed, 135 insertions(+), 47 deletions(-) diff --git a/IonDotnet/Internals/Tree/SystemTreeReader.cs b/IonDotnet/Internals/Tree/SystemTreeReader.cs index 77162108..ceda4254 100644 --- a/IonDotnet/Internals/Tree/SystemTreeReader.cs +++ b/IonDotnet/Internals/Tree/SystemTreeReader.cs @@ -36,38 +36,38 @@ protected SystemTreeReader(IIonValue value) else { _parent = (IIonValue)value.Container; - _next = value; + _current = value; } } public int CurrentDepth => _top/2; - public IonType CurrentType => _value.Type(); + public IonType CurrentType => _current.Type(); public string CurrentFieldName => throw new NotImplementedException(); - public bool CurrentIsNull => _value.IsNull; + public bool CurrentIsNull => _current.IsNull; public bool IsInStruct => CurrentDepth > 0 && _parent.Type() == IonType.Struct; public BigInteger BigIntegerValue() { - return _value.BigIntegerValue; + return _current.BigIntegerValue; } public bool BoolValue() { - return _value.BoolValue; + return _current.BoolValue; } public BigDecimal DecimalValue() { - return _value.BigDecimalValue; + return _current.BigDecimalValue; } public double DoubleValue() { - return _value.DoubleValue; + return _current.DoubleValue; } public int GetBytes(Span buffer) @@ -81,12 +81,12 @@ public int GetBytes(Span buffer) } else if (lobSize <= bufSize) { - _value.Bytes().CopyTo(buffer); + _current.Bytes().CopyTo(buffer); return lobSize; } else if (lobSize > bufSize) { - _value.Bytes() + _current.Bytes() .Slice(0, bufSize - 1) .CopyTo(buffer); return bufSize; @@ -97,39 +97,39 @@ public int GetBytes(Span buffer) public SymbolToken GetFieldNameSymbol() { - return _value.FieldNameSymbol; + return _current.FieldNameSymbol; } public IntegerSize GetIntegerSize() { - return _value.IntegerSize; + return _current.IntegerSize; } public int GetLobByteSize() { - return _value.ByteSize(); + return _current.ByteSize(); } public virtual ISymbolTable GetSymbolTable() => _systemSymbols; public IEnumerable GetTypeAnnotations() { - return _value.GetTypeAnnotations(); + return _current.GetTypeAnnotations(); } public int IntValue() { - return _value.IntValue; + return _next.IntValue; } public long LongValue() { - return _value.LongValue; + return _current.LongValue; } - public IonType MoveNext() + public virtual IonType MoveNext() { - if (_next == null && !hasNext()) + if (_next == null && !HasNext()) { _current = null; return IonType.Null; @@ -142,7 +142,7 @@ public IonType MoveNext() public byte[] NewByteArray() { - return _value.Bytes().ToArray(); + return _current.Bytes().ToArray(); } public void StepIn() @@ -152,9 +152,9 @@ public void StepIn() throw new IonException("current value must be a container"); } - push(); + Push(); _parent = _current; - _iter = _current; + _iter = new Children(_current); _current = null; } @@ -166,7 +166,7 @@ private bool IsContainer() || _current.Type() == IonType.Datagram; } - private void push() + private void Push() { int oldlen = _stack.Length; if (_top + 1 >= oldlen) @@ -181,7 +181,7 @@ private void push() _stack[_top++] = _iter; } - private void pop() + private void Pop() { _top--; _iter = (IEnumerator)_stack[_top]; @@ -195,13 +195,13 @@ private void pop() _eof = false; } - public bool hasNext() + public virtual bool HasNext() { - IonType next_type = next_helper_system(); + IonType next_type = NextHelperSystem(); return (next_type != IonType.Null); } - IonType next_helper_system() + protected IonType NextHelperSystem() { if (_eof) return IonType.Null; if (_next != null) return _next.Type(); @@ -218,30 +218,29 @@ IonType next_helper_system() return _next.Type(); } - //TODO public void StepOut() { if (_top < 1) { throw new IonException("Cannot stepOut any further, already at top level."); } - pop(); + Pop(); _current = null; } public string StringValue() { - return _value.StringValue; + return _current.StringValue; } public SymbolToken SymbolValue() { - return _value.SymbolValue; + return _current.SymbolValue; } public Timestamp TimestampValue() { - return _value.TimestampValue; + return _current.TimestampValue; } internal class Children : IEnumerator @@ -285,7 +284,6 @@ public void Dispose() throw new NotImplementedException(); } - //TODO: FIX THE LOGIC public bool MoveNext() { if (_eof) @@ -313,16 +311,7 @@ public bool MoveNext() { _eof = true; } - //return !_eof; - - if (true) - { - _next_idx++; - return true; - } - return false; - // position++; - // return (position < _people.Length); + return !_eof; } public void Reset() diff --git a/IonDotnet/Internals/Tree/UserTreeReader.cs b/IonDotnet/Internals/Tree/UserTreeReader.cs index 0f73f697..8663a626 100644 --- a/IonDotnet/Internals/Tree/UserTreeReader.cs +++ b/IonDotnet/Internals/Tree/UserTreeReader.cs @@ -7,8 +7,10 @@ namespace IonDotnet.Internals.Tree { internal class UserTreeReader : SystemTreeReader { - private ISymbolTable _currentSymtab; + /// The ID of system symbol {@value #ION_1_0}, as defined by Ion 1.0. + public static readonly int ION_1_0_SID = 2; private readonly ICatalog _catalog; + private ISymbolTable _currentSymtab; public UserTreeReader(IIonValue value, ICatalog catalog) : base(value) { @@ -18,21 +20,118 @@ public UserTreeReader(IIonValue value, ICatalog catalog) : base(value) public override ISymbolTable GetSymbolTable() => _currentSymtab; - public bool hasNext() + public override bool HasNext() { - return next_helper_user(); + return NextHelperUser(); } - public IonType MoveNext() + public override IonType MoveNext() { - if (!next_helper_user()) + if (!NextHelperUser()) { _current = null; - return null; + return IonType.Null; } _current = _next; _next = null; return _current.Type(); } + + bool NextHelperUser() + { + if (_eof) return false; + if (_next != null) return true; + + ClearSystemValueStack(); + + // read values from the system + // reader and if they are system values + // process them. Return when we've + // read all the immediate system values + IonType next_type; + while (true) + { + next_type = NextHelperSystem(); + + if (_top == 0 && _parent.Type() == IonType.Datagram) + { + if (IonType.Symbol == next_type) + { + var sym = _next; + if (sym.IsNull) + { + // there are no null values we will consume here + break; + } + int sid = sym.SymbolValue.Sid; + if (sid == -1) // if sid is unknown + { + String name = sym.SymbolValue.Text; + if (name != null) + { + sid = _systemSymbols.FindSymbolId(name); + } + } + if (sid == ION_1_0_SID && _next.GetTypeAnnotations().Count == 0) + { + // $ion_1_0 is read as an IVM only if it is not annotated + ISymbolTable symbols = _systemSymbols; + _currentSymtab = symbols; + PushSymbolTable(symbols); + _next = null; + continue; + } + } + else if (IonType.Struct == next_type && _next.HasAnnotation("$ion_symbol_table")) + { + // read a local symbol table + IIonReader reader = new UserTreeReader(_next, _catalog); + ISymbolTable symtab = ReaderLocalTable.ImportReaderTable(this, _catalog, true); + _currentSymtab = symtab; + PushSymbolTable(symtab); + _next = null; + continue; + } + } + // if we get here we didn't process a system + // value, if we had we would have 'continue'd + // so this is a value the user gets + break; + } + return (next_type != IonType.Null); + } + + private int _symbol_table_top = 0; + private ISymbolTable[] _symbol_table_stack = new ISymbolTable[3]; // 3 is rare, IVM followed by a local sym tab with open content + private void ClearSystemValueStack() + { + while (_symbol_table_top > 0) + { + _symbol_table_top--; + _symbol_table_stack[_symbol_table_top] = null; + } + } + private void PushSymbolTable(ISymbolTable symbols) + { + if (_symbol_table_top >= _symbol_table_stack.Length) + { + int new_len = _symbol_table_stack.Length * 2; + ISymbolTable[] temp = new ISymbolTable[new_len]; + Array.Copy(_symbol_table_stack, 0, temp, 0, _symbol_table_stack.Length); + _symbol_table_stack = temp; + } + _symbol_table_stack[_symbol_table_top++] = symbols; + } + private ISymbolTable PopPassedSymbolTable() + { + if (_symbol_table_top <= 0) + { + return null; + } + _symbol_table_top--; + ISymbolTable symbols = _symbol_table_stack[_symbol_table_top]; + _symbol_table_stack[_symbol_table_top] = null; + return symbols; + } } } From 5aabb4be909d7cfc0c9979b5699f0fc29d17c875 Mon Sep 17 00:00:00 2001 From: Armanbqt Date: Thu, 9 Jan 2020 11:19:35 -0800 Subject: [PATCH 4/9] CurrentFieldName --- IonDotnet/Internals/Tree/SystemTreeReader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IonDotnet/Internals/Tree/SystemTreeReader.cs b/IonDotnet/Internals/Tree/SystemTreeReader.cs index ceda4254..70c04bbd 100644 --- a/IonDotnet/Internals/Tree/SystemTreeReader.cs +++ b/IonDotnet/Internals/Tree/SystemTreeReader.cs @@ -44,7 +44,7 @@ protected SystemTreeReader(IIonValue value) public IonType CurrentType => _current.Type(); - public string CurrentFieldName => throw new NotImplementedException(); + public string CurrentFieldName => _current.FieldNameSymbol.Text; public bool CurrentIsNull => _current.IsNull; From 8288ecca2ef9da816f1959e4ac83494c9fddbbf2 Mon Sep 17 00:00:00 2001 From: Armanbqt Date: Thu, 9 Jan 2020 16:57:07 -0800 Subject: [PATCH 5/9] Code review fixes --- IonDotnet/Internals/Tree/SystemTreeReader.cs | 14 ++++++-------- IonDotnet/Internals/Tree/UserTreeReader.cs | 6 +++--- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/IonDotnet/Internals/Tree/SystemTreeReader.cs b/IonDotnet/Internals/Tree/SystemTreeReader.cs index 70c04bbd..fea201df 100644 --- a/IonDotnet/Internals/Tree/SystemTreeReader.cs +++ b/IonDotnet/Internals/Tree/SystemTreeReader.cs @@ -10,8 +10,6 @@ namespace IonDotnet.Internals.Tree { internal class SystemTreeReader : IIonReader { - - protected readonly IIonValue _value; protected readonly ISymbolTable _systemSymbols; protected IEnumerator _iter; protected IIonValue _parent; @@ -119,7 +117,7 @@ public IEnumerable GetTypeAnnotations() public int IntValue() { - return _next.IntValue; + return _current.IntValue; } public long LongValue() @@ -168,13 +166,13 @@ private bool IsContainer() private void Push() { - int oldlen = _stack.Length; - if (_top + 1 >= oldlen) + int oldLen = _stack.Length; + if (_top + 1 >= oldLen) { // we're going to do a "+2" on top so we need extra space - int newlen = oldlen * 2; - Object[] temp = new Object[newlen]; - Array.Copy(_stack, 0, temp, 0, oldlen); + int newLen = oldLen * 2; + Object[] temp = new Object[newLen]; + Array.Copy(_stack, 0, temp, 0, oldLen); _stack = temp; } _stack[_top++] = _parent; diff --git a/IonDotnet/Internals/Tree/UserTreeReader.cs b/IonDotnet/Internals/Tree/UserTreeReader.cs index 8663a626..1b724f84 100644 --- a/IonDotnet/Internals/Tree/UserTreeReader.cs +++ b/IonDotnet/Internals/Tree/UserTreeReader.cs @@ -8,9 +8,11 @@ namespace IonDotnet.Internals.Tree internal class UserTreeReader : SystemTreeReader { /// The ID of system symbol {@value #ION_1_0}, as defined by Ion 1.0. - public static readonly int ION_1_0_SID = 2; + private const int ION_1_0_SID = 2; private readonly ICatalog _catalog; private ISymbolTable _currentSymtab; + private int _symbol_table_top = 0; + private ISymbolTable[] _symbol_table_stack = new ISymbolTable[3]; // 3 is rare, IVM followed by a local sym tab with open content public UserTreeReader(IIonValue value, ICatalog catalog) : base(value) { @@ -101,8 +103,6 @@ bool NextHelperUser() return (next_type != IonType.Null); } - private int _symbol_table_top = 0; - private ISymbolTable[] _symbol_table_stack = new ISymbolTable[3]; // 3 is rare, IVM followed by a local sym tab with open content private void ClearSystemValueStack() { while (_symbol_table_top > 0) From a1d2ead4049de9eaad7e6c75ed3a11e73ba03425 Mon Sep 17 00:00:00 2001 From: Armanbqt Date: Mon, 13 Jan 2020 18:18:08 -0800 Subject: [PATCH 6/9] Ongoing - fixing code review comments and adding tests --- IonDotnet.Tests/Internals/TreeReaderTest.cs | 103 +++++++++++++++++++ IonDotnet/Internals/Tree/SystemTreeReader.cs | 27 ++--- IonDotnet/Internals/Tree/UserTreeReader.cs | 34 +++--- 3 files changed, 134 insertions(+), 30 deletions(-) create mode 100644 IonDotnet.Tests/Internals/TreeReaderTest.cs diff --git a/IonDotnet.Tests/Internals/TreeReaderTest.cs b/IonDotnet.Tests/Internals/TreeReaderTest.cs new file mode 100644 index 00000000..12aea879 --- /dev/null +++ b/IonDotnet.Tests/Internals/TreeReaderTest.cs @@ -0,0 +1,103 @@ +using IonDotnet.Builders; +using IonDotnet.Tests.Common; +using IonDotnet.Tree; +using IonDotnet.Tree.Impl; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using IonDotnet.Internals.Tree; +using System; +using System.Collections.Generic; +using System.Text; + +namespace IonDotnet.Tests.Internals +{ + [TestClass] + public class TreeReaderTest + { + + private IValueFactory _ionValueFactory = new ValueFactory(); + + [TestMethod] + public void SingleIntNumberTest() + { + var value = (IIonValue)_ionValueFactory.NewInt(123); + var reader = new UserTreeReader(value); + + ReaderTestCommon.SingleNumber(reader, 123); + } + + [TestMethod] + public void SingleDecimalNumberTest() + { + var decimalValue = new BigDecimal(decimal.MaxValue); + var value = (IIonValue)_ionValueFactory.NewDecimal(decimalValue); + var reader = new UserTreeReader(value); + + Assert.AreEqual(IonType.Decimal, reader.MoveNext()); + Assert.AreEqual(decimalValue, reader.DecimalValue()); + } + + + [TestMethod] + public void SingleDoubleNumberTest() + { + var value = (IIonValue)_ionValueFactory.NewFloat(123.456); + var reader = new UserTreeReader(value); + + Assert.AreEqual(IonType.Float, reader.MoveNext()); + Assert.AreEqual(123.456, reader.DoubleValue()); + } + + [TestMethod] + public void TimestampTest() + { + var timestamp = new Timestamp(DateTime.Now); + var value = (IIonValue)_ionValueFactory.NewTimestamp(timestamp); + var reader = new UserTreeReader(value); + + Assert.AreEqual(IonType.Timestamp, reader.MoveNext()); + Assert.AreEqual(timestamp, reader.TimestampValue()); + } + + [TestMethod] + public void BoolValueTest() + { + var value = (IIonValue)_ionValueFactory.NewBool(true); + var reader = new UserTreeReader(value); + + ReaderTestCommon.SingleBool(reader, true); + } + + [TestMethod] + public void StringValueTest() + { + var value = (IIonValue)_ionValueFactory.NewString("test"); + var reader = new UserTreeReader(value); + + Assert.AreEqual(IonType.String, reader.MoveNext()); + Assert.AreEqual("test", reader.StringValue()); + } + + [TestMethod] + public void NullValueTest() + { + var value = (IIonValue)_ionValueFactory.NewNull(); + var reader = new UserTreeReader(value); + + Assert.AreEqual(IonType.Null, reader.MoveNext()); + Assert.IsTrue(reader.CurrentIsNull); + } + + [TestMethod] + public void ListOfIntsTest() + { + var value = (IIonValue)_ionValueFactory.NewEmptyList(); + value.Add((IIonValue)_ionValueFactory.NewInt(123)); + value.Add((IIonValue)_ionValueFactory.NewInt(456)); + value.Add((IIonValue)_ionValueFactory.NewInt(789)); + var reader = new UserTreeReader(value); + + ReaderTestCommon.FlatIntList(reader); + } + + } +} diff --git a/IonDotnet/Internals/Tree/SystemTreeReader.cs b/IonDotnet/Internals/Tree/SystemTreeReader.cs index fea201df..b8862668 100644 --- a/IonDotnet/Internals/Tree/SystemTreeReader.cs +++ b/IonDotnet/Internals/Tree/SystemTreeReader.cs @@ -17,6 +17,7 @@ internal class SystemTreeReader : IIonReader protected IIonValue _current; protected bool _eof; protected int _top; + // Holds pairs: IonValue parent (_parent), Iterator cursor (_iter) private Object[] _stack = new Object[10]; protected SystemTreeReader(IIonValue value) @@ -34,7 +35,7 @@ protected SystemTreeReader(IIonValue value) else { _parent = (IIonValue)value.Container; - _current = value; + _next = value; } } @@ -130,7 +131,7 @@ public virtual IonType MoveNext() if (_next == null && !HasNext()) { _current = null; - return IonType.Null; + return IonType.None; } _current = _next; _next = null; @@ -196,12 +197,12 @@ private void Pop() public virtual bool HasNext() { IonType next_type = NextHelperSystem(); - return (next_type != IonType.Null); + return (next_type != IonType.None); } protected IonType NextHelperSystem() { - if (_eof) return IonType.Null; + if (_eof) return IonType.None; if (_next != null) return _next.Type(); while(_iter != null && _iter.MoveNext()) @@ -211,7 +212,7 @@ protected IonType NextHelperSystem() if ((_eof = (_next == null))) { - return IonType.Null; + return IonType.None; } return _next.Type(); } @@ -244,14 +245,14 @@ public Timestamp TimestampValue() internal class Children : IEnumerator { bool _eof; - int _next_idx; + int _nextIdx; IIonValue _parent; IIonValue _curr; public Children(IIonValue parent) { _parent = parent; - _next_idx = 0; + _nextIdx = 0; _curr = null; if (_parent.IsNull) { @@ -266,7 +267,7 @@ public IIonValue Current { try { - return _parent.GetElementAt(_next_idx); + return _parent.GetElementAt(_nextIdx); } catch (IndexOutOfRangeException) { @@ -291,21 +292,21 @@ public bool MoveNext() int len = _parent.Count; - if (_next_idx > 0) + if (_nextIdx > 0) { - int ii = _next_idx - 1; - _next_idx = len; + int ii = _nextIdx - 1; + _nextIdx = len; while (ii < len) { if (_curr == _parent.GetElementAt(ii)) { - _next_idx = ii + 1; + _nextIdx = ii + 1; break; } } } - if (_next_idx >= _parent.Count) + if (_nextIdx >= _parent.Count) { _eof = true; } diff --git a/IonDotnet/Internals/Tree/UserTreeReader.cs b/IonDotnet/Internals/Tree/UserTreeReader.cs index 1b724f84..a42be1dd 100644 --- a/IonDotnet/Internals/Tree/UserTreeReader.cs +++ b/IonDotnet/Internals/Tree/UserTreeReader.cs @@ -11,10 +11,10 @@ internal class UserTreeReader : SystemTreeReader private const int ION_1_0_SID = 2; private readonly ICatalog _catalog; private ISymbolTable _currentSymtab; - private int _symbol_table_top = 0; - private ISymbolTable[] _symbol_table_stack = new ISymbolTable[3]; // 3 is rare, IVM followed by a local sym tab with open content + private int _symbolTableTop = 0; + private ISymbolTable[] _symbolTableStack = new ISymbolTable[3]; // 3 is rare, IVM followed by a local sym tab with open content - public UserTreeReader(IIonValue value, ICatalog catalog) : base(value) + public UserTreeReader(IIonValue value, ICatalog catalog = null) : base(value) { _catalog = catalog; _currentSymtab = _systemSymbols; @@ -32,7 +32,7 @@ public override IonType MoveNext() if (!NextHelperUser()) { _current = null; - return IonType.Null; + return IonType.None; } _current = _next; _next = null; @@ -100,37 +100,37 @@ bool NextHelperUser() // so this is a value the user gets break; } - return (next_type != IonType.Null); + return (next_type != IonType.None); } private void ClearSystemValueStack() { - while (_symbol_table_top > 0) + while (_symbolTableTop > 0) { - _symbol_table_top--; - _symbol_table_stack[_symbol_table_top] = null; + _symbolTableTop--; + _symbolTableStack[_symbolTableTop] = null; } } private void PushSymbolTable(ISymbolTable symbols) { - if (_symbol_table_top >= _symbol_table_stack.Length) + if (_symbolTableTop >= _symbolTableStack.Length) { - int new_len = _symbol_table_stack.Length * 2; + int new_len = _symbolTableStack.Length * 2; ISymbolTable[] temp = new ISymbolTable[new_len]; - Array.Copy(_symbol_table_stack, 0, temp, 0, _symbol_table_stack.Length); - _symbol_table_stack = temp; + Array.Copy(_symbolTableStack, 0, temp, 0, _symbolTableStack.Length); + _symbolTableStack = temp; } - _symbol_table_stack[_symbol_table_top++] = symbols; + _symbolTableStack[_symbolTableTop++] = symbols; } private ISymbolTable PopPassedSymbolTable() { - if (_symbol_table_top <= 0) + if (_symbolTableTop <= 0) { return null; } - _symbol_table_top--; - ISymbolTable symbols = _symbol_table_stack[_symbol_table_top]; - _symbol_table_stack[_symbol_table_top] = null; + _symbolTableTop--; + ISymbolTable symbols = _symbolTableStack[_symbolTableTop]; + _symbolTableStack[_symbolTableTop] = null; return symbols; } } From a5e24281a32a27ccf13574d82feef2e3506a0f9c Mon Sep 17 00:00:00 2001 From: Armanbqt Date: Tue, 14 Jan 2020 15:32:21 -0800 Subject: [PATCH 7/9] Ongoing - Adding tests for structs and containers --- IonDotnet.Tests/Internals/TreeReaderTest.cs | 66 ++++++++++++++++++++ IonDotnet/Internals/Tree/SystemTreeReader.cs | 25 +++----- IonDotnet/Tree/Impl/IonStruct.cs | 8 +++ 3 files changed, 81 insertions(+), 18 deletions(-) diff --git a/IonDotnet.Tests/Internals/TreeReaderTest.cs b/IonDotnet.Tests/Internals/TreeReaderTest.cs index 12aea879..0bdef1b4 100644 --- a/IonDotnet.Tests/Internals/TreeReaderTest.cs +++ b/IonDotnet.Tests/Internals/TreeReaderTest.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Text; +using System.Numerics; namespace IonDotnet.Tests.Internals { @@ -90,6 +91,7 @@ public void NullValueTest() [TestMethod] public void ListOfIntsTest() { + //Must be: [123,456,789] var value = (IIonValue)_ionValueFactory.NewEmptyList(); value.Add((IIonValue)_ionValueFactory.NewInt(123)); value.Add((IIonValue)_ionValueFactory.NewInt(456)); @@ -99,5 +101,69 @@ public void ListOfIntsTest() ReaderTestCommon.FlatIntList(reader); } + [TestMethod] + public void SimpleDatagramTest() + { + //simple datagram: {yolo:true} + var value = new IonStruct{{ "yolo", (IIonValue)_ionValueFactory.NewBool(true) }}; + var reader = new UserTreeReader(value); + + ReaderTestCommon.OneBoolInStruct(reader); + } + + [TestMethod] + public void FlatStructScalarTest() + { + //Must be a flat struct of scalar values: + //boolean:true + //str:"yes" + //integer:123456 + //longInt:int.Max*2 + //bigInt:long.Max*10 + //double:2213.1267567f + var value = new IonStruct + { + { "boolean", (IIonValue)_ionValueFactory.NewBool(true) }, + { "str", (IIonValue)_ionValueFactory.NewString("yes") }, + { "integer", (IIonValue)_ionValueFactory.NewInt(123456) }, + { "longInt", (IIonValue)_ionValueFactory.NewInt((long)int.MaxValue * 2) }, + { "bigInt", (IIonValue)_ionValueFactory.NewInt(BigInteger.Multiply(new BigInteger(long.MaxValue), 10)) }, + { "double", (IIonValue)_ionValueFactory.NewFloat(2213.1267567) } + }; + var reader = new UserTreeReader(value); + + ReaderTestCommon.FlatScalar(reader); + } + + [TestMethod] + public void SingleSymbolTest() + { + //{single_symbol:'something'} + var value = new IonStruct {{ "single_symbol", (IIonValue)_ionValueFactory.NewSymbol("something")}}; + var reader = new UserTreeReader(value); + + ReaderTestCommon.SingleSymbol(reader); + } + + [TestMethod] + public void NestedAndCombinedListStructTest() + { + var popupList = (IIonValue)_ionValueFactory.NewEmptyList(); + popupList.Add((IIonValue)_ionValueFactory.NewString("open")); + popupList.Add((IIonValue)_ionValueFactory.NewString("load")); + popupList.Add((IIonValue)_ionValueFactory.NewString("close")); + + var positionList = (IIonValue)_ionValueFactory.NewEmptyList(); + positionList.Add((IIonValue)_ionValueFactory.NewInt(1234)); + positionList.Add((IIonValue)_ionValueFactory.NewInt(5678)); + positionList.Add((IIonValue)_ionValueFactory.NewInt(90)); + + var deep3 = new IonStruct {{ "deep4val", (IIonValue)_ionValueFactory.NewString("enddeep") }}; + + var deep2 = new IonStruct + { { "deep4val", (IIonValue)_ionValueFactory.NewString("enddeep") } }; + + } + } } diff --git a/IonDotnet/Internals/Tree/SystemTreeReader.cs b/IonDotnet/Internals/Tree/SystemTreeReader.cs index b8862668..1c6c1b24 100644 --- a/IonDotnet/Internals/Tree/SystemTreeReader.cs +++ b/IonDotnet/Internals/Tree/SystemTreeReader.cs @@ -205,7 +205,7 @@ protected IonType NextHelperSystem() if (_eof) return IonType.None; if (_next != null) return _next.Type(); - while(_iter != null && _iter.MoveNext()) + if(_iter != null && _iter.MoveNext()) { _next = _iter.Current; } @@ -252,7 +252,7 @@ internal class Children : IEnumerator public Children(IIonValue parent) { _parent = parent; - _nextIdx = 0; + _nextIdx = -1; _curr = null; if (_parent.IsNull) { @@ -287,28 +287,17 @@ public bool MoveNext() { if (_eof) { + _curr = null; return false; } - int len = _parent.Count; - - if (_nextIdx > 0) + if (_nextIdx >= _parent.Count - 1) { - int ii = _nextIdx - 1; - _nextIdx = len; - - while (ii < len) - { - if (_curr == _parent.GetElementAt(ii)) - { - _nextIdx = ii + 1; - break; - } - } + _eof = true; } - if (_nextIdx >= _parent.Count) + else { - _eof = true; + _curr = _parent.GetElementAt(++_nextIdx); } return !_eof; } diff --git a/IonDotnet/Tree/Impl/IonStruct.cs b/IonDotnet/Tree/Impl/IonStruct.cs index 669cbc9f..40a911f8 100644 --- a/IonDotnet/Tree/Impl/IonStruct.cs +++ b/IonDotnet/Tree/Impl/IonStruct.cs @@ -158,6 +158,14 @@ public override IEnumerator GetEnumerator() } } + public override IIonValue GetElementAt(int index) + { + if (this.Count <= 0 || index < 0) + return null; + + return _values.ElementAt(index); + } + public override bool Remove(IIonValue item) { ThrowIfNull(); From 4d5fb9705708cb00086c9249009f2cab4e92bb89 Mon Sep 17 00:00:00 2001 From: Armanbqt Date: Tue, 14 Jan 2020 19:01:44 -0800 Subject: [PATCH 8/9] Ongoing - Adding tests --- IonDotnet.Tests/Internals/TreeReaderTest.cs | 87 ++++++++++++++++++-- IonDotnet/Internals/Tree/SystemTreeReader.cs | 7 +- 2 files changed, 87 insertions(+), 7 deletions(-) diff --git a/IonDotnet.Tests/Internals/TreeReaderTest.cs b/IonDotnet.Tests/Internals/TreeReaderTest.cs index 0bdef1b4..971008c4 100644 --- a/IonDotnet.Tests/Internals/TreeReaderTest.cs +++ b/IonDotnet.Tests/Internals/TreeReaderTest.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Text; using System.Numerics; +using System.Linq; namespace IonDotnet.Tests.Internals { @@ -148,10 +149,34 @@ public void SingleSymbolTest() [TestMethod] public void NestedAndCombinedListStructTest() { + //Must be: + // { + // menu: { + // id: "file", + // popup: [ + // "Open", + // "Load", + // "Close" + // ], + // deep1: { + // deep2: { + // deep3: { + // deep4val: "enddeep" + // } + // } + // }, + // positions: [ + // 1234, + // 5678, + // 90 + // ] + // } + // } + var popupList = (IIonValue)_ionValueFactory.NewEmptyList(); - popupList.Add((IIonValue)_ionValueFactory.NewString("open")); - popupList.Add((IIonValue)_ionValueFactory.NewString("load")); - popupList.Add((IIonValue)_ionValueFactory.NewString("close")); + popupList.Add((IIonValue)_ionValueFactory.NewString("Open")); + popupList.Add((IIonValue)_ionValueFactory.NewString("Load")); + popupList.Add((IIonValue)_ionValueFactory.NewString("Close")); var positionList = (IIonValue)_ionValueFactory.NewEmptyList(); positionList.Add((IIonValue)_ionValueFactory.NewInt(1234)); @@ -160,9 +185,61 @@ public void NestedAndCombinedListStructTest() var deep3 = new IonStruct {{ "deep4val", (IIonValue)_ionValueFactory.NewString("enddeep") }}; - var deep2 = new IonStruct - { { "deep4val", (IIonValue)_ionValueFactory.NewString("enddeep") } }; + var deep2 = new IonStruct{{ "deep3", deep3 }}; + + var deep1 = new IonStruct { { "deep2", deep2 } }; + + var menu = new IonStruct + { + { "id", (IIonValue)_ionValueFactory.NewString("file") }, + { "popup", popupList }, + { "deep1", deep1 }, + { "positions", positionList } + }; + + var value = new IonStruct {{ "menu", menu }}; + var reader = new UserTreeReader(value); + + ReaderTestCommon.Combined1(reader); + } + + [TestMethod] + public void BlobTest() + { + //Must be in a struct: + // { blobbbb: {{data}} } + var arrayOfbytes = Enumerable.Repeat(1, 100).ToArray(); + ReadOnlySpan bytes = new ReadOnlySpan(arrayOfbytes); + var blob = (IIonValue)_ionValueFactory.NewBlob(bytes); + var value = new IonStruct { { "blobbbb", blob }}; + var reader = new UserTreeReader(value); + + ReaderTestCommon.Struct_OneBlob(reader); + } + + [TestMethod] + public void BlobPartialReadTest() + { + var blob = new byte[30]; + for (var i = 0; i < 30; i++) + { + blob[i] = (byte)i; + } + var value = (IIonValue)_ionValueFactory.NewBlob(blob); + var reader = new UserTreeReader(value); + + ReaderTestCommon.Blob_PartialRead(30, 7, reader); + } + + [TestMethod] + public void SimpleDatagrasamTest() + { + //simple datagram: {yolo:true} + var value = new IonStruct { { "withannot", (IIonValue)_ionValueFactory.NewInt(18) } }; + value.AddTypeAnnotation("years::months::days::hours::minutes::seconds"); + var reader = new UserTreeReader(value); + ReaderTestCommon.ReadAnnotations_SingleField(reader); } } diff --git a/IonDotnet/Internals/Tree/SystemTreeReader.cs b/IonDotnet/Internals/Tree/SystemTreeReader.cs index 1c6c1b24..58cd3e48 100644 --- a/IonDotnet/Internals/Tree/SystemTreeReader.cs +++ b/IonDotnet/Internals/Tree/SystemTreeReader.cs @@ -19,6 +19,7 @@ internal class SystemTreeReader : IIonReader protected int _top; // Holds pairs: IonValue parent (_parent), Iterator cursor (_iter) private Object[] _stack = new Object[10]; + private int pos = 0; protected SystemTreeReader(IIonValue value) { @@ -81,13 +82,15 @@ public int GetBytes(Span buffer) else if (lobSize <= bufSize) { _current.Bytes().CopyTo(buffer); + pos += lobSize; return lobSize; } - else if (lobSize > bufSize) + else if (lobSize > bufSize && pos <= lobSize) { _current.Bytes() - .Slice(0, bufSize - 1) + .Slice(pos, bufSize) .CopyTo(buffer); + pos += bufSize; return bufSize; } From f3ac1cf032391070e87e1bca776c2d0142fbca17 Mon Sep 17 00:00:00 2001 From: Armanbqt Date: Wed, 15 Jan 2020 10:22:29 -0800 Subject: [PATCH 9/9] Adding tests --- IonDotnet.Tests/Internals/TreeReaderTest.cs | 29 ++++++++++++--------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/IonDotnet.Tests/Internals/TreeReaderTest.cs b/IonDotnet.Tests/Internals/TreeReaderTest.cs index 971008c4..a16b4cdd 100644 --- a/IonDotnet.Tests/Internals/TreeReaderTest.cs +++ b/IonDotnet.Tests/Internals/TreeReaderTest.cs @@ -203,6 +203,23 @@ public void NestedAndCombinedListStructTest() ReaderTestCommon.Combined1(reader); } + [TestMethod] + public void ValueWithAnnotationTest() + { + //Must be: {withannot: years::months::days::hours::minutes::seconds::18} + var intValue = (IIonValue)_ionValueFactory.NewInt(18); + intValue.AddTypeAnnotation("years"); + intValue.AddTypeAnnotation("months"); + intValue.AddTypeAnnotation("days"); + intValue.AddTypeAnnotation("hours"); + intValue.AddTypeAnnotation("minutes"); + intValue.AddTypeAnnotation("seconds"); + var value = new IonStruct { { "withannot", intValue } }; + var reader = new UserTreeReader(value); + + ReaderTestCommon.ReadAnnotations_SingleField(reader); + } + [TestMethod] public void BlobTest() { @@ -230,17 +247,5 @@ public void BlobPartialReadTest() ReaderTestCommon.Blob_PartialRead(30, 7, reader); } - - [TestMethod] - public void SimpleDatagrasamTest() - { - //simple datagram: {yolo:true} - var value = new IonStruct { { "withannot", (IIonValue)_ionValueFactory.NewInt(18) } }; - value.AddTypeAnnotation("years::months::days::hours::minutes::seconds"); - var reader = new UserTreeReader(value); - - ReaderTestCommon.ReadAnnotations_SingleField(reader); - } - } }