Skip to content

Commit f80ffb0

Browse files
Merge pull request #126 from Stillpoint-Software/improve-descent-wildcard
Improve descent wildcard
2 parents 40bfba1 + 8023ecc commit f80ffb0

19 files changed

+943
-287
lines changed

README.md

Lines changed: 201 additions & 36 deletions
Large diffs are not rendered by default.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace Hyperbee.Json.Descriptors;
2+
3+
[Flags]
4+
public enum ChildEnumerationOptions
5+
{
6+
None = 0,
7+
ComplexTypesOnly = 1,
8+
Reverse = 2,
9+
}

src/Hyperbee.Json/Descriptors/Element/ElementActions.cs

Lines changed: 64 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,72 @@ public bool TryGetFromPointer( in JsonElement node, JsonSegment segment, out Jso
3333
public bool DeepEquals( JsonElement left, JsonElement right ) =>
3434
left.DeepEquals( right );
3535

36-
public IEnumerable<(JsonElement Value, string Key)> GetChildren( in JsonElement value, bool complexTypesOnly = false )
36+
public IEnumerable<JsonElement> GetChildren( JsonElement value, ChildEnumerationOptions options )
3737
{
38+
bool complexTypesOnly = options.HasFlag( ChildEnumerationOptions.ComplexTypesOnly );
39+
bool reverse = options.HasFlag( ChildEnumerationOptions.Reverse );
40+
41+
// allocating is faster than using yield return and less memory intensive.
42+
// using a collection results in fewer overall allocations than calling
43+
// LINQ reverse, which internally allocates, and then discards, a new array.
44+
45+
List<JsonElement> results;
46+
47+
switch ( value.ValueKind )
48+
{
49+
case JsonValueKind.Array:
50+
{
51+
var length = value.GetArrayLength();
52+
results = new List<JsonElement>( length );
53+
54+
for ( var index = 0; index < length; index++ )
55+
{
56+
var child = value[index];
57+
58+
if ( complexTypesOnly && child.ValueKind is not (JsonValueKind.Array or JsonValueKind.Object) )
59+
continue;
60+
61+
results.Add( child );
62+
}
63+
64+
return reverse ? results.EnumerateReverse() : results;
65+
}
66+
case JsonValueKind.Object:
67+
{
68+
results = new List<JsonElement>( 8 );
69+
70+
foreach ( var child in value.EnumerateObject() )
71+
{
72+
if ( complexTypesOnly && child.Value.ValueKind is not (JsonValueKind.Array or JsonValueKind.Object) )
73+
continue;
74+
75+
results.Add( child.Value );
76+
}
77+
78+
return reverse ? results.EnumerateReverse() : results;
79+
}
80+
}
81+
82+
return [];
83+
}
84+
85+
public IEnumerable<(JsonElement Value, string Key)> GetChildrenWithName( in JsonElement value, ChildEnumerationOptions options )
86+
{
87+
bool complexTypesOnly = options.HasFlag( ChildEnumerationOptions.ComplexTypesOnly );
88+
bool reverse = options.HasFlag( ChildEnumerationOptions.Reverse );
89+
3890
// allocating is faster than using yield return and less memory intensive.
39-
// using stack results in fewer overall allocations than calling reverse,
40-
// which internally allocates, and then discards, a new array.
91+
// using a collection results in fewer overall allocations than calling
92+
// LINQ reverse, which internally allocates, and then discards, a new array.
93+
94+
List<(JsonElement, string)> results;
4195

4296
switch ( value.ValueKind )
4397
{
4498
case JsonValueKind.Array:
4599
{
46100
var length = value.GetArrayLength();
47-
var results = new Stack<(JsonElement, string)>( length ); // stack will reverse items
101+
results = new List<(JsonElement, string)>( length );
48102

49103
for ( var index = 0; index < length; index++ )
50104
{
@@ -53,27 +107,28 @@ public bool DeepEquals( JsonElement left, JsonElement right ) =>
53107
if ( complexTypesOnly && child.ValueKind is not (JsonValueKind.Array or JsonValueKind.Object) )
54108
continue;
55109

56-
results.Push( (child, IndexHelper.GetIndexString( index )) );
110+
results.Add( (child, IndexHelper.GetIndexString( index )) );
57111
}
58112

59-
return results;
113+
return reverse ? results.EnumerateReverse() : results;
60114
}
61115
case JsonValueKind.Object:
62116
{
63-
var results = new Stack<(JsonElement, string)>(); // stack will reverse items
117+
results = new List<(JsonElement, string)>( 8 );
64118

65119
foreach ( var child in value.EnumerateObject() )
66120
{
67121
if ( complexTypesOnly && child.Value.ValueKind is not (JsonValueKind.Array or JsonValueKind.Object) )
68122
continue;
69123

70-
results.Push( (child.Value, child.Name) );
124+
results.Add( (child.Value, child.Name) );
71125
}
72126

73-
return results;
127+
return reverse ? results.EnumerateReverse() : results;
74128
}
75129
}
76130

77131
return [];
78132
}
79133
}
134+

src/Hyperbee.Json/Descriptors/INodeActions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ public interface INodeActions<TNode>
1111

1212
public bool DeepEquals( TNode left, TNode right );
1313

14-
public IEnumerable<(TNode Value, string Key)> GetChildren( in TNode value, bool complexTypesOnly = false );
14+
public IEnumerable<(TNode Value, string Key)> GetChildrenWithName( in TNode value, ChildEnumerationOptions options );
15+
public IEnumerable<TNode> GetChildren( TNode value, ChildEnumerationOptions options );
1516
}

