From 627a262f12257458b0bbae5e250ca349c03d6863 Mon Sep 17 00:00:00 2001 From: Milkitic Date: Fri, 10 Oct 2025 09:43:07 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat(ValueReadResult):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?ToDebuggerDisplay=E5=B1=9E=E6=80=A7=E4=BB=A5=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E8=B0=83=E8=AF=95=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/KbinXml.Net/Internal/ValueReadResult.cs | 52 ++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/src/KbinXml.Net/Internal/ValueReadResult.cs b/src/KbinXml.Net/Internal/ValueReadResult.cs index e0795c3..d0bfc70 100644 --- a/src/KbinXml.Net/Internal/ValueReadResult.cs +++ b/src/KbinXml.Net/Internal/ValueReadResult.cs @@ -1,5 +1,8 @@ -namespace KbinXml.Net.Internal; +using System.Diagnostics; +namespace KbinXml.Net.Internal; + +[DebuggerDisplay("{ToDebuggerDisplay}")] internal readonly ref struct ValueReadResult { public readonly T Value; @@ -18,4 +21,51 @@ public ValueReadResult(T value ReadStatus = readStatus; #endif } + + public string ToDebuggerDisplay + { + get + { + if (Value is byte b) + return b.ToString("X2"); + if (Value is short @short) + { + return @short.ToString("X4"); + } + if (Value is int @int) + { + return @int.ToString("X8"); + } + if (Value is long @long) + { + return @long.ToString("X16"); + } + if (Value is sbyte sb) + { + return sb.ToString("X2"); + } + if (Value is ushort us) + { + return us.ToString("X4"); + } + if (Value is uint ui) + { + return ui.ToString("X8"); + } + if (Value is ulong ul) + { + return ul.ToString("X16"); + } + if (Value is float f) + { + return f.ToString("F"); + } + if (Value is double d) + { + return d.ToString("F"); + } + + return Value.ToString(); + } + } } \ No newline at end of file From 03a39b7fdd5d9aa0bced89a6e5adfbccc2f03a88 Mon Sep 17 00:00:00 2001 From: Milkitic Date: Fri, 10 Oct 2025 09:52:20 +0800 Subject: [PATCH 2/3] =?UTF-8?q?refactor:=20=E6=B8=85=E7=90=86=E6=9C=AA?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E7=9A=84using=E8=AF=AD=E5=8F=A5=E5=92=8C?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit style: 统一代码格式和换行符 --- .../ReadBenchmark/SingleThreadComparison.cs | 2 +- .../V2Benchmarks/MultiThreadUtils.cs | 6 +- .../Internal/Providers/XDocumentProvider.cs | 1 - .../Internal/Providers/XmlDocumentProvider.cs | 1 - .../Internal/Providers/XmlWriterProvider.cs | 1 - .../TypeConverters/DummyBinConverter.cs | 2 +- .../TypeConverters/DummyStrConverter.cs | 2 +- .../Internal/TypeConverters/FloatConverter.cs | 2 +- .../Internal/TypeConverters/S16Converter.cs | 2 +- .../Internal/TypeConverters/S64Converter.cs | 2 +- .../Internal/TypeConverters/U8Converter.cs | 2 +- .../Internal/Writers/DataWriter.Base.cs | 3 +- src/KbinXml.Net/WriteOptions.cs | 4 +- src/Tests/GeneralUnitTests/DataTypeTests.cs | 80 +++++----- src/Tests/GeneralUnitTests/DebugUnitTest.cs | 89 +++++------ src/Tests/GeneralUnitTests/EdgeCaseTests.cs | 146 +++++++++--------- src/Tests/GeneralUnitTests/EncodingTests.cs | 3 +- src/Tests/GeneralUnitTests/SixbitTests.cs | 5 +- .../GeneralUnitTests/SpecialStructureTests.cs | 144 +++++++++-------- 19 files changed, 239 insertions(+), 258 deletions(-) diff --git a/src/Benchmarks/ReadBenchmark/SingleThreadComparison.cs b/src/Benchmarks/ReadBenchmark/SingleThreadComparison.cs index 445c599..d491568 100644 --- a/src/Benchmarks/ReadBenchmark/SingleThreadComparison.cs +++ b/src/Benchmarks/ReadBenchmark/SingleThreadComparison.cs @@ -31,7 +31,7 @@ public void Setup() _xml = KbinConverter.ReadXml(_kbin); _xmlStr = _linq.ToString(); } - + [Benchmark(Baseline = true)] public object? ReadRawStream() { diff --git a/src/Benchmarks/V2Benchmarks/MultiThreadUtils.cs b/src/Benchmarks/V2Benchmarks/MultiThreadUtils.cs index 55bd766..ae4aa49 100644 --- a/src/Benchmarks/V2Benchmarks/MultiThreadUtils.cs +++ b/src/Benchmarks/V2Benchmarks/MultiThreadUtils.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Threading; - -namespace ReadBenchmark +namespace ReadBenchmark { internal static class MultiThreadUtils { diff --git a/src/KbinXml.Net/Internal/Providers/XDocumentProvider.cs b/src/KbinXml.Net/Internal/Providers/XDocumentProvider.cs index b15fb38..fb3caf0 100644 --- a/src/KbinXml.Net/Internal/Providers/XDocumentProvider.cs +++ b/src/KbinXml.Net/Internal/Providers/XDocumentProvider.cs @@ -3,7 +3,6 @@ using System.Runtime.CompilerServices; using System.Text; using System.Xml.Linq; -using KbinXml.Net.Utils; namespace KbinXml.Net.Internal.Providers; diff --git a/src/KbinXml.Net/Internal/Providers/XmlDocumentProvider.cs b/src/KbinXml.Net/Internal/Providers/XmlDocumentProvider.cs index ab2b992..dfc6be3 100644 --- a/src/KbinXml.Net/Internal/Providers/XmlDocumentProvider.cs +++ b/src/KbinXml.Net/Internal/Providers/XmlDocumentProvider.cs @@ -3,7 +3,6 @@ using System.Runtime.CompilerServices; using System.Text; using System.Xml; -using KbinXml.Net.Utils; namespace KbinXml.Net.Internal.Providers; diff --git a/src/KbinXml.Net/Internal/Providers/XmlWriterProvider.cs b/src/KbinXml.Net/Internal/Providers/XmlWriterProvider.cs index cb4ab09..1362426 100644 --- a/src/KbinXml.Net/Internal/Providers/XmlWriterProvider.cs +++ b/src/KbinXml.Net/Internal/Providers/XmlWriterProvider.cs @@ -2,7 +2,6 @@ using System.Runtime.CompilerServices; using System.Text; using System.Xml; -using KbinXml.Net.Utils; namespace KbinXml.Net.Internal.Providers; diff --git a/src/KbinXml.Net/Internal/TypeConverters/DummyBinConverter.cs b/src/KbinXml.Net/Internal/TypeConverters/DummyBinConverter.cs index 976001e..0082b60 100644 --- a/src/KbinXml.Net/Internal/TypeConverters/DummyBinConverter.cs +++ b/src/KbinXml.Net/Internal/TypeConverters/DummyBinConverter.cs @@ -17,7 +17,7 @@ public int WriteString(ref ValueListBuilder builder, ReadOnlySpan st { throw new NotSupportedException("Binary data should not be written as string."); } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ToString(ReadOnlySpan span) { diff --git a/src/KbinXml.Net/Internal/TypeConverters/DummyStrConverter.cs b/src/KbinXml.Net/Internal/TypeConverters/DummyStrConverter.cs index af8e79d..14feade 100644 --- a/src/KbinXml.Net/Internal/TypeConverters/DummyStrConverter.cs +++ b/src/KbinXml.Net/Internal/TypeConverters/DummyStrConverter.cs @@ -17,7 +17,7 @@ public int WriteString(ref ValueListBuilder builder, ReadOnlySpan st { throw new NotSupportedException("String data should not be written as string."); } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ToString(ReadOnlySpan span) { diff --git a/src/KbinXml.Net/Internal/TypeConverters/FloatConverter.cs b/src/KbinXml.Net/Internal/TypeConverters/FloatConverter.cs index f6405b2..a4bf86f 100644 --- a/src/KbinXml.Net/Internal/TypeConverters/FloatConverter.cs +++ b/src/KbinXml.Net/Internal/TypeConverters/FloatConverter.cs @@ -18,7 +18,7 @@ public int WriteString(ref ValueListBuilder builder, ReadOnlySpan st return BitConverterHelper.WriteBeBytes(ref builder, ParseHelper.ParseSingle(str, ConvertHelper.UsNumberFormat)); // 返回 4(大端字节序写入 4 个字节) } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ToString(ReadOnlySpan span) { diff --git a/src/KbinXml.Net/Internal/TypeConverters/S16Converter.cs b/src/KbinXml.Net/Internal/TypeConverters/S16Converter.cs index 8284a41..331f980 100644 --- a/src/KbinXml.Net/Internal/TypeConverters/S16Converter.cs +++ b/src/KbinXml.Net/Internal/TypeConverters/S16Converter.cs @@ -18,7 +18,7 @@ public int WriteString(ref ValueListBuilder builder, ReadOnlySpan st return BitConverterHelper.WriteBeBytes(ref builder, ParseHelper.ParseInt16(str)); // 返回 2(大端字节序写入 2 个字节) } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ToString(ReadOnlySpan span) { diff --git a/src/KbinXml.Net/Internal/TypeConverters/S64Converter.cs b/src/KbinXml.Net/Internal/TypeConverters/S64Converter.cs index a6d6bd4..75315cd 100644 --- a/src/KbinXml.Net/Internal/TypeConverters/S64Converter.cs +++ b/src/KbinXml.Net/Internal/TypeConverters/S64Converter.cs @@ -18,7 +18,7 @@ public int WriteString(ref ValueListBuilder builder, ReadOnlySpan st return BitConverterHelper.WriteBeBytes(ref builder, ParseHelper.ParseInt64(str)); // 返回 8(大端字节序写入 8 个字节) } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ToString(ReadOnlySpan span) { diff --git a/src/KbinXml.Net/Internal/TypeConverters/U8Converter.cs b/src/KbinXml.Net/Internal/TypeConverters/U8Converter.cs index 8ba6a4d..937958f 100644 --- a/src/KbinXml.Net/Internal/TypeConverters/U8Converter.cs +++ b/src/KbinXml.Net/Internal/TypeConverters/U8Converter.cs @@ -19,7 +19,7 @@ public int WriteString(ref ValueListBuilder builder, ReadOnlySpan st builder.Append(ParseHelper.ParseByte(str, numberStyle)); return 1; // 写入 1 个字节 } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ToString(ReadOnlySpan span) { diff --git a/src/KbinXml.Net/Internal/Writers/DataWriter.Base.cs b/src/KbinXml.Net/Internal/Writers/DataWriter.Base.cs index 70c54b7..4194989 100644 --- a/src/KbinXml.Net/Internal/Writers/DataWriter.Base.cs +++ b/src/KbinXml.Net/Internal/Writers/DataWriter.Base.cs @@ -1,5 +1,4 @@ -using System.Diagnostics; -using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; using KbinXml.Net.Utils; namespace KbinXml.Net.Internal.Writers; diff --git a/src/KbinXml.Net/WriteOptions.cs b/src/KbinXml.Net/WriteOptions.cs index 5082c57..9846d83 100644 --- a/src/KbinXml.Net/WriteOptions.cs +++ b/src/KbinXml.Net/WriteOptions.cs @@ -13,7 +13,7 @@ public class WriteOptions /// The default value is (strict validation enabled). /// public bool StrictMode { get; set; } = true; - + /// /// Gets or sets a value indicating whether XML output should be compressed using SixBit algorithm. /// @@ -21,7 +21,7 @@ public class WriteOptions /// The default value is (compression enabled). /// public bool Compress { get; set; } = true; - + /// /// Gets or sets the prefix used to repair invalid XML element names during serialization. (e.g.: Names which start with numbers). /// When set to a non-null value, invalid names will be prefixed with this string. diff --git a/src/Tests/GeneralUnitTests/DataTypeTests.cs b/src/Tests/GeneralUnitTests/DataTypeTests.cs index 155f4b8..a98bc67 100644 --- a/src/Tests/GeneralUnitTests/DataTypeTests.cs +++ b/src/Tests/GeneralUnitTests/DataTypeTests.cs @@ -34,7 +34,7 @@ public void NumericTypeU8_ConversionIsCorrect(string type, string value, byte ex { TestNumericTypeConversion(type, value); } - + [Theory] [InlineData("s8", "0", 0)] [InlineData("s8", "-128", -128)] @@ -43,7 +43,7 @@ public void NumericTypeS8_ConversionIsCorrect(string type, string value, sbyte e { TestNumericTypeConversion(type, value); } - + [Theory] [InlineData("u16", "0", 0U)] [InlineData("u16", "32767", 32767U)] @@ -52,7 +52,7 @@ public void NumericTypeU16_ConversionIsCorrect(string type, string value, ushort { TestNumericTypeConversion(type, value); } - + [Theory] [InlineData("s16", "0", 0)] [InlineData("s16", "-32768", -32768)] @@ -61,7 +61,7 @@ public void NumericTypeS16_ConversionIsCorrect(string type, string value, short { TestNumericTypeConversion(type, value); } - + [Theory] [InlineData("u32", "0", 0U)] [InlineData("u32", "2147483647", 2147483647U)] @@ -70,7 +70,7 @@ public void NumericTypeU32_ConversionIsCorrect(string type, string value, uint e { TestNumericTypeConversion(type, value); } - + [Theory] [InlineData("s32", "0", 0)] [InlineData("s32", "-2147483648", -2147483648)] @@ -79,7 +79,7 @@ public void NumericTypeS32_ConversionIsCorrect(string type, string value, int ex { TestNumericTypeConversion(type, value); } - + [Theory] [InlineData("u64", "0", "0")] [InlineData("u64", "9223372036854775807", "9223372036854775807")] @@ -88,7 +88,7 @@ public void NumericTypeU64_ConversionIsCorrect(string type, string value, string { TestNumericTypeConversion(type, value); } - + [Theory] [InlineData("s64", "0", "0")] [InlineData("s64", "-9223372036854775808", "-9223372036854775808")] @@ -102,14 +102,14 @@ private void TestNumericTypeConversion(string type, string value) { // Prepare XML var xml = $"{value}"; - + // Convert to Kbin and return var kbin = KbinConverter.Write(xml, KnownEncodings.UTF8); var result = KbinConverter.ReadXmlLinq(kbin); - + // Verify value is unchanged Assert.Equal(value, result.Root.Element("value").Value); - + // Verify type attribute is preserved Assert.Equal(type, result.Root.Element("value").Attribute("__type").Value); @@ -129,14 +129,14 @@ public void StringType_ConversionIsCorrect(string value) { // Prepare XML var xml = $"{value}"; - + // Convert to Kbin and return var kbin = KbinConverter.Write(xml, KnownEncodings.UTF8); var result = KbinConverter.ReadXmlLinq(kbin); - + // Verify value is unchanged Assert.Equal(value, result.Root.Element("value").Value); - + // Verify type attribute is preserved Assert.Equal("str", result.Root.Element("value").Attribute("__type").Value); @@ -155,14 +155,14 @@ public void BinaryType_ConversionIsCorrect(string hexValue, int size) { // Prepare XML var xml = $"{hexValue}"; - + // Convert to Kbin and return var kbin = KbinConverter.Write(xml, KnownEncodings.UTF8); var result = KbinConverter.ReadXmlLinq(kbin); - + // Verify value is unchanged (ignoring case) Assert.Equal(hexValue.ToUpperInvariant(), result.Root.Element("value").Value.ToUpperInvariant()); - + // Verify type attribute and size attribute are preserved Assert.Equal("bin", result.Root.Element("value").Attribute("__type").Value); Assert.Equal(size.ToString(), result.Root.Element("value").Attribute("__size").Value); @@ -182,14 +182,14 @@ public void ArrayType_ConversionIsCorrect(string type, string values, int count) { // Prepare XML var xml = $"{values}"; - + // Convert to Kbin and return var kbin = KbinConverter.Write(xml, KnownEncodings.UTF8); var result = KbinConverter.ReadXmlLinq(kbin); - + // Verify value is unchanged Assert.Equal(values, result.Root.Element("array").Value); - + // Verify type attribute and count attribute are preserved Assert.Equal(type, result.Root.Element("array").Attribute("__type").Value); Assert.Equal(count.ToString(), result.Root.Element("array").Attribute("__count").Value); @@ -206,14 +206,14 @@ public void Ip4Type_ConversionIsCorrect() { // Prepare XML var xml = "192.168.1.1"; - + // Convert to Kbin and return var kbin = KbinConverter.Write(xml, KnownEncodings.UTF8); var result = KbinConverter.ReadXmlLinq(kbin); - + // Verify value is unchanged Assert.Equal("192.168.1.1", result.Root.Element("ip").Value); - + // Verify type attribute is preserved Assert.Equal("ip4", result.Root.Element("ip").Attribute("__type").Value); @@ -229,17 +229,17 @@ public void ByteConverterTest() { byte value = 123; string valueStr = value.ToString(); - + // Test writing var builder = new ValueListBuilder(stackalloc byte[4]); int bytesWritten = U8Converter.Instance.WriteString(ref builder, valueStr.AsSpan()); - + Assert.Equal(1, bytesWritten); - + // Test reading var bytes = builder.AsSpan().ToArray(); string result = U8Converter.Instance.ToString(bytes); - + Assert.Equal(valueStr, result); } @@ -248,17 +248,17 @@ public void Int32ConverterTest() { int value = 123456789; string valueStr = value.ToString(); - + // Test writing var builder = new ValueListBuilder(stackalloc byte[8]); int bytesWritten = S32Converter.Instance.WriteString(ref builder, valueStr.AsSpan()); - + Assert.Equal(4, bytesWritten); - + // Test reading var bytes = builder.AsSpan().ToArray(); string result = S32Converter.Instance.ToString(bytes); - + Assert.Equal(valueStr, result); } @@ -266,17 +266,17 @@ public void Int32ConverterTest() public void Ip4ConverterTest() { string value = "192.168.1.1"; - + // Test writing var builder = new ValueListBuilder(stackalloc byte[4]); int bytesWritten = Ip4Converter.Instance.WriteString(ref builder, value.AsSpan()); - + Assert.Equal(4, bytesWritten); - + // Test reading var bytes = builder.AsSpan().ToArray(); string result = Ip4Converter.Instance.ToString(bytes); - + Assert.Equal(value, result); } @@ -289,17 +289,17 @@ public void InvalidValue_ThrowsException() { // Prepare invalid value XML (out of type range) var xml = "256"; - + // Verify throws exception Assert.Throws(() => KbinConverter.Write(xml, KnownEncodings.UTF8)); } - + [Fact] public void InvalidType_ThrowsException() { // Prepare invalid type XML var xml = "123"; - + // Verify throws exception Assert.Throws(() => KbinConverter.Write(xml, KnownEncodings.UTF8)); } @@ -307,7 +307,7 @@ public void InvalidType_ThrowsException() #endregion #region Primitive Type Conversion Tests - + [Theory] [ClassData(typeof(ByteTestData))] public void ByteTest(byte value) @@ -479,7 +479,7 @@ private static void DoWorks(object value, Assert.Equal(bytes, bytes2); Assert.Equal(output, output2); } - + #endregion } -} \ No newline at end of file +} \ No newline at end of file diff --git a/src/Tests/GeneralUnitTests/DebugUnitTest.cs b/src/Tests/GeneralUnitTests/DebugUnitTest.cs index a31da50..9d0eab3 100644 --- a/src/Tests/GeneralUnitTests/DebugUnitTest.cs +++ b/src/Tests/GeneralUnitTests/DebugUnitTest.cs @@ -1,12 +1,7 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Linq; -using System.IO; +using System.Text; using KbinXml.Net; -using KbinXml.Net.Internal; -using KbinXml.Net.Internal.Writers; -using KbinXml.Net.Internal.Readers; using Xunit; using Xunit.Abstractions; @@ -33,11 +28,11 @@ public void Debug_EncodingProcess() // Prepare a simple XML, add __type attribute var testXml = "テスト"; _outputHelper.WriteLine($"Original XML: {testXml}"); - + // Generate Kbin binary data var kbin = KbinConverter.Write(testXml, KnownEncodings.UTF8); _outputHelper.WriteLine($"Kbin size: {kbin.Length} bytes"); - + // Print the first 64 bytes of Kbin binary data for debugging _outputHelper.WriteLine("First 64 bytes of Kbin data:"); for (int i = 0; i < Math.Min(64, kbin.Length); i++) @@ -48,16 +43,16 @@ public void Debug_EncodingProcess() // Try reading with different encodings foreach (KnownEncodings encoding in Enum.GetValues(typeof(KnownEncodings))) { - try + try { _outputHelper.WriteLine($"\nTrying to decode with {encoding}:"); var result = KbinConverter.ReadXmlLinq(kbin); _outputHelper.WriteLine($"Decode result: {result}"); - + // Check value element's value var valueText = result.Root?.Element("value")?.Value; _outputHelper.WriteLine($"Value element value: '{valueText}'"); - + // Trace the complete XML structure _outputHelper.WriteLine("XML structure:"); foreach (var element in result.Descendants()) @@ -78,7 +73,7 @@ public void Debug_EncodingBuffers() // Test different string encoding results var testString = "テスト"; _outputHelper.WriteLine($"Test string: '{testString}'"); - + foreach (KnownEncodings encoding in Enum.GetValues(typeof(KnownEncodings))) { try @@ -92,11 +87,11 @@ public void Debug_EncodingBuffers() { _outputHelper.WriteLine($"[{i}] 0x{bytes[i]:X2}"); } - + // Decode again var decoded = encodingObj.GetString(bytes); _outputHelper.WriteLine($"After decoding: '{decoded}'"); - + // Try using UTF8 to decode var utf8Decoded = Encoding.UTF8.GetString(bytes); _outputHelper.WriteLine($"UTF8 decoding: '{utf8Decoded}'"); @@ -115,26 +110,26 @@ public void Debug_NodeWriter_WriteString() // So we only test the overall XML conversion instead of testing NodeWriter separately var testString = "テスト"; _outputHelper.WriteLine($"Test string: '{testString}'"); - + foreach (KnownEncodings knownEncoding in Enum.GetValues(typeof(KnownEncodings))) { try { _outputHelper.WriteLine($"\n===== Encoding: {knownEncoding} ====="); - + // Create XML with test string var xml = $"{testString}"; - + // Convert to Kbin var kbin = KbinConverter.Write(xml, knownEncoding); - + _outputHelper.WriteLine($"Kbin size: {kbin.Length} bytes"); _outputHelper.WriteLine("First 32 bytes of Kbin data:"); for (int i = 0; i < Math.Min(kbin.Length, 32); i++) { _outputHelper.WriteLine($"[{i,2}] 0x{kbin[i]:X2}"); } - + // Convert Kbin back to XML var resultXml = KbinConverter.ReadXmlLinq(kbin); var valueText = resultXml.Root?.Element("value")?.Value; @@ -154,26 +149,26 @@ public void Debug_DataReadWrite() // So we only test the overall XML conversion instead of testing DataReader/DataWriter separately var testString = "テスト"; _outputHelper.WriteLine($"Test string: '{testString}'"); - + foreach (KnownEncodings knownEncoding in Enum.GetValues(typeof(KnownEncodings))) { try { _outputHelper.WriteLine($"\n===== Encoding: {knownEncoding} ====="); - + // Create XML with test string var xml = $"{testString}"; - + // Convert to Kbin var kbin = KbinConverter.Write(xml, knownEncoding); - + _outputHelper.WriteLine($"Kbin size: {kbin.Length} bytes"); _outputHelper.WriteLine("First 32 bytes of Kbin data:"); for (int i = 0; i < Math.Min(kbin.Length, 32); i++) { _outputHelper.WriteLine($"[{i,2}] 0x{kbin[i]:X2}"); } - + // Convert Kbin back to XML var resultXml = KbinConverter.ReadXmlLinq(kbin); var valueText = resultXml.Root?.Element("value")?.Value; @@ -190,11 +185,11 @@ public void Debug_DataReadWrite() public void Debug_EncodingDetailTest() { var japaneseStrings = new[] { "テスト", "メインサーバー", "最初の項目", "コンテンツあり" }; - + foreach (var testString in japaneseStrings) { _outputHelper.WriteLine($"\n==== Test string: '{testString}' ===="); - + // Only use UTF8 encoding for testing, avoid using unsupported encodings foreach (KnownEncodings knownEncoding in new[] { KnownEncodings.UTF8, KnownEncodings.ASCII }) { @@ -202,27 +197,27 @@ public void Debug_EncodingDetailTest() { var encoding = knownEncoding.ToEncoding(); _outputHelper.WriteLine($"\n=== Encoding: {knownEncoding} ==="); - + // 1. Encode to byte array var bytes = encoding.GetBytes(testString); _outputHelper.WriteLine($"Encoded byte count: {bytes.Length}"); _outputHelper.WriteLine("Byte content:"); - + string bytesHex = BitConverter.ToString(bytes); _outputHelper.WriteLine($"HEX: {bytesHex}"); - + // 2. Generate XML and add __type attribute var xml = $"{testString}"; - + // 3. Convert to KBin var kbin = KbinConverter.Write(xml, knownEncoding); _outputHelper.WriteLine($"\nKbin size: {kbin.Length} bytes"); - + // 4. Convert KBin back to XML var resultXml = KbinConverter.ReadXmlLinq(kbin); var valueText = resultXml.Root?.Element("value")?.Value; _outputHelper.WriteLine($"Value after decoding: '{valueText}'"); - + // Check if it matches if (testString == valueText) { @@ -282,32 +277,32 @@ public void Debug_ComplexTypesTest() // Convert to Kbin var kbin = KbinConverter.Write(xml, KnownEncodings.UTF8); _outputHelper.WriteLine($"Kbin size: {kbin.Length} bytes"); - + // Convert Kbin back to XML var resultXml = KbinConverter.ReadXmlLinq(kbin); _outputHelper.WriteLine($"Read back XML: {resultXml}"); - + // Verify each element _outputHelper.WriteLine("\nVerify each element value:"); - + var strValue = resultXml.Root?.Element("strValue")?.Value; _outputHelper.WriteLine($"strValue: '{strValue}'"); - + var intValue = resultXml.Root?.Element("intValue")?.Value; _outputHelper.WriteLine($"intValue: '{intValue}'"); - + var floatValue = resultXml.Root?.Element("floatValue")?.Value; _outputHelper.WriteLine($"floatValue: '{floatValue}'"); - + var binValue = resultXml.Root?.Element("binValue")?.Value; _outputHelper.WriteLine($"binValue: '{binValue}'"); - + var boolValue = resultXml.Root?.Element("boolValue")?.Value; _outputHelper.WriteLine($"boolValue: '{boolValue}'"); - + var intArray = resultXml.Root?.Element("intArray")?.Value; _outputHelper.WriteLine($"intArray: '{intArray}'"); - + var strArrayItems = resultXml.Root?.Element("strArray")?.Elements("item").Select(e => e.Value).ToList(); if (strArrayItems != null) { @@ -316,10 +311,10 @@ public void Debug_ComplexTypesTest() _outputHelper.WriteLine($"strArray[{i}]: '{strArrayItems[i]}'"); } } - + var sectionTitle = resultXml.Root?.Element("section")?.Element("title")?.Value; _outputHelper.WriteLine($"section.title: '{sectionTitle}'"); - + var sectionContent = resultXml.Root?.Element("section")?.Element("content")?.Value; _outputHelper.WriteLine($"section.content: '{sectionContent}'"); } @@ -372,13 +367,13 @@ public void Debug_AllNodeTypesTest() // Convert to Kbin var kbin = KbinConverter.Write(xml, KnownEncodings.UTF8); _outputHelper.WriteLine($"Kbin size: {kbin.Length} bytes"); - + // Convert Kbin back to XML var resultXml = KbinConverter.ReadXmlLinq(kbin); - + // Verify each element _outputHelper.WriteLine("\nVerify each element value:"); - + foreach (var element in resultXml.Root.Elements()) { var typeName = element.Attribute("__type")?.Value; @@ -391,4 +386,4 @@ public void Debug_AllNodeTypesTest() } } } -} \ No newline at end of file +} \ No newline at end of file diff --git a/src/Tests/GeneralUnitTests/EdgeCaseTests.cs b/src/Tests/GeneralUnitTests/EdgeCaseTests.cs index 64d00cd..cbd4dd1 100644 --- a/src/Tests/GeneralUnitTests/EdgeCaseTests.cs +++ b/src/Tests/GeneralUnitTests/EdgeCaseTests.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.IO; using System.Linq; using System.Text; using System.Xml; @@ -25,33 +23,33 @@ public EdgeCaseTests(ITestOutputHelper outputHelper) Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); #endif } - + #region Exception Tests - + [Fact] public void ReadXmlLinq_InvalidKbin_ThrowsKbinException() { // Prepare invalid Kbin data var invalidKbin = new byte[] { 0x42, 0x43, 0x44, 0x45 }; // Non-Kbin format byte array - + // Verify exception is thrown Assert.Throws(() => KbinConverter.ReadXmlLinq(invalidKbin)); } - + [Fact] public void Write_InvalidEncoding_ThrowsArgumentOutOfRangeException() { // Prepare valid XML var xml = "テスト"; - + // Convert XML string to XmlDocument var xmlDoc = new XmlDocument(); xmlDoc.LoadXml(xml); - + // Try converting with invalid encoding Assert.Throws(() => KbinConverter.Write(xmlDoc, (KnownEncodings)999)); } - + [Fact] public void Write_NullXml_ThrowsArgumentNullException() { @@ -60,157 +58,157 @@ public void Write_NullXml_ThrowsArgumentNullException() var stringException = Assert.Throws(() => KbinConverter.Write(xml, KnownEncodings.UTF8)); Assert.IsType(stringException); Assert.Contains("xml", stringException.ParamName, StringComparison.OrdinalIgnoreCase); - + // Try converting with null XmlDocument XmlDocument xmlDoc = null; var docException = Assert.Throws(() => KbinConverter.Write(xmlDoc, KnownEncodings.UTF8)); Assert.IsType(docException); Assert.Contains("xml", docException.ParamName, StringComparison.OrdinalIgnoreCase); - + // Try converting with null XContainer XContainer xContainer = null; var containerException = Assert.Throws(() => KbinConverter.Write(xContainer, KnownEncodings.UTF8)); Assert.IsType(containerException); Assert.Contains("xml", containerException.ParamName, StringComparison.OrdinalIgnoreCase); } - + [Fact] public void Write_InvalidXmlString_ThrowsXmlException() { // Prepare invalid XML string (missing closing tag) var invalidXml = "テスト"; - + // Verify exception is thrown Assert.Throws(() => KbinConverter.Write(invalidXml, KnownEncodings.UTF8)); } - + [Fact] public void ReadXmlBytes_InvalidKbin_ThrowsKbinException() { // Prepare invalid Kbin data var invalidKbin = new byte[] { 0x42, 0x43, 0x44, 0x45 }; // Non-Kbin format byte array - + // Verify exception is thrown Assert.Throws(() => KbinConverter.ReadXmlBytes(invalidKbin)); } - + [Fact] public void GetXmlStream_InvalidKbin_ThrowsKbinException() { // Prepare invalid Kbin data var invalidKbin = new byte[] { 0x42, 0x43, 0x44, 0x45 }; // Non-Kbin format byte array - + // Verify exception is thrown Assert.Throws(() => KbinConverter.GetXmlStream(invalidKbin)); } - + [Fact] public void ReadXml_InvalidKbin_ThrowsKbinException() { // Prepare invalid Kbin data var invalidKbin = new byte[] { 0x42, 0x43, 0x44, 0x45 }; // Non-Kbin format byte array - + // Verify exception is thrown Assert.Throws(() => KbinConverter.ReadXml(invalidKbin)); } - + #endregion - + #region Boundary Condition Tests - + [Fact] public void EmptyXml_CanConvert() { // Prepare empty XML var xml = ""; - + // Convert to Kbin and return var kbin = KbinConverter.Write(xml, KnownEncodings.UTF8); var result = KbinConverter.ReadXmlLinq(kbin); - + // Verify empty XML correctly converted Assert.NotNull(result); Assert.NotNull(result.Root); Assert.Equal("root", result.Root.Name); Assert.Equal(0, result.Root.Elements().Count()); } - + [Fact] public void LargeXml_CanConvert() { // Generate large XML var largeXmlBuilder = new StringBuilder(); largeXmlBuilder.Append(""); - + for (int i = 0; i < 1000; i++) { largeXmlBuilder.Append($"{i}"); } - + largeXmlBuilder.Append(""); - + var largeXml = largeXmlBuilder.ToString(); - + // Convert to Kbin and return var kbin = KbinConverter.Write(largeXml, KnownEncodings.UTF8); var result = KbinConverter.ReadXmlLinq(kbin); - + // Verify large XML correctly converted Assert.NotNull(result); Assert.NotNull(result.Root); Assert.Equal(1000, result.Root.Elements("item").Count()); - + // Check some specific element values var items = result.Root.Elements("item").ToList(); var item0 = items.FirstOrDefault(e => e.Attribute("id")?.Value == "0"); var item999 = items.FirstOrDefault(e => e.Attribute("id")?.Value == "999"); - + Assert.NotNull(item0); Assert.NotNull(item999); Assert.Equal("0", item0.Value); Assert.Equal("999", item999.Value); } - + [Fact] public void DeepNestedXml_CanConvert() { // Generate deeply nested XML var deepXmlBuilder = new StringBuilder(); deepXmlBuilder.Append(""); - + string currentTag = ""; deepXmlBuilder.Append(currentTag); - + // Create 20 levels deep XML for (int i = 2; i <= 19; i++) { currentTag = $""; deepXmlBuilder.Append(currentTag); } - + // Add __type attribute to innermost level deepXmlBuilder.Append(""); deepXmlBuilder.Append("最深部"); deepXmlBuilder.Append(""); - + // Close all tags for (int i = 19; i >= 1; i--) { deepXmlBuilder.Append($""); } - + deepXmlBuilder.Append(""); - + var deepXml = deepXmlBuilder.ToString(); - + // Convert to Kbin and return var kbin = KbinConverter.Write(deepXml, KnownEncodings.UTF8); var result = KbinConverter.ReadXmlLinq(kbin); - + // Verify deeply nested XML correctly converted Assert.NotNull(result); Assert.NotNull(result.Root); - + // Navigate to deepest level and check value var element = result.Root.Element("level1"); for (int i = 2; i <= 20; i++) @@ -218,66 +216,66 @@ public void DeepNestedXml_CanConvert() element = element.Element($"level{i}"); Assert.NotNull(element); } - + Assert.Equal("最深部", element.Value); } - + [Fact] public void XmlWithSpecialChars_CanConvert() { // Prepare XML with special characters var xml = "特殊文字: <>&"'"; - + // Convert to Kbin and return var kbin = KbinConverter.Write(xml, KnownEncodings.UTF8); var result = KbinConverter.ReadXmlLinq(kbin); - + // Verify XML with special characters correctly converted Assert.Equal("特殊文字: <>&\"'", result.Root.Element("value").Value); } - + [Fact] public void MaxSizeArrays_CanConvert() { // Generate an array with a large number of elements var arrayBuilder = new StringBuilder(); arrayBuilder.Append(""); - + for (int i = 0; i < 1000; i++) { arrayBuilder.Append(i % 256); if (i < 999) arrayBuilder.Append(" "); } - + arrayBuilder.Append(""); - + var arrayXml = arrayBuilder.ToString(); - + // Convert to Kbin and return var kbin = KbinConverter.Write(arrayXml, KnownEncodings.UTF8); var result = KbinConverter.ReadXmlLinq(kbin); - + // Verify array information correctly var array = result.Root.Element("array"); Assert.NotNull(array); Assert.Equal("u8", array.Attribute("__type").Value); Assert.Equal("1000", array.Attribute("__count").Value); - + // Verify array content var values = array.Value.Split(' '); Assert.Equal(1000, values.Length); } - + [Fact] public void ZeroLengthData_CanConvert() { // Prepare XML with zero length data var xml = ""; - + // Convert to Kbin and return var kbin = KbinConverter.Write(xml, KnownEncodings.UTF8); var result = KbinConverter.ReadXmlLinq(kbin); - + // Verify zero length data correctly converted var bin = result.Root.Element("bin"); Assert.NotNull(bin); @@ -285,66 +283,66 @@ public void ZeroLengthData_CanConvert() Assert.Equal("0", bin.Attribute("__size").Value); Assert.Equal("", bin.Value); } - + #endregion - + #region Exception Class Tests - + [Fact] public void KbinException_DefaultConstructor_CreatesInstance() { // Call default constructor var exception = new KbinException(); - + // Verify instance is created Assert.NotNull(exception); Assert.Contains("KbinXml.Net.KbinException", exception.Message); } - + [Fact] public void KbinException_MessageConstructor_SetsMessage() { // Expected error message const string expectedMessage = "テストエラーメッセージ"; - + // Use message constructor to create exception var exception = new KbinException(expectedMessage); - + // Verify message is correctly set Assert.Equal(expectedMessage, exception.Message); } - + [Fact] public void KbinException_InnerExceptionConstructor_SetsMessageAndInnerException() { // Prepare inner exception and message const string expectedMessage = "外部例外メッセージ"; var innerException = new InvalidOperationException("内部例外メッセージ"); - + // Use message and inner exception constructor to create exception var exception = new KbinException(expectedMessage, innerException); - + // Verify message and inner exception are correctly set Assert.Equal(expectedMessage, exception.Message); Assert.Same(innerException, exception.InnerException); } - + [Fact] public void KbinException_DerivesFromException() { // Create exception instance var exception = new KbinException(); - + // Verify inherits from Exception Assert.IsAssignableFrom(exception); } - + [Fact] public void KbinTypeNotFoundException_DerivesFromKbinException() { // Create type not found exception var typeException = new KbinTypeNotFoundException("test"); - + // Verify inherits from KbinException Assert.IsAssignableFrom(typeException); } @@ -354,14 +352,14 @@ public void KbinTypeNotFoundException_IncludesTypeNameInMessage() { // Test type name const string typeName = "invalidType"; - + // Create exception var exception = new KbinTypeNotFoundException(typeName); - + // Verify type name is included in message Assert.Contains(typeName, exception.Message); } - + #endregion } -} \ No newline at end of file +} \ No newline at end of file diff --git a/src/Tests/GeneralUnitTests/EncodingTests.cs b/src/Tests/GeneralUnitTests/EncodingTests.cs index 43fe0f0..ce8daf2 100644 --- a/src/Tests/GeneralUnitTests/EncodingTests.cs +++ b/src/Tests/GeneralUnitTests/EncodingTests.cs @@ -1,10 +1,9 @@ using System; using System.Text; -using System.Xml.Linq; +using System.Xml; using KbinXml.Net; using Xunit; using Xunit.Abstractions; -using System.Xml; namespace GeneralUnitTests { diff --git a/src/Tests/GeneralUnitTests/SixbitTests.cs b/src/Tests/GeneralUnitTests/SixbitTests.cs index 27bd6e5..8cd3e33 100644 --- a/src/Tests/GeneralUnitTests/SixbitTests.cs +++ b/src/Tests/GeneralUnitTests/SixbitTests.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using KbinXml.Net.Internal.Sixbit; using Xunit; @@ -27,7 +26,7 @@ public void VerifyBitEquivalence() SixbitHelperOptimized.Encode(testData, outputOptimized); SixbitHelperSuperOptimized.Encode(testData, outputSuperOptimized); SixbitHelperCoreClrOptimized.Encode(testData, outputCoreClrOptimized); - + Assert.Equal(outputOriginal.ToArray(), outputOptimized.ToArray()); Assert.Equal(outputOriginal.ToArray(), outputSuperOptimized.ToArray()); Assert.Equal(outputOriginal.ToArray(), outputCoreClrOptimized.ToArray()); @@ -57,7 +56,7 @@ public void VerifyBitEquivalence2() SixbitHelperOptimized.Decode(output, inputOptimized); SixbitHelperSuperOptimized.Decode(output, inputSuperOptimized); SixbitHelperCoreClrOptimized.Decode(output, inputCoreClrOptimized); - + Assert.Equal(inputOriginal.ToArray(), inputOptimized.ToArray()); Assert.Equal(inputOriginal.ToArray(), inputSuperOptimized.ToArray()); Assert.Equal(inputSuperOptimized.ToArray(), inputCoreClrOptimized.ToArray()); diff --git a/src/Tests/GeneralUnitTests/SpecialStructureTests.cs b/src/Tests/GeneralUnitTests/SpecialStructureTests.cs index e8ef02b..4fc8f3d 100644 --- a/src/Tests/GeneralUnitTests/SpecialStructureTests.cs +++ b/src/Tests/GeneralUnitTests/SpecialStructureTests.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.IO; using System.Linq; using System.Text; using System.Xml; @@ -25,7 +23,7 @@ public SpecialStructureTests(ITestOutputHelper outputHelper) Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); #endif } - + [Fact] public void NestedElements_PreservesStructure() { @@ -38,18 +36,18 @@ public void NestedElements_PreservesStructure() "; - + // Convert to Kbin and return var kbin = KbinConverter.Write(xml, KnownEncodings.UTF8); var result = KbinConverter.ReadXmlLinq(kbin); - + // Verify nested structure is preserved Assert.NotNull(result.Root.Element("level1")); Assert.NotNull(result.Root.Element("level1").Element("level2")); Assert.NotNull(result.Root.Element("level1").Element("level2").Element("level3")); Assert.Equal("深層ネストテスト", result.Root.Element("level1").Element("level2").Element("level3").Value); } - + [Fact] public void AttributesInNodes_PreservesAttributes() { @@ -59,18 +57,18 @@ public void AttributesInNodes_PreservesAttributes() 値1 値2 "; - + // Convert to Kbin and return var kbin = KbinConverter.Write(xml, KnownEncodings.UTF8); var result = KbinConverter.ReadXmlLinq(kbin); - + // Verify root node attributes are preserved Assert.Equal("1.0", result.Root.Attribute("version").Value); - + // Verify child node attributes are preserved var nodes = result.Root.Elements("node"); int nodeCount = 0; - + foreach (var node in nodes) { nodeCount++; @@ -85,10 +83,10 @@ public void AttributesInNodes_PreservesAttributes() Assert.Equal("値2", node.Value); } } - + Assert.Equal(2, nodeCount); } - + [Fact] public void EmptyElements_PreservesStructure() { @@ -99,21 +97,21 @@ public void EmptyElements_PreservesStructure() コンテンツあり "; - + // Convert to Kbin and return var kbin = KbinConverter.Write(xml, KnownEncodings.UTF8); var result = KbinConverter.ReadXmlLinq(kbin); - + // Verify empty elements are preserved Assert.NotNull(result.Root.Element("empty1")); Assert.NotNull(result.Root.Element("empty2")); Assert.Equal(string.Empty, result.Root.Element("empty1").Value); Assert.Equal(string.Empty, result.Root.Element("empty2").Value); - + // Verify non-empty elements are correct Assert.Equal("コンテンツあり", result.Root.Element("notEmpty").Value); } - + [Fact] public void MixedContentElements_PreservesStructure() { @@ -122,20 +120,20 @@ public void MixedContentElements_PreservesStructure() テキスト1内部要素テキスト2 "; - + // Convert to Kbin and return var kbin = KbinConverter.Write(xml, KnownEncodings.UTF8); var result = KbinConverter.ReadXmlLinq(kbin); - + // Verify mixed content structure var mixedElement = result.Root.Element("mixed"); Assert.NotNull(mixedElement); - + // Note: Kbin XML conversion may not support preserving mixed content, but should ensure internal elements are normal Assert.NotNull(mixedElement.Element("inner")); Assert.Equal("内部要素", mixedElement.Element("inner").Value); } - + [Fact] public void ArrayWithDifferentTypes_PreservesStructure() { @@ -148,15 +146,15 @@ public void ArrayWithDifferentTypes_PreservesStructure() 30 "; - + // Convert to Kbin and return var kbin = KbinConverter.Write(xml, KnownEncodings.UTF8); var result = KbinConverter.ReadXmlLinq(kbin); - + // Verify array structure is preserved var numbers = result.Root.Element("numbers"); Assert.NotNull(numbers); - + // Verify each element's type and value var intElements = numbers.Elements("int"); var intCount = 0; @@ -167,13 +165,13 @@ public void ArrayWithDifferentTypes_PreservesStructure() Assert.True(intElem.Value == "10" || intElem.Value == "20"); } Assert.Equal(2, intCount); - + var floatElement = numbers.Element("float"); Assert.NotNull(floatElement); Assert.Equal("u32", floatElement.Attribute("__type").Value); Assert.Equal("30", floatElement.Value); } - + [Fact] public void SameNameNodes_PreservesStructure() { @@ -184,25 +182,25 @@ public void SameNameNodes_PreservesStructure() 2番目の項目 3番目の項目 "; - + // Convert to Kbin and return var kbin = KbinConverter.Write(xml, KnownEncodings.UTF8); var result = KbinConverter.ReadXmlLinq(kbin); - + // Verify same name nodes are preserved var items = result.Root.Elements("item"); var values = new[] { "最初の項目", "2番目の項目", "3番目の項目" }; int i = 0; - + foreach (var item in items) { Assert.Equal(values[i], item.Value); i++; } - + Assert.Equal(3, i); } - + [Fact] public void ReadWriteComplex_PreservesStructure() { @@ -234,65 +232,65 @@ public void ReadWriteComplex_PreservesStructure() 文書の終わり "; - + // Convert to Kbin and return var kbin = KbinConverter.Write(xml, KnownEncodings.UTF8); var result = KbinConverter.ReadXmlLinq(kbin); - + // Verify overall structure is preserved Assert.Equal("2.0", result.Root.Attribute("version").Value); - + // Verify title Assert.Equal("テスト文書", result.Root.Element("header").Element("title").Value); Assert.Equal("テストユーザー", result.Root.Element("header").Element("author").Value); Assert.Equal("2023-10-15", result.Root.Element("header").Element("date").Value); Assert.Equal("str", result.Root.Element("header").Element("date").Attribute("__type").Value); - + // Verify first section var section1 = result.Root.Element("body").Elements("section").First(s => s.Attribute("id").Value == "1"); Assert.Equal("セクション1", section1.Element("title").Value); Assert.NotNull(section1.Element("paragraph").Element("emphasis")); Assert.Equal("強調テキスト", section1.Element("paragraph").Element("emphasis").Value); - + var stats = section1.Element("stats").Elements("value"); Assert.Equal(3, stats.Count()); - + // Verify second section var section2 = result.Root.Element("body").Elements("section").First(s => s.Attribute("id").Value == "2"); Assert.Equal("セクション2", section2.Element("title").Value); Assert.Equal("これは2番目の段落です。", section2.Element("paragraph").Value); - + var list = section2.Element("list"); Assert.Equal("u8", list.Attribute("__type").Value); Assert.Equal("3", list.Attribute("__count").Value); Assert.Equal("1 2 3", list.Value); - + // Verify footer Assert.Equal("文書の終わり", result.Root.Element("footer").Element("note").Value); } #region Attributes and Arrays Tests - + [Fact] public void TestMultipleAttributes() { // Prepare XML with multiple attributes var xml = ""; - + // Convert to Kbin and return var kbin = KbinConverter.Write(xml, KnownEncodings.UTF8); var result = KbinConverter.ReadXmlLinq(kbin); - + // Verify attributes are preserved Assert.Equal("1", result.Root.Attribute("id").Value); Assert.Equal("test", result.Root.Attribute("name").Value); Assert.Equal("123", result.Root.Attribute("value").Value); - + var node = result.Root.Element("node"); Assert.NotNull(node); Assert.Equal("val1", node.Attribute("attr1").Value); Assert.Equal("val2", node.Attribute("attr2").Value); - + Assert.Equal(xml, result.ToString(SaveOptions.DisableFormatting)); } @@ -301,18 +299,18 @@ public void TestEmptyArray() { // Prepare empty array XML var xml = ""; - + // Convert to Kbin and return var kbin = KbinConverter.Write(xml, KnownEncodings.UTF8); var result = KbinConverter.ReadXmlLinq(kbin); - + // Verify array attributes var array = result.Root.Element("array"); Assert.NotNull(array); Assert.Equal("s32", array.Attribute("__type").Value); Assert.Equal("0", array.Attribute("__count").Value); Assert.Equal("", array.Value); - + Assert.Equal(xml, result.ToString(SaveOptions.DisableFormatting)); } @@ -322,23 +320,23 @@ public void TestLargeArray() // Generate large array data var values = string.Join(" ", Enumerable.Range(1, 1000)); var xml = $"{values}"; - + // Convert to Kbin and return var kbin = KbinConverter.Write(xml, KnownEncodings.UTF8); var result = KbinConverter.ReadXmlLinq(kbin); - + // Verify array attributes and content var array = result.Root.Element("array"); Assert.NotNull(array); Assert.Equal("s32", array.Attribute("__type").Value); Assert.Equal("1000", array.Attribute("__count").Value); - + // Verify array elements var resultValues = array.Value.Split(' '); Assert.Equal(1000, resultValues.Length); Assert.Equal("1", resultValues[0]); Assert.Equal("1000", resultValues[999]); - + Assert.Equal(xml, result.ToString(SaveOptions.DisableFormatting)); } @@ -351,24 +349,24 @@ public void TestMixedArrayTypes() "1000 2000 3000" + "-1 -2 -3" + ""; - + // Convert to Kbin and return var kbin = KbinConverter.Write(xml, KnownEncodings.UTF8); var result = KbinConverter.ReadXmlLinq(kbin); - + // Verify different array types Assert.Equal("s8", result.Root.Element("array1").Attribute("__type").Value); Assert.Equal("3", result.Root.Element("array1").Attribute("__count").Value); Assert.Equal("1 2 3", result.Root.Element("array1").Value); - + Assert.Equal("u16", result.Root.Element("array2").Attribute("__type").Value); Assert.Equal("3", result.Root.Element("array2").Attribute("__count").Value); Assert.Equal("1000 2000 3000", result.Root.Element("array2").Value); - + Assert.Equal("s32", result.Root.Element("array3").Attribute("__type").Value); Assert.Equal("3", result.Root.Element("array3").Attribute("__count").Value); Assert.Equal("-1 -2 -3", result.Root.Element("array3").Value); - + Assert.Equal(xml, result.ToString(SaveOptions.DisableFormatting)); } @@ -377,15 +375,15 @@ public void TestInvalidArrayCount() { // Prepare array count mismatch XML var xml = "1 2"; - + // Set strict mode options var writeOptions = new WriteOptions() { StrictMode = true }; - + // Verify throws exception - Assert.Throws(() => + Assert.Throws(() => KbinConverter.Write(xml, KnownEncodings.UTF8, writeOptions)); } @@ -394,26 +392,26 @@ public void TestInvalidArrayType() { // Prepare invalid array type XML var xml = "1"; - + // Verify throws exception - Assert.Throws(() => + Assert.Throws(() => KbinConverter.Write(xml, KnownEncodings.UTF8)); } - + #endregion - + #region Special Cases Tests - + [Fact] public void TestEmptyNode() { // Prepare XML with empty nodes var xml = ""; - + // Convert to Kbin and return var kbin = KbinConverter.Write(xml, KnownEncodings.UTF8); var result = KbinConverter.ReadXmlLinq(kbin); - + // Verify empty nodes are correctly converted Assert.NotNull(result.Root.Element("empty")); Assert.NotNull(result.Root.Element("self_closing")); @@ -431,7 +429,7 @@ public void TestSpecialCharactersAndEncoding(string xml, KnownEncodings encoding // Convert to Kbin and return var kbin = KbinConverter.Write(xml, encoding); var result = KbinConverter.ReadXmlLinq(kbin); - + // Verify special characters and encoding are correctly processed var xmlElement = XElement.Parse(xml); Assert.Equal(xmlElement.Element("node").Value, result.Root.Element("node").Value); @@ -444,11 +442,11 @@ public void TestDeepNestedNodes() { // Prepare deep nested XML var xml = "深層ネストテスト"; - + // Convert to Kbin and return var kbin = KbinConverter.Write(xml, KnownEncodings.UTF8); var result = KbinConverter.ReadXmlLinq(kbin); - + // Verify deep nested structure is preserved var eElement = result.Root.Element("a").Element("b").Element("c").Element("d").Element("e"); Assert.NotNull(eElement); @@ -472,11 +470,11 @@ public void TestLargeXml() // Convert to Kbin and return var kbin = KbinConverter.Write(xml, KnownEncodings.UTF8); var result = KbinConverter.ReadXmlLinq(kbin); - + // Verify large XML structure is preserved var items = result.Root.Elements("item").ToList(); Assert.Equal(100, items.Count); - + // Verify certain specific elements Assert.Equal("0", items[0].Value); Assert.Equal("0", items[0].Attribute("id").Value); @@ -491,11 +489,11 @@ public void TestInvalidXmlThrowsException() { // Prepare invalid XML var invalidXml = ""; - + // Verify throws exception Assert.Throws(() => KbinConverter.Write(invalidXml, KnownEncodings.UTF8)); } - + #endregion } -} \ No newline at end of file +} \ No newline at end of file From 5883984879be00d5c3c333f71350a9e998c4eca7 Mon Sep 17 00:00:00 2001 From: Milkitic Date: Fri, 10 Oct 2025 11:17:15 +0800 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0KBin=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E9=AA=8C=E8=AF=81=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加三个重载方法用于验证字节数组、Span和流是否为有效的KBin格式。这些方法会检查签名、编码标志和最小长度要求,并提供详细的XML文档注释说明验证逻辑。 --- src/KbinXml.Net/KbinConverter.cs | 93 ++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/src/KbinXml.Net/KbinConverter.cs b/src/KbinXml.Net/KbinConverter.cs index 51adcb3..7767f25 100644 --- a/src/KbinXml.Net/KbinConverter.cs +++ b/src/KbinXml.Net/KbinConverter.cs @@ -1,5 +1,7 @@ using System; +using System.Buffers; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Runtime.CompilerServices; using KbinXml.Net.Internal; @@ -81,4 +83,95 @@ private static bool IsDigit(char c) { return c is >= '0' and <= '9'; } + + /// + /// Determines whether the specified byte array represents a valid KBin format. + /// + /// The byte array to check. + /// + /// true if the buffer contains valid KBin format data; otherwise, false. + /// + /// + /// This method checks the KBin file signature and header structure to determine validity. + /// It verifies: + /// + /// The signature byte (0xA0) + /// The encoding flag and its inverse relationship + /// Minimum required buffer length + /// + /// + public static bool IsKbinFormat(byte[] buffer) + { + if (buffer == null) + return false; + + return IsKbinFormat(buffer.AsSpan()); + } + + /// + /// Determines whether the specified span represents a valid KBin format. + /// + /// The span to check. + /// + /// true if the buffer contains valid KBin format data; otherwise, false. + /// + /// + public static bool IsKbinFormat(ReadOnlySpan buffer) + { + // kbin format requires at least 4 bytes for the header + if (buffer.Length < 4) + return false; + + // Check signature (first byte must be 0xA0) + if (buffer[0] != 0xA0) + return false; + + // Get encoding flag and its inverse + var encodingFlag = buffer[2]; + var encodingFlagNot = buffer[3]; + + // Encoding flag should be an inverse of the fourth byte + if ((byte)~encodingFlag != encodingFlagNot) + return false; + + return true; + } + + /// + /// Determines whether the specified stream contains valid KBin format data. + /// + /// The stream to check. The stream position will be restored after checking. + /// + /// true if the stream contains valid KBin format data; otherwise, false. + /// + /// is null. + /// The stream does not support seeking. + /// + public static bool IsKbinFormat(Stream stream) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + if (!stream.CanSeek) + throw new NotSupportedException("Stream must support seeking to check KBin format."); + + var originalPosition = stream.Position; + var buffer = ArrayPool.Shared.Rent(4); + try + { + // Read the first 4 bytes for header validation + var bytesRead = stream.Read(buffer, 0, 4); + + if (bytesRead < 4) + return false; + + return IsKbinFormat(buffer.AsSpan(0, 4)); + } + finally + { + ArrayPool.Shared.Return(buffer); + // Restore original position + stream.Position = originalPosition; + } + } } \ No newline at end of file