11using System ;
22using System . Collections . Generic ;
3+ using System . Diagnostics . CodeAnalysis ;
34using System . IO ;
45using Robust . Shared . Collections ;
56using Robust . Shared . Serialization . Markdown . Mapping ;
@@ -17,22 +18,31 @@ public static class DataNodeParser
1718{
1819 public static IEnumerable < DataNodeDocument > ParseYamlStream ( TextReader reader )
1920 {
20- return ParseYamlStream ( new Parser ( reader ) ) ;
21+ return ParseYamlStream ( reader , internStrings : false ) ;
2122 }
2223
23- internal static IEnumerable < DataNodeDocument > ParseYamlStream ( Parser parser )
24+ internal static IEnumerable < DataNodeDocument > ParseYamlStream ( TextReader reader , bool internStrings )
2425 {
26+ return ParseYamlStream ( new Parser ( reader ) , internStrings ) ;
27+ }
28+
29+ internal static IEnumerable < DataNodeDocument > ParseYamlStream ( Parser parser , bool internStrings = false )
30+ {
31+ var state = new ParserState ( internStrings ) ;
32+
2533 parser . Consume < StreamStart > ( ) ;
2634
2735 while ( ! parser . TryConsume < StreamEnd > ( out _ ) )
2836 {
29- yield return ParseDocument ( parser ) ;
37+ yield return ParseDocument ( parser , state ) ;
3038 }
39+
40+ // System.Console.WriteLine(state.TotalStringsSaved);
3141 }
3242
33- private static DataNodeDocument ParseDocument ( Parser parser )
43+ private static DataNodeDocument ParseDocument ( Parser parser , ParserState parserState )
3444 {
35- var state = new DocumentState ( ) ;
45+ var state = new DocumentState ( parserState ) ;
3646
3747 parser . Consume < DocumentStart > ( ) ;
3848
@@ -78,7 +88,11 @@ private static DataNode ParseAlias(Parser parser, DocumentState state)
7888 private static ValueDataNode ParseValue ( Parser parser , DocumentState state )
7989 {
8090 var ev = parser . Consume < Scalar > ( ) ;
81- var node = new ValueDataNode ( ev ) { Tag = ConvertTag ( ev . Tag ) } ;
91+ var node = new ValueDataNode ( ev )
92+ {
93+ Tag = ConvertTag ( ev . Tag , state . ParserState ) ,
94+ Value = state . ParserState . InternString ( ev . Value )
95+ } ;
8296
8397 NodeParsed ( node , ev , false , state ) ;
8498
@@ -100,7 +114,7 @@ private static SequenceDataNode ParseSequence(Parser parser, DocumentState state
100114 var ev = parser . Consume < SequenceStart > ( ) ;
101115
102116 var node = new SequenceDataNode ( ) ;
103- node . Tag = ConvertTag ( ev . Tag ) ;
117+ node . Tag = ConvertTag ( ev . Tag , state . ParserState ) ;
104118 node . Start = ev . Start ;
105119
106120 var unresolvedAlias = false ;
@@ -127,14 +141,14 @@ private static MappingDataNode ParseMapping(Parser parser, DocumentState state)
127141 var ev = parser . Consume < MappingStart > ( ) ;
128142
129143 var node = new MappingDataNode ( ) ;
130- node . Tag = ConvertTag ( ev . Tag ) ;
144+ node . Tag = ConvertTag ( ev . Tag , state . ParserState ) ;
131145
132146 var unresolvedAlias = false ;
133147
134148 MappingEnd mapEnd ;
135149 while ( ! parser . TryConsume ( out mapEnd ) )
136150 {
137- var key = ParseKey ( parser ) ;
151+ var key = state . ParserState . InternString ( ParseKey ( parser ) ) ;
138152 var value = Parse ( parser , state ) ;
139153
140154 node . Add ( key , value ) ;
@@ -218,13 +232,14 @@ private static DataNode ResolveAlias(DataNodeAlias alias, DocumentState state)
218232 return node ;
219233 }
220234
221- private static string ConvertTag ( TagName tag )
235+ private static string ConvertTag ( TagName tag , ParserState state )
222236 {
223- return ( tag . IsNonSpecific || tag . IsEmpty ) ? null : tag . Value ;
237+ return ( tag . IsNonSpecific || tag . IsEmpty ) ? null : state . InternString ( tag . Value ) ;
224238 }
225239
226- private sealed class DocumentState
240+ private sealed class DocumentState ( ParserState parserState )
227241 {
242+ public readonly ParserState ParserState = parserState ;
228243 public readonly Dictionary < AnchorName , DataNode > Anchors = new ( ) ;
229244 public ValueList < DataNode > UnresolvedAliasOwners ;
230245 }
@@ -256,6 +271,37 @@ public override DataNode PushInheritance(DataNode parent)
256271 throw new NotSupportedException ( ) ;
257272 }
258273 }
274+
275+ #nullable enable
276+
277+ private sealed class ParserState ( bool internStrings )
278+ {
279+ public readonly HashSet < string > ? StringInternIndex = internStrings ? [ ] : null ;
280+ //public int TotalStringsSaved = 0;
281+
282+ [ return : NotNullIfNotNull ( nameof ( str ) ) ]
283+ public string ? InternString ( string ? str )
284+ {
285+ if ( StringInternIndex == null )
286+ return str ;
287+
288+ if ( str == null )
289+ return null ;
290+
291+ // Use a basic string interning system to avoid releasing a bunch of equivalent strings.
292+ // This avoids having thousands of identical strings for stuff like "type" in prototypes stored in memory.
293+ if ( StringInternIndex . TryGetValue ( str , out var indexedString ) )
294+ {
295+ // if (!ReferenceEquals(str, indexedString))
296+ // TotalStringsSaved += 1;
297+
298+ return indexedString ;
299+ }
300+
301+ StringInternIndex . Add ( str ) ;
302+ return str ;
303+ }
304+ }
259305}
260306
261307public sealed class DataParseException : Exception
0 commit comments