src/Hyperbee.Json/Descriptors/Node/NodeActions.cs

Lines changed: 64 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Text.Json;
22
using System.Text.Json.Nodes;
3+
using Hyperbee.Json.Extensions;
34
using Hyperbee.Json.Path;
45
using Hyperbee.Json.Pointer;
56
using Hyperbee.Json.Query;
@@ -28,18 +29,72 @@ public bool TryGetFromPointer( in JsonNode node, JsonSegment segment, out JsonNo
2829
public bool DeepEquals( JsonNode left, JsonNode right ) =>
2930
JsonNode.DeepEquals( left, right );
3031

31-
public IEnumerable<(JsonNode Value, string Key)> GetChildren( in JsonNode value, bool complexTypesOnly = false )
32+
public IEnumerable<JsonNode> GetChildren( JsonNode value, ChildEnumerationOptions options )
3233
{
34+
bool complexTypesOnly = options.HasFlag( ChildEnumerationOptions.ComplexTypesOnly );
35+
bool reverse = options.HasFlag( ChildEnumerationOptions.Reverse );
36+
3337
// allocating is faster than using yield return and less memory intensive.
34-
// using stack results in fewer overall allocations than calling reverse,
35-
// which internally allocates, and then discards, a new array.
38+
// using a collection results in fewer overall allocations than calling
39+
// LINQ reverse, which internally allocates, and then discards, a new array.
40+
41+
List<JsonNode> results;
42+
43+
switch ( value )
44+
{
45+
case JsonArray jsonArray:
46+
{
47+
var length = jsonArray.Count;
48+
results = new List<JsonNode>( length );
49+
50+
for ( var index = 0; index < length; index++ )
51+
{
52+
var child = value[index];
53+
54+
if ( complexTypesOnly && child is not (JsonArray or JsonObject) )
55+
continue;
56+
57+
results.Add( child );
58+
}
59+
60+
return reverse ? results.EnumerateReverse() : results;
61+
}
62+
case JsonObject jsonObject:
63+
{
64+
results = new List<JsonNode>( 8 );
65+
66+
foreach ( var child in jsonObject )
67+
{
68+
if ( complexTypesOnly && child.Value is not (JsonArray or JsonObject) )
69+
continue;
70+
71+
results.Add( child.Value );
72+
}
73+
74+
return reverse ? results.EnumerateReverse() : results;
75+
}
76+
}
77+
78+
return [];
79+
}
80+
81+
public IEnumerable<(JsonNode Value, string Key)> GetChildrenWithName( in JsonNode value, ChildEnumerationOptions options )
82+
{
83+
bool complexTypesOnly = options.HasFlag( ChildEnumerationOptions.ComplexTypesOnly );
84+
bool reverse = options.HasFlag( ChildEnumerationOptions.Reverse );
85+
86+
// allocating is faster than using yield return and less memory intensive.
87+
// using a collection results in fewer overall allocations than calling
88+
// LINQ reverse, which internally allocates, and then discards, a new array.
89+
90+
List<(JsonNode, string)> results;
3691

3792
switch ( value )
3893
{
3994
case JsonArray jsonArray:
4095
{
4196
var length = jsonArray.Count;
42-
var results = new Stack<(JsonNode, string)>( length ); // stack will reverse items
97+
results = new List<(JsonNode, string)>( length );
4398

4499
for ( var index = 0; index < length; index++ )
45100
{
@@ -48,24 +103,24 @@ public bool DeepEquals( JsonNode left, JsonNode right ) =>
48103
if ( complexTypesOnly && child is not (JsonArray or JsonObject) )
49104
continue;
50105

51-
results.Push( (child, IndexHelper.GetIndexString( index )) );
106+
results.Add( (child, IndexHelper.GetIndexString( index )) );
52107
}
53108

54-
return results;
109+
return reverse ? results.EnumerateReverse() : results;
55110
}
56111
case JsonObject jsonObject:
57112
{
58-
var results = new Stack<(JsonNode, string)>(); // stack will reverse items
113+
results = new List<(JsonNode, string)>( 8 );
59114

60115
foreach ( var child in jsonObject )
61116
{
62117
if ( complexTypesOnly && child.Value is not (JsonArray or JsonObject) )
63118
continue;
64119

65-
results.Push( (child.Value, child.Key) );
120+
results.Add( (child.Value, child.Key) );
66121
}
67122

68-
return results;
123+
return reverse ? results.EnumerateReverse() : results;
69124
}
70125
}
71126

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace Hyperbee.Json.Extensions;
2+
3+
public static class ListExtensions
4+
{
5+
internal static IEnumerable<T> EnumerateReverse<T>( this IList<T> list )
6+
{
7+
for ( var i = list.Count - 1; i >= 0; i-- )
8+
yield return list[i];
9+
}
10+
}

src/Hyperbee.Json/Path/Filters/Parser/Expressions/JsonExpressionFactory.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ private static bool TryParseNode<TNode>( INodeActions<TNode> actions, ReadOnlySp
4444
private static void ConvertToDoubleQuotes( ref Span<byte> buffer, int length )
4545
{
4646
var insideString = false;
47+
4748
for ( var i = 0; i < length; i++ )
4849
{
4950
if ( buffer[i] == (byte) '\"' )

0 commit comments

Comments
 (0)