1+ using System . Diagnostics ;
2+ using System . Reflection ;
3+ using System . Xml . Linq ;
4+
5+ namespace DevDecoder . DynamicXml . Benchmarks ;
6+
7+ public static class XmlArticles
8+ {
9+ public static readonly XDocument EmptyRoot = new XDocument ( new XElement ( "Root" ) ) ;
10+ public static readonly XDocument Simple = XDocument . Parse ( "<Root><Node attribute=\" value\" /></Root>" ) ;
11+ public static XDocument Sample => LazySample . Value ;
12+ public static XDocument Large => LazyLarge . Value ;
13+
14+ public static IEnumerable < XDocument > AllDocuments ( )
15+ {
16+ yield return EmptyRoot ;
17+ yield return Simple ;
18+ yield return Sample ;
19+ yield return Large ;
20+ }
21+
22+ private static readonly Lazy < XDocument > LazySample =
23+ new Lazy < XDocument > ( ( ) =>
24+ {
25+ using var stream = Assembly . GetExecutingAssembly ( )
26+ . GetManifestResourceStream ( $ "{ typeof ( XmlArticles ) . Namespace } .Sample.xml") ! ;
27+ return XDocument . Load ( stream ) ;
28+ } , LazyThreadSafetyMode . ExecutionAndPublication ) ;
29+
30+
31+ private static readonly Lazy < XDocument > LazyLarge =
32+ new Lazy < XDocument > ( ( ) =>
33+ {
34+ // Total nodes = (width^(depth - 1)) + (width^(depth - 2)) + (width^(depth - 3)) + ... + (width^0))
35+ // = 2,097,152 + 262,144 + 32,768 + 4,096 + 512 + 64 + 8 + 1
36+ // = 2,396,745 nodes
37+ const int width = 8 ; // 26 is maximum width (as converted to ASCII 'z').
38+ const int depth = 8 ;
39+
40+ var xDoc = new XDocument ( ) ;
41+
42+ // Build large Xml document
43+ var current = new XElement ( "X" ) ;
44+ xDoc . Add ( current ) ;
45+ var count = 1 ;
46+ var stopwatch = Stopwatch . StartNew ( ) ;
47+ while ( true )
48+ {
49+ int childCount ;
50+ if ( current . Name . LocalName . Length >= depth || ( childCount = current . Elements ( ) . Count ( ) ) >= width )
51+ {
52+ current = current . Parent ;
53+ if ( current is null ) break ;
54+ continue ;
55+ }
56+
57+ var el = new XElement ( XName . Get ( current . Name . LocalName + ( char ) ( 97 + childCount ) ,
58+ current . Name . NamespaceName ) ) ;
59+ current . Add ( el ) ;
60+ count ++ ;
61+ current = el ;
62+ }
63+
64+ Console . WriteLine ( $ "{ count } nodes in { stopwatch . ElapsedMilliseconds } ms") ;
65+ return xDoc ;
66+ } , LazyThreadSafetyMode . ExecutionAndPublication ) ;
67+
68+ public static XObject RandomObject ( this XDocument document , Random ? random = null )
69+ {
70+ random ??= new Random ( ) ;
71+ var node = RandomElement ( document . DescendantNodes ( ) , random ) ;
72+ switch ( node )
73+ {
74+ case XElement xElement :
75+ var attributeCount = xElement . Attributes ( ) . Count ( ) ;
76+ if ( attributeCount > 0 && random . Next ( 2 ) > 0 )
77+ {
78+ return xElement . Attributes ( ) . ElementAt ( random . Next ( attributeCount ) ) ;
79+ }
80+
81+ return xElement ;
82+ default :
83+ return node ;
84+ }
85+ }
86+
87+ public static XNode RandomNode ( this XDocument document , Random ? random = null ) => RandomElement ( document . DescendantNodes ( ) , random ) ;
88+
89+ public static XElement RandomElement ( this XDocument document , Random ? random = null ) => RandomElement ( document . Descendants ( ) , random ) ;
90+
91+ public static T RandomElement < T > ( this IEnumerable < T > enumerable , Random ? random = null )
92+ {
93+ // ReSharper disable PossibleMultipleEnumeration
94+ var count = enumerable . Count ( ) ;
95+ if ( count < 1 ) throw new ArgumentException ( "No items in enumeration" , nameof ( enumerable ) ) ;
96+ random ??= new Random ( ) ;
97+ return enumerable . ElementAt ( random . Next ( count ) ) ;
98+ // ReSharper restore PossibleMultipleEnumeration
99+ }
100+
101+ public static string Describe ( this XObject xObject ) => xObject switch
102+ {
103+ XAttribute attribute => $ "{ attribute . Name } =\" { attribute . Value } \" ",
104+ XComment comment => $ "<!--{ comment . Value } -->",
105+ XDocument document => "<DOCUMENT>" ,
106+ XElement element => $ "<{ element . Name } >",
107+ XProcessingInstruction processingInstruction => $ "<?{ processingInstruction . Target } >",
108+ XText text => text . Value ,
109+ XDocumentType documentType => $ "<!{ documentType . Name } >",
110+ _ => $ "<{ xObject . GetType ( ) } >"
111+ } ;
112+ }
0 commit comments