Skip to content

Commit 52fac05

Browse files
committed
handle loading using reflection nicer
1 parent 0c487f3 commit 52fac05

File tree

4 files changed

+91
-32
lines changed

4 files changed

+91
-32
lines changed

Codecs/Binary.cs

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ namespace Datamodel.Codecs
1616
[CodecFormat("binary", 4)]
1717
[CodecFormat("binary", 5)]
1818
[CodecFormat("binary", 9)]
19-
class Binary : ICodec
19+
class Binary : IDeferredAttributeCodec
2020
{
2121
static readonly Dictionary<int, Type?[]> SupportedAttributes = [];
22+
BinaryReader? Reader;
2223

2324
/// <summary>
2425
/// The number of Datamodel binary ticks in one second. Used to store TimeSpan values.
@@ -348,32 +349,32 @@ public Datamodel Decode(string encoding, int encoding_version, string format, in
348349
var dm = new Datamodel(format, format_version);
349350

350351
EncodingVersion = encoding_version;
351-
BinaryReader reader = new BinaryReader(stream, Datamodel.TextEncoding);
352+
Reader = new BinaryReader(stream, Datamodel.TextEncoding);
352353

353354
if (EncodingVersion >= 9)
354355
{
355356
// Read prefix elements
356-
foreach (int prefix_elem in Enumerable.Range(0, reader.ReadInt32()))
357+
foreach (int prefix_elem in Enumerable.Range(0, Reader.ReadInt32()))
357358
{
358-
foreach (int attr_index in Enumerable.Range(0, reader.ReadInt32()))
359+
foreach (int attr_index in Enumerable.Range(0, Reader.ReadInt32()))
359360
{
360-
var name = ReadString_Raw(reader);
361-
var value = DecodeAttribute(dm, true, reader);
361+
var name = ReadString_Raw(Reader);
362+
var value = DecodeAttribute(dm, true, Reader);
362363
if (prefix_elem == 0) // skip subsequent elements...are they considered "old versions"?
363364
dm.PrefixAttributes[name] = value;
364365
}
365366
}
366367
}
367368

368-
StringDict = new StringDictionary(this, reader);
369-
var num_elements = reader.ReadInt32();
369+
StringDict = new StringDictionary(this, Reader);
370+
var num_elements = Reader.ReadInt32();
370371

371372
// read index
372373
foreach (var i in Enumerable.Range(0, num_elements))
373374
{
374-
var type = StringDict.ReadString(reader);
375-
var name = EncodingVersion >= 4 ? StringDict.ReadString(reader) : ReadString_Raw(reader);
376-
var id_bits = reader.ReadBytes(16);
375+
var type = StringDict.ReadString(Reader);
376+
var name = EncodingVersion >= 4 ? StringDict.ReadString(Reader) : ReadString_Raw(Reader);
377+
var id_bits = Reader.ReadBytes(16);
377378
var id = new Guid(BitConverter.IsLittleEndian ? id_bits : id_bits.Reverse().ToArray());
378379

379380
Element? elem = null;
@@ -417,19 +418,19 @@ public Datamodel Decode(string encoding, int encoding_version, string format, in
417418
// assert if stub
418419
Debug.Assert(!elem.Stub);
419420

420-
var num_attrs = reader.ReadInt32();
421+
var num_attrs = Reader.ReadInt32();
421422

422423
foreach (var i in Enumerable.Range(0, num_attrs))
423424
{
424-
var name = StringDict.ReadString(reader);
425+
var name = StringDict.ReadString(Reader);
425426
if (defer_mode == DeferredMode.Automatic && reflectionParams.AttemptReflection == false)
426427
{
427-
CodecUtilities.AddDeferredAttribute(elem, name, reader.BaseStream.Position);
428-
SkipAttribute(reader);
428+
CodecUtilities.AddDeferredAttribute(elem, name, Reader.BaseStream.Position);
429+
SkipAttribute(Reader);
429430
}
430431
else
431432
{
432-
elem.Add(name, DecodeAttribute(dm, false, reader));
433+
elem.Add(name, DecodeAttribute(dm, false, Reader));
433434
}
434435
}
435436
}
@@ -439,10 +440,15 @@ public Datamodel Decode(string encoding, int encoding_version, string format, in
439440

440441
int EncodingVersion;
441442

442-
public object? DeferredDecodeAttribute(Datamodel dm, long offset, BinaryReader reader)
443+
public object? DeferredDecodeAttribute(Datamodel dm, long offset)
443444
{
444-
reader.BaseStream.Seek(offset, SeekOrigin.Begin);
445-
return DecodeAttribute(dm, false, reader);
445+
if(Reader is null)
446+
{
447+
throw new InvalidDataException("Tried to read a deferred attribute but the reader is invalid");
448+
}
449+
450+
Reader.BaseStream.Seek(offset, SeekOrigin.Begin);
451+
return DecodeAttribute(dm, false, Reader);
446452
}
447453

448454
object? DecodeAttribute(Datamodel dm, bool prefix, BinaryReader reader)

Datamodel.cs

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -239,32 +239,76 @@ public void Save(string path, string encoding, int encoding_version)
239239
/// </summary>
240240
/// <param name="stream">The input Stream.</param>
241241
/// <param name="defer_mode">How to handle deferred loading.</param>
242-
public static Datamodel Load(Stream stream, DeferredMode defer_mode = DeferredMode.Automatic, ReflectionParams? reflectionParams = null)
242+
public static Datamodel Load(Stream stream, DeferredMode defer_mode = DeferredMode.Automatic)
243243
{
244-
return Load_Internal(stream, Assembly.GetCallingAssembly(), defer_mode, reflectionParams);
244+
return Load_Internal<Element>(stream, Assembly.GetCallingAssembly(), defer_mode, null);
245245
}
246246
/// <summary>
247-
/// Loads a Datamodel from a byte array.
247+
/// Loads a Datamodel from a <see cref="Stream"/>.
248248
/// </summary>
249249
/// <param name="stream">The input Stream.</param>
250250
/// <param name="defer_mode">How to handle deferred loading.</param>
251-
public static Datamodel Load(byte[] data, DeferredMode defer_mode = DeferredMode.Automatic, ReflectionParams? reflectionParams = null)
251+
/// <typeparam name="T">Type hint for what the Root of this datamodel should be when using reflection</param>
252+
public static Datamodel Load<T>(Stream stream, DeferredMode defer_mode = DeferredMode.Automatic, ReflectionParams? reflectionParams = null)
253+
where T : Element
252254
{
253-
return Load_Internal(new MemoryStream(data, true), Assembly.GetCallingAssembly(), defer_mode, reflectionParams);
255+
return Load_Internal<T>(stream, Assembly.GetCallingAssembly(), defer_mode, reflectionParams);
256+
}
257+
258+
/// <summary>
259+
/// Loads a Datamodel from a byte array.
260+
/// </summary>
261+
/// <param name="data">The input byte array.</param>
262+
/// <param name="defer_mode">How to handle deferred loading.</param>
263+
public static Datamodel Load(byte[] data, DeferredMode defer_mode = DeferredMode.Automatic)
264+
{
265+
return Load_Internal<Element>(new MemoryStream(data, true), Assembly.GetCallingAssembly(), defer_mode);
266+
}
267+
/// <summary>
268+
/// Loads a Datamodel from a byte array.
269+
/// </summary>
270+
/// <param name="data">The input byte array.</param>
271+
/// <param name="defer_mode">How to handle deferred loading.</param>
272+
/// <typeparam name="T">Type hint for what the Root of this datamodel should be when using reflection</param>
273+
public static Datamodel Load<T>(byte[] data, DeferredMode defer_mode = DeferredMode.Automatic, ReflectionParams? reflectionParams = null)
274+
where T : Element
275+
{
276+
return Load_Internal<T>(new MemoryStream(data, true), Assembly.GetCallingAssembly(), defer_mode, reflectionParams);
254277
}
255278

256279
/// <summary>
257280
/// Loads a Datamodel from a file path.
258281
/// </summary>
259282
/// <param name="path">The source file path.</param>
260283
/// <param name="defer_mode">How to handle deferred loading.</param>
261-
public static Datamodel Load(string path, DeferredMode defer_mode = DeferredMode.Automatic, ReflectionParams? reflectionParams = null)
284+
public static Datamodel Load(string path, DeferredMode defer_mode = DeferredMode.Automatic)
262285
{
263286
var stream = File.OpenRead(path);
264287
Datamodel? dm = null;
265288
try
266289
{
267-
dm = Load_Internal(stream, Assembly.GetCallingAssembly(), defer_mode, reflectionParams);
290+
dm = Load_Internal<Element>(stream, Assembly.GetCallingAssembly(), defer_mode);
291+
return dm;
292+
}
293+
finally
294+
{
295+
if (defer_mode == DeferredMode.Disabled || (dm != null && dm.Codec == null)) stream.Dispose();
296+
}
297+
}
298+
/// <summary>
299+
/// Loads a Datamodel from a file path.
300+
/// </summary>
301+
/// <param name="path">The source file path.</param>
302+
/// <param name="defer_mode">How to handle deferred loading.</param>
303+
/// <typeparam name="T">Type hint for what the Root of this datamodel should be when using reflection</param>
304+
public static Datamodel Load<T>(string path, DeferredMode defer_mode = DeferredMode.Automatic, ReflectionParams? reflectionParams = null)
305+
where T : Element
306+
{
307+
var stream = File.OpenRead(path);
308+
Datamodel? dm = null;
309+
try
310+
{
311+
dm = Load_Internal<T>(stream, Assembly.GetCallingAssembly(), defer_mode, reflectionParams);
268312
return dm;
269313
}
270314
finally
@@ -273,9 +317,16 @@ public static Datamodel Load(string path, DeferredMode defer_mode = DeferredMode
273317
}
274318
}
275319

276-
private static Datamodel Load_Internal(Stream stream, Assembly callingAssembly, DeferredMode defer_mode = DeferredMode.Automatic, ReflectionParams? reflectionParams = null)
320+
private static Datamodel Load_Internal<T>(Stream stream, Assembly callingAssembly, DeferredMode defer_mode = DeferredMode.Automatic, ReflectionParams? reflectionParams = null)
321+
where T : Element
277322
{
278-
reflectionParams ??= new();
323+
reflectionParams ??= new ();
324+
325+
if(typeof(T) == typeof(Element))
326+
{
327+
reflectionParams.AttemptReflection = false;
328+
}
329+
279330
reflectionParams.AssembliesToSearch.Add(callingAssembly);
280331

281332
stream.Seek(0, SeekOrigin.Begin);
@@ -316,6 +367,8 @@ private static Datamodel Load_Internal(Stream stream, Assembly callingAssembly,
316367
dm.Encoding = encoding;
317368
dm.EncodingVersion = encoding_version;
318369

370+
dm.Root = (T?)dm.Root;
371+
319372
return dm;
320373
}
321374

ICodec.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public interface IDeferredAttributeCodec : ICodec
6565
/// <param name="dm">The <see cref="Datamodel"/> to which the Attribute belongs.</param>
6666
/// <param name="offset">The offset at which the Attribute begins in the source <see cref="Stream"/>.</param>
6767
/// <returns>The Attribute's value.</returns>
68-
object DeferredDecodeAttribute(Datamodel dm, long offset);
68+
object? DeferredDecodeAttribute(Datamodel dm, long offset);
6969
}
7070

7171
/// <summary>

Tests/Tests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -325,14 +325,14 @@ private void Test_Vmap_Reflection(Datamodel.Datamodel unserialisedVmap)
325325
[Test]
326326
public void LoadVmap_Reflection_Binary()
327327
{
328-
var unserialisedVmap = DM.Load(Path.Combine(TestContext.CurrentContext.TestDirectory, "Resources", "cs2_map.vmap"));
328+
var unserialisedVmap = DM.Load<CMapRootElement>(Path.Combine(TestContext.CurrentContext.TestDirectory, "Resources", "cs2_map.vmap"));
329329
Test_Vmap_Reflection(unserialisedVmap);
330330
}
331331

332332
[Test]
333333
public void LoadVmap_Reflection_Text()
334334
{
335-
var unserialisedVmap = DM.Load(Path.Combine(TestContext.CurrentContext.TestDirectory, "Resources", "cs2_map.vmap.txt"));
335+
var unserialisedVmap = DM.Load<CMapRootElement>(Path.Combine(TestContext.CurrentContext.TestDirectory, "Resources", "cs2_map.vmap.txt"));
336336
Test_Vmap_Reflection(unserialisedVmap);
337337
}
338338

@@ -548,7 +548,7 @@ void Get_TF2(Datamodel.Datamodel dm)
548548
[Test]
549549
public void Dota2_Binary_9()
550550
{
551-
var dm = DM.Load(Binary_9_File);
551+
var dm = DM.Load<Element>(Binary_9_File);
552552
PrintContents(dm);
553553
dm.Root.Get<Element>("skeleton").GetArray<Element>("children")[0].Any();
554554
SaveAndConvert(dm, "binary", 9);

0 commit comments

Comments
 (0)