mirror of
https://github.com/SabreTools/SabreTools.IO.git
synced 2026-07-08 17:57:02 +00:00
Start parting out IO.Extensions a bit more
This commit is contained in:
@@ -2,7 +2,6 @@ using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Extensions.Test
|
||||
@@ -18,59 +17,6 @@ namespace SabreTools.IO.Extensions.Test
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
];
|
||||
|
||||
[Fact]
|
||||
public void ReadNullTerminatedStringTest()
|
||||
{
|
||||
// Encoding.ASCII
|
||||
byte[] bytes = [0x41, 0x42, 0x43, 0x00];
|
||||
var stream = new MemoryStream(bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
string? actual = br.ReadNullTerminatedString(Encoding.ASCII);
|
||||
Assert.Equal("ABC", actual);
|
||||
|
||||
// Encoding.Latin1
|
||||
bytes = [0x41, 0x42, 0x43, 0x00];
|
||||
stream = new MemoryStream(bytes);
|
||||
br = new BinaryReader(stream);
|
||||
actual = br.ReadNullTerminatedString(Encoding.Latin1);
|
||||
Assert.Equal("ABC", actual);
|
||||
|
||||
// Encoding.UTF8
|
||||
bytes = [0x41, 0x42, 0x43, 0x00];
|
||||
stream = new MemoryStream(bytes);
|
||||
br = new BinaryReader(stream);
|
||||
actual = br.ReadNullTerminatedString(Encoding.UTF8);
|
||||
Assert.Equal("ABC", actual);
|
||||
|
||||
// Encoding.Unicode
|
||||
bytes = [0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00, 0x00];
|
||||
stream = new MemoryStream(bytes);
|
||||
br = new BinaryReader(stream);
|
||||
actual = br.ReadNullTerminatedString(Encoding.Unicode);
|
||||
Assert.Equal("ABC", actual);
|
||||
|
||||
// Encoding.BigEndianUnicode
|
||||
bytes = [0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00];
|
||||
stream = new MemoryStream(bytes);
|
||||
br = new BinaryReader(stream);
|
||||
actual = br.ReadNullTerminatedString(Encoding.BigEndianUnicode);
|
||||
Assert.Equal("ABC", actual);
|
||||
|
||||
// Encoding.UTF32
|
||||
bytes = [0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
||||
stream = new MemoryStream(bytes);
|
||||
br = new BinaryReader(stream);
|
||||
actual = br.ReadNullTerminatedString(Encoding.UTF32);
|
||||
Assert.Equal("ABC", actual);
|
||||
|
||||
// Encoding.Latin1
|
||||
bytes = [0x41, 0x42, 0x43, 0x00];
|
||||
stream = new MemoryStream(bytes);
|
||||
br = new BinaryReader(stream);
|
||||
actual = br.ReadNullTerminatedString(Encoding.Latin1);
|
||||
Assert.Equal("ABC", actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadTypeTest()
|
||||
{
|
||||
|
||||
@@ -17,78 +17,6 @@ namespace SabreTools.IO.Extensions.Test
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
];
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedAnsiStringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[4], 0, 4, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = [0x41, 0x42, 0x43, 0x00];
|
||||
|
||||
bool write = bw.WriteNullTerminatedAnsiString("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedUTF8StringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[4], 0, 4, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = [0x41, 0x42, 0x43, 0x00];
|
||||
|
||||
bool write = bw.WriteNullTerminatedUTF8String("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedUnicodeStringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[8], 0, 8, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = [0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00];
|
||||
|
||||
bool write = bw.WriteNullTerminatedUnicodeString("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedUTF32StringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = [0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
||||
|
||||
bool write = bw.WriteNullTerminatedUTF32String("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WritePrefixedAnsiStringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[4], 0, 4, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = [0x03, 0x41, 0x42, 0x43];
|
||||
|
||||
bool write = bw.WritePrefixedAnsiString("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WritePrefixedUnicodeStringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[8], 0, 8, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = [0x03, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00];
|
||||
|
||||
bool write = bw.WritePrefixedUnicodeString("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteTypeTest()
|
||||
{
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Extensions.Test
|
||||
@@ -17,52 +16,6 @@ namespace SabreTools.IO.Extensions.Test
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
];
|
||||
|
||||
[Fact]
|
||||
public void ReadNullTerminatedStringTest()
|
||||
{
|
||||
// Encoding.ASCII
|
||||
int offset = 0;
|
||||
byte[] bytes = [0x41, 0x42, 0x43, 0x00];
|
||||
string? actual = bytes.ReadNullTerminatedString(ref offset, Encoding.ASCII);
|
||||
Assert.Equal("ABC", actual);
|
||||
|
||||
// Encoding.Latin1
|
||||
offset = 0;
|
||||
bytes = [0x41, 0x42, 0x43, 0x00];
|
||||
actual = bytes.ReadNullTerminatedString(ref offset, Encoding.Latin1);
|
||||
Assert.Equal("ABC", actual);
|
||||
|
||||
// Encoding.UTF8
|
||||
offset = 0;
|
||||
bytes = [0x41, 0x42, 0x43, 0x00];
|
||||
actual = bytes.ReadNullTerminatedString(ref offset, Encoding.UTF8);
|
||||
Assert.Equal("ABC", actual);
|
||||
|
||||
// Encoding.Unicode
|
||||
offset = 0;
|
||||
bytes = [0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00, 0x00];
|
||||
actual = bytes.ReadNullTerminatedString(ref offset, Encoding.Unicode);
|
||||
Assert.Equal("ABC", actual);
|
||||
|
||||
// Encoding.BigEndianUnicode
|
||||
offset = 0;
|
||||
bytes = [0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00];
|
||||
actual = bytes.ReadNullTerminatedString(ref offset, Encoding.BigEndianUnicode);
|
||||
Assert.Equal("ABC", actual);
|
||||
|
||||
// Encoding.UTF32
|
||||
offset = 0;
|
||||
bytes = [0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
||||
actual = bytes.ReadNullTerminatedString(ref offset, Encoding.UTF32);
|
||||
Assert.Equal("ABC", actual);
|
||||
|
||||
// Encoding.Latin1
|
||||
offset = 0;
|
||||
bytes = [0x41, 0x42, 0x43, 0x00];
|
||||
actual = bytes.ReadNullTerminatedString(ref offset, Encoding.Latin1);
|
||||
Assert.Equal("ABC", actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadTypeTest()
|
||||
{
|
||||
|
||||
@@ -16,78 +16,6 @@ namespace SabreTools.IO.Extensions.Test
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
];
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedAnsiStringTest()
|
||||
{
|
||||
int offset = 0;
|
||||
byte[] buffer = new byte[4];
|
||||
byte[] expected = [0x41, 0x42, 0x43, 0x00];
|
||||
|
||||
bool write = buffer.WriteNullTerminatedAnsiString(ref offset, "ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedUTF8StringTest()
|
||||
{
|
||||
int offset = 0;
|
||||
byte[] buffer = new byte[4];
|
||||
byte[] expected = [0x41, 0x42, 0x43, 0x00];
|
||||
|
||||
bool write = buffer.WriteNullTerminatedUTF8String(ref offset, "ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedUnicodeStringTest()
|
||||
{
|
||||
int offset = 0;
|
||||
byte[] buffer = new byte[8];
|
||||
byte[] expected = [0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00];
|
||||
|
||||
bool write = buffer.WriteNullTerminatedUnicodeString(ref offset, "ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedUTF32StringTest()
|
||||
{
|
||||
int offset = 0;
|
||||
byte[] buffer = new byte[16];
|
||||
byte[] expected = [0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
||||
|
||||
bool write = buffer.WriteNullTerminatedUTF32String(ref offset, "ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WritePrefixedAnsiStringTest()
|
||||
{
|
||||
int offset = 0;
|
||||
byte[] buffer = new byte[4];
|
||||
byte[] expected = [0x03, 0x41, 0x42, 0x43];
|
||||
|
||||
bool write = buffer.WritePrefixedAnsiString(ref offset, "ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WritePrefixedUnicodeStringTest()
|
||||
{
|
||||
int offset = 0;
|
||||
byte[] buffer = new byte[8];
|
||||
byte[] expected = [0x03, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00];
|
||||
|
||||
bool write = buffer.WritePrefixedUnicodeString(ref offset, "ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteTypeTest()
|
||||
{
|
||||
|
||||
@@ -2,7 +2,6 @@ using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Extensions.Test
|
||||
@@ -18,52 +17,6 @@ namespace SabreTools.IO.Extensions.Test
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
];
|
||||
|
||||
[Fact]
|
||||
public void ReadNullTerminatedStringTest()
|
||||
{
|
||||
// Encoding.ASCII
|
||||
byte[] bytes = [0x41, 0x42, 0x43, 0x00];
|
||||
var stream = new MemoryStream(bytes);
|
||||
string? actual = stream.ReadNullTerminatedString(Encoding.ASCII);
|
||||
Assert.Equal("ABC", actual);
|
||||
|
||||
// Encoding.Latin1
|
||||
bytes = [0x41, 0x42, 0x43, 0x00];
|
||||
stream = new MemoryStream(bytes);
|
||||
actual = stream.ReadNullTerminatedString(Encoding.Latin1);
|
||||
Assert.Equal("ABC", actual);
|
||||
|
||||
// Encoding.UTF8
|
||||
bytes = [0x41, 0x42, 0x43, 0x00];
|
||||
stream = new MemoryStream(bytes);
|
||||
actual = stream.ReadNullTerminatedString(Encoding.UTF8);
|
||||
Assert.Equal("ABC", actual);
|
||||
|
||||
// Encoding.Unicode
|
||||
bytes = [0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00, 0x00];
|
||||
stream = new MemoryStream(bytes);
|
||||
actual = stream.ReadNullTerminatedString(Encoding.Unicode);
|
||||
Assert.Equal("ABC", actual);
|
||||
|
||||
// Encoding.BigEndianUnicode
|
||||
bytes = [0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00];
|
||||
stream = new MemoryStream(bytes);
|
||||
actual = stream.ReadNullTerminatedString(Encoding.BigEndianUnicode);
|
||||
Assert.Equal("ABC", actual);
|
||||
|
||||
// Encoding.UTF32
|
||||
bytes = [0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
||||
stream = new MemoryStream(bytes);
|
||||
actual = stream.ReadNullTerminatedString(Encoding.UTF32);
|
||||
Assert.Equal("ABC", actual);
|
||||
|
||||
// Encoding.Latin1
|
||||
bytes = [0x41, 0x42, 0x43, 0x00];
|
||||
stream = new MemoryStream(bytes);
|
||||
actual = stream.ReadNullTerminatedString(Encoding.Latin1);
|
||||
Assert.Equal("ABC", actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadTypeTest()
|
||||
{
|
||||
|
||||
@@ -17,72 +17,6 @@ namespace SabreTools.IO.Extensions.Test
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
];
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedAnsiStringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[4], 0, 4, true, true);
|
||||
byte[] expected = [0x41, 0x42, 0x43, 0x00];
|
||||
|
||||
bool write = stream.WriteNullTerminatedAnsiString("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedUTF8StringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[4], 0, 4, true, true);
|
||||
byte[] expected = [0x41, 0x42, 0x43, 0x00];
|
||||
|
||||
bool write = stream.WriteNullTerminatedUTF8String("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedUnicodeStringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[8], 0, 8, true, true);
|
||||
byte[] expected = [0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00];
|
||||
|
||||
bool write = stream.WriteNullTerminatedUnicodeString("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedUTF32StringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = [0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
||||
|
||||
bool write = stream.WriteNullTerminatedUTF32String("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WritePrefixedAnsiStringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[4], 0, 4, true, true);
|
||||
byte[] expected = [0x03, 0x41, 0x42, 0x43];
|
||||
|
||||
bool write = stream.WritePrefixedAnsiString("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WritePrefixedUnicodeStringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[8], 0, 8, true, true);
|
||||
byte[] expected = [0x03, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00];
|
||||
|
||||
bool write = stream.WritePrefixedUnicodeString("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteTypeTest()
|
||||
{
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using SabreTools.Numerics.Extensions;
|
||||
using SabreTools.Text.Extensions;
|
||||
|
||||
namespace SabreTools.IO.Extensions
|
||||
{
|
||||
@@ -13,165 +13,6 @@ namespace SabreTools.IO.Extensions
|
||||
/// </summary>
|
||||
public static class BinaryReaderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Read a null-terminated string from the underlying stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedString(this BinaryReader reader, Encoding encoding)
|
||||
{
|
||||
// Short-circuit to explicit implementations
|
||||
if (encoding.CodePage == Encoding.ASCII.CodePage)
|
||||
return reader.ReadNullTerminatedAnsiString();
|
||||
#if NET5_0_OR_GREATER
|
||||
else if (encoding.CodePage == Encoding.Latin1.CodePage)
|
||||
return reader.ReadNullTerminatedLatin1String();
|
||||
#endif
|
||||
else if (encoding.CodePage == Encoding.UTF8.CodePage)
|
||||
return reader.ReadNullTerminatedUTF8String();
|
||||
else if (encoding.CodePage == Encoding.Unicode.CodePage)
|
||||
return reader.ReadNullTerminatedUnicodeString();
|
||||
else if (encoding.CodePage == Encoding.BigEndianUnicode.CodePage)
|
||||
return reader.ReadNullTerminatedBigEndianUnicodeString();
|
||||
else if (encoding.CodePage == Encoding.UTF32.CodePage)
|
||||
return reader.ReadNullTerminatedUTF32String();
|
||||
|
||||
if (reader.BaseStream.Position >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
List<byte> buffer = [];
|
||||
while (reader.BaseStream.Position < reader.BaseStream.Length)
|
||||
{
|
||||
byte ch = reader.ReadByte();
|
||||
if (ch == '\0')
|
||||
break;
|
||||
|
||||
buffer.Add(ch);
|
||||
}
|
||||
|
||||
return encoding.GetString([.. buffer]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated ASCII string from the underlying stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedAnsiString(this BinaryReader reader)
|
||||
{
|
||||
if (reader.BaseStream.Position >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull1Byte(reader);
|
||||
return Encoding.ASCII.GetString(buffer);
|
||||
}
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Read a null-terminated Latin1 string from the underlying stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedLatin1String(this BinaryReader reader)
|
||||
{
|
||||
if (reader.BaseStream.Position >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull1Byte(reader);
|
||||
return Encoding.Latin1.GetString(buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated UTF-8 string from the underlying stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedUTF8String(this BinaryReader reader)
|
||||
{
|
||||
if (reader.BaseStream.Position >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull1Byte(reader);
|
||||
return Encoding.ASCII.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated UTF-16 (Unicode) string from the underlying stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedUnicodeString(this BinaryReader reader)
|
||||
{
|
||||
if (reader.BaseStream.Position >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull2Byte(reader);
|
||||
return Encoding.Unicode.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated UTF-16 (Unicode) string from the underlying stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedBigEndianUnicodeString(this BinaryReader reader)
|
||||
{
|
||||
if (reader.BaseStream.Position >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull2Byte(reader);
|
||||
return Encoding.BigEndianUnicode.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated UTF-32 string from the underlying stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedUTF32String(this BinaryReader reader)
|
||||
{
|
||||
if (reader.BaseStream.Position >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull4Byte(reader);
|
||||
return Encoding.UTF32.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a byte-prefixed ASCII string from the underlying stream
|
||||
/// </summary>
|
||||
public static string? ReadPrefixedAnsiString(this BinaryReader reader)
|
||||
{
|
||||
if (reader.BaseStream.Position >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
byte size = reader.ReadByte();
|
||||
if (reader.BaseStream.Position + size >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = reader.ReadBytes(size);
|
||||
return Encoding.ASCII.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a ushort-prefixed Unicode string from the underlying stream
|
||||
/// </summary>
|
||||
public static string? ReadPrefixedUnicodeString(this BinaryReader reader)
|
||||
{
|
||||
if (reader.BaseStream.Position >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
ushort size = reader.ReadUInt16();
|
||||
if (reader.BaseStream.Position + (size * 2) >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = reader.ReadBytes(size * 2);
|
||||
return Encoding.Unicode.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a ushort-prefixed Unicode string from the underlying stream
|
||||
/// </summary>
|
||||
public static string? ReadPrefixedBigEndianUnicodeString(this BinaryReader reader)
|
||||
{
|
||||
if (reader.BaseStream.Position >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
ushort size = reader.ReadUInt16();
|
||||
if (reader.BaseStream.Position + (size * 2) >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = reader.ReadBytes(size * 2);
|
||||
return Encoding.BigEndianUnicode.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a <typeparamref name="T"/> from the underlying stream
|
||||
/// </summary>
|
||||
@@ -394,59 +235,5 @@ namespace SabreTools.IO.Extensions
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read bytes until a 1-byte null terminator is found
|
||||
/// </summary>
|
||||
private static byte[] ReadUntilNull1Byte(BinaryReader reader)
|
||||
{
|
||||
var bytes = new List<byte>();
|
||||
while (reader.BaseStream.Position < reader.BaseStream.Length)
|
||||
{
|
||||
byte next = reader.ReadByte();
|
||||
if (next == 0x00)
|
||||
break;
|
||||
|
||||
bytes.Add(next);
|
||||
}
|
||||
|
||||
return [.. bytes];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read bytes until a 2-byte null terminator is found
|
||||
/// </summary>
|
||||
private static byte[] ReadUntilNull2Byte(BinaryReader reader)
|
||||
{
|
||||
var bytes = new List<byte>();
|
||||
while (reader.BaseStream.Position < reader.BaseStream.Length)
|
||||
{
|
||||
ushort next = reader.ReadUInt16();
|
||||
if (next == 0x0000)
|
||||
break;
|
||||
|
||||
bytes.AddRange(BitConverter.GetBytes(next));
|
||||
}
|
||||
|
||||
return [.. bytes];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read bytes until a 4-byte null terminator is found
|
||||
/// </summary>
|
||||
private static byte[] ReadUntilNull4Byte(BinaryReader reader)
|
||||
{
|
||||
var bytes = new List<byte>();
|
||||
while (reader.BaseStream.Position < reader.BaseStream.Length)
|
||||
{
|
||||
uint next = reader.ReadUInt32();
|
||||
if (next == 0x00000000)
|
||||
break;
|
||||
|
||||
bytes.AddRange(BitConverter.GetBytes(next));
|
||||
}
|
||||
|
||||
return [.. bytes];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using SabreTools.Numerics.Extensions;
|
||||
using SabreTools.Text.Extensions;
|
||||
|
||||
namespace SabreTools.IO.Extensions
|
||||
{
|
||||
@@ -12,137 +13,6 @@ namespace SabreTools.IO.Extensions
|
||||
/// </summary>
|
||||
public static class BinaryWriterExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Write a null-terminated string to the underlying stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedString(this BinaryWriter writer, string? value, Encoding encoding)
|
||||
{
|
||||
// If the value is null
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
// Add the null terminator and write
|
||||
value += "\0";
|
||||
byte[] buffer = encoding.GetBytes(value);
|
||||
return WriteFromBuffer(writer, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated ASCII string to the underlying stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedAnsiString(this BinaryWriter writer, string? value)
|
||||
=> writer.WriteNullTerminatedString(value, Encoding.ASCII);
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Write a null-terminated Latin1 string to the underlying stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedLatin1String(this BinaryWriter writer, string? value)
|
||||
=> writer.WriteNullTerminatedString(value, Encoding.Latin1);
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-8 string to the underlying stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedUTF8String(this BinaryWriter writer, string? value)
|
||||
=> writer.WriteNullTerminatedString(value, Encoding.UTF8);
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-16 (Unicode) string to the underlying stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedUnicodeString(this BinaryWriter writer, string? value)
|
||||
=> writer.WriteNullTerminatedString(value, Encoding.Unicode);
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-16 (Unicode) string to the underlying stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedBigEndianUnicodeString(this BinaryWriter writer, string? value)
|
||||
=> writer.WriteNullTerminatedString(value, Encoding.BigEndianUnicode);
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-32 string to the underlying stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedUTF32String(this BinaryWriter writer, string? value)
|
||||
=> writer.WriteNullTerminatedString(value, Encoding.UTF32);
|
||||
|
||||
/// <summary>
|
||||
/// Write a byte-prefixed ASCII string to the underlying stream
|
||||
/// </summary>
|
||||
public static bool WritePrefixedAnsiString(this BinaryWriter writer, string? value)
|
||||
{
|
||||
// If the value is null
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
// Get the buffer
|
||||
byte[] buffer = Encoding.ASCII.GetBytes(value);
|
||||
|
||||
// Write the length as a byte
|
||||
writer.Write((byte)value.Length);
|
||||
|
||||
// Write the buffer
|
||||
return WriteFromBuffer(writer, buffer);
|
||||
}
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Write a byte-prefixed Latin1 string to the underlying stream
|
||||
/// </summary>
|
||||
public static bool WritePrefixedLatin1String(this BinaryWriter writer, string? value)
|
||||
{
|
||||
// If the value is null
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
// Get the buffer
|
||||
byte[] buffer = Encoding.Latin1.GetBytes(value);
|
||||
|
||||
// Write the length as a byte
|
||||
writer.Write((byte)value.Length);
|
||||
|
||||
// Write the buffer
|
||||
return WriteFromBuffer(writer, buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Write a ushort-prefixed Unicode string to the underlying stream
|
||||
/// </summary>
|
||||
public static bool WritePrefixedUnicodeString(this BinaryWriter writer, string? value)
|
||||
{
|
||||
// If the value is null
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
// Get the buffer
|
||||
byte[] buffer = Encoding.Unicode.GetBytes(value);
|
||||
|
||||
// Write the length as a ushort
|
||||
writer.Write((ushort)value.Length);
|
||||
|
||||
// Write the buffer
|
||||
return WriteFromBuffer(writer, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a ushort-prefixed Unicode string to the underlying stream
|
||||
/// </summary>
|
||||
public static bool WritePrefixedBigEndianUnicodeString(this BinaryWriter writer, string? value)
|
||||
{
|
||||
// If the value is null
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
// Get the buffer
|
||||
byte[] buffer = Encoding.BigEndianUnicode.GetBytes(value);
|
||||
|
||||
// Write the length as a ushort
|
||||
writer.Write((ushort)value.Length);
|
||||
|
||||
// Write the buffer
|
||||
return WriteFromBuffer(writer, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a <typeparamref name="T"/> to the underlying stream
|
||||
/// </summary>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using SabreTools.Numerics.Extensions;
|
||||
using SabreTools.Text.Extensions;
|
||||
|
||||
namespace SabreTools.IO.Extensions
|
||||
{
|
||||
@@ -12,183 +12,6 @@ namespace SabreTools.IO.Extensions
|
||||
/// </summary>
|
||||
public static class ByteArrayReaderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Read a null-terminated string from the array
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedString(this byte[] content, ref int offset, Encoding encoding)
|
||||
{
|
||||
// Short-circuit to explicit implementations
|
||||
if (encoding.CodePage == Encoding.ASCII.CodePage)
|
||||
return content.ReadNullTerminatedAnsiString(ref offset);
|
||||
#if NET5_0_OR_GREATER
|
||||
else if (encoding.CodePage == Encoding.Latin1.CodePage)
|
||||
return content.ReadNullTerminatedAnsiString(ref offset);
|
||||
#endif
|
||||
else if (encoding.CodePage == Encoding.UTF8.CodePage)
|
||||
return content.ReadNullTerminatedUTF8String(ref offset);
|
||||
else if (encoding.CodePage == Encoding.Unicode.CodePage)
|
||||
return content.ReadNullTerminatedUnicodeString(ref offset);
|
||||
else if (encoding.CodePage == Encoding.BigEndianUnicode.CodePage)
|
||||
return content.ReadNullTerminatedBigEndianUnicodeString(ref offset);
|
||||
else if (encoding.CodePage == Encoding.UTF32.CodePage)
|
||||
return content.ReadNullTerminatedUTF32String(ref offset);
|
||||
|
||||
if (offset >= content.Length)
|
||||
return null;
|
||||
|
||||
List<byte> buffer = [];
|
||||
while (offset < content.Length)
|
||||
{
|
||||
byte ch = content.ReadByteValue(ref offset);
|
||||
if (ch == '\0')
|
||||
break;
|
||||
|
||||
buffer.Add(ch);
|
||||
}
|
||||
|
||||
return encoding.GetString([.. buffer]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated ASCII string from the byte array
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedAnsiString(this byte[] content, ref int offset)
|
||||
{
|
||||
if (offset >= content.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull1Byte(content, ref offset);
|
||||
return Encoding.ASCII.GetString(buffer);
|
||||
}
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Read a null-terminated Latin1 string from the byte array
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedLatin1String(this byte[] content, ref int offset)
|
||||
{
|
||||
if (offset >= content.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull1Byte(content, ref offset);
|
||||
return Encoding.Latin1.GetString(buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated UTF-8 string from the byte array
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedUTF8String(this byte[] content, ref int offset)
|
||||
{
|
||||
if (offset >= content.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull1Byte(content, ref offset);
|
||||
return Encoding.UTF8.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated UTF-16 (Unicode) string from the byte array
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedUnicodeString(this byte[] content, ref int offset)
|
||||
{
|
||||
if (offset >= content.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull2Byte(content, ref offset);
|
||||
return Encoding.Unicode.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated UTF-16 (Unicode) string from the byte array
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedBigEndianUnicodeString(this byte[] content, ref int offset)
|
||||
{
|
||||
if (offset >= content.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull2Byte(content, ref offset);
|
||||
return Encoding.BigEndianUnicode.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated UTF-32 string from the byte array
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedUTF32String(this byte[] content, ref int offset)
|
||||
{
|
||||
if (offset >= content.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull4Byte(content, ref offset);
|
||||
return Encoding.UTF32.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a byte-prefixed ASCII string from the byte array
|
||||
/// </summary>
|
||||
public static string? ReadPrefixedAnsiString(this byte[] content, ref int offset)
|
||||
{
|
||||
if (offset >= content.Length)
|
||||
return null;
|
||||
|
||||
byte size = content.ReadByteValue(ref offset);
|
||||
if (offset + size >= content.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = content.ReadBytes(ref offset, size);
|
||||
return Encoding.ASCII.GetString(buffer);
|
||||
}
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Read a byte-prefixed Latin1 string from the byte array
|
||||
/// </summary>
|
||||
public static string? ReadPrefixedLatin1String(this byte[] content, ref int offset)
|
||||
{
|
||||
if (offset >= content.Length)
|
||||
return null;
|
||||
|
||||
byte size = content.ReadByteValue(ref offset);
|
||||
if (offset + size >= content.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = content.ReadBytes(ref offset, size);
|
||||
return Encoding.Latin1.GetString(buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Read a ushort-prefixed Unicode string from the byte array
|
||||
/// </summary>
|
||||
public static string? ReadPrefixedUnicodeString(this byte[] content, ref int offset)
|
||||
{
|
||||
if (offset >= content.Length)
|
||||
return null;
|
||||
|
||||
ushort size = content.ReadUInt16(ref offset);
|
||||
if (offset + (size * 2) >= content.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = content.ReadBytes(ref offset, size * 2);
|
||||
return Encoding.Unicode.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a ushort-prefixed Unicode string from the byte array
|
||||
/// </summary>
|
||||
public static string? ReadPrefixedBigEndianUnicodeString(this byte[] content, ref int offset)
|
||||
{
|
||||
if (offset >= content.Length)
|
||||
return null;
|
||||
|
||||
ushort size = content.ReadUInt16(ref offset);
|
||||
if (offset + (size * 2) >= content.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = content.ReadBytes(ref offset, size * 2);
|
||||
return Encoding.BigEndianUnicode.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a <typeparamref name="T"/> and increment the pointer to an array
|
||||
/// </summary>
|
||||
@@ -403,60 +226,6 @@ namespace SabreTools.IO.Extensions
|
||||
#pragma warning restore IDE0010
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read bytes until a 1-byte null terminator is found
|
||||
/// </summary>
|
||||
private static byte[] ReadUntilNull1Byte(byte[] content, ref int offset)
|
||||
{
|
||||
var bytes = new List<byte>();
|
||||
while (offset < content.Length)
|
||||
{
|
||||
byte next = content.ReadByte(ref offset);
|
||||
if (next == 0x00)
|
||||
break;
|
||||
|
||||
bytes.Add(next);
|
||||
}
|
||||
|
||||
return [.. bytes];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read bytes until a 2-byte null terminator is found
|
||||
/// </summary>
|
||||
private static byte[] ReadUntilNull2Byte(byte[] content, ref int offset)
|
||||
{
|
||||
var bytes = new List<byte>();
|
||||
while (offset < content.Length)
|
||||
{
|
||||
ushort next = content.ReadUInt16(ref offset);
|
||||
if (next == 0x0000)
|
||||
break;
|
||||
|
||||
bytes.AddRange(BitConverter.GetBytes(next));
|
||||
}
|
||||
|
||||
return [.. bytes];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read bytes until a 4-byte null terminator is found
|
||||
/// </summary>
|
||||
private static byte[] ReadUntilNull4Byte(byte[] content, ref int offset)
|
||||
{
|
||||
var bytes = new List<byte>();
|
||||
while (offset < content.Length)
|
||||
{
|
||||
uint next = content.ReadUInt32(ref offset);
|
||||
if (next == 0x00000000)
|
||||
break;
|
||||
|
||||
bytes.AddRange(BitConverter.GetBytes(next));
|
||||
}
|
||||
|
||||
return [.. bytes];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a number of bytes from the byte array to a buffer
|
||||
/// </summary>
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using SabreTools.Numerics.Extensions;
|
||||
using SabreTools.Text.Extensions;
|
||||
|
||||
namespace SabreTools.IO.Extensions
|
||||
{
|
||||
@@ -11,141 +12,6 @@ namespace SabreTools.IO.Extensions
|
||||
/// </summary>
|
||||
public static class ByteArrayWriterExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Write a null-terminated string to the array
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedString(this byte[] content, ref int offset, string? value, Encoding encoding)
|
||||
{
|
||||
// If the value is null
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
// Add the null terminator and write
|
||||
value += "\0";
|
||||
byte[] buffer = encoding.GetBytes(value);
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated ASCII string to the byte array
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedAnsiString(this byte[] content, ref int offset, string? value)
|
||||
=> content.WriteNullTerminatedString(ref offset, value, Encoding.ASCII);
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Write a null-terminated Latin1 string to the byte array
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedLatin1String(this byte[] content, ref int offset, string? value)
|
||||
=> content.WriteNullTerminatedString(ref offset, value, Encoding.Latin1);
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-8 string to the byte array
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedUTF8String(this byte[] content, ref int offset, string? value)
|
||||
=> content.WriteNullTerminatedString(ref offset, value, Encoding.UTF8);
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-16 (Unicode) string to the byte array
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedUnicodeString(this byte[] content, ref int offset, string? value)
|
||||
=> content.WriteNullTerminatedString(ref offset, value, Encoding.Unicode);
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-16 (Unicode) string to the byte array
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedBigEndianUnicodeString(this byte[] content, ref int offset, string? value)
|
||||
=> content.WriteNullTerminatedString(ref offset, value, Encoding.BigEndianUnicode);
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-32 string to the byte array
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedUTF32String(this byte[] content, ref int offset, string? value)
|
||||
=> content.WriteNullTerminatedString(ref offset, value, Encoding.UTF32);
|
||||
|
||||
/// <summary>
|
||||
/// Write a byte-prefixed ASCII string to the byte array
|
||||
/// </summary>
|
||||
public static bool WritePrefixedAnsiString(this byte[] content, ref int offset, string? value)
|
||||
{
|
||||
// If the value is null
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
// Get the buffer
|
||||
byte[] buffer = Encoding.ASCII.GetBytes(value);
|
||||
|
||||
// Write the length as a byte
|
||||
if (!content.Write(ref offset, (byte)value.Length))
|
||||
return false;
|
||||
|
||||
// Write the buffer
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Write a byte-prefixed Latin1 string to the byte array
|
||||
/// </summary>
|
||||
public static bool WritePrefixedLatin1String(this byte[] content, ref int offset, string? value)
|
||||
{
|
||||
// If the value is null
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
// Get the buffer
|
||||
byte[] buffer = Encoding.Latin1.GetBytes(value);
|
||||
|
||||
// Write the length as a byte
|
||||
if (!content.Write(ref offset, (byte)value.Length))
|
||||
return false;
|
||||
|
||||
// Write the buffer
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Write a ushort-prefixed Unicode string to the byte array
|
||||
/// </summary>
|
||||
public static bool WritePrefixedUnicodeString(this byte[] content, ref int offset, string? value)
|
||||
{
|
||||
// If the value is null
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
// Get the buffer
|
||||
byte[] buffer = Encoding.Unicode.GetBytes(value);
|
||||
|
||||
// Write the length as a ushort
|
||||
if (!content.Write(ref offset, (ushort)value.Length))
|
||||
return false;
|
||||
|
||||
// Write the buffer
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a ushort-prefixed Unicode string to the byte array
|
||||
/// </summary>
|
||||
public static bool WritePrefixedBigEndianUnicodeString(this byte[] content, ref int offset, string? value)
|
||||
{
|
||||
// If the value is null
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
// Get the buffer
|
||||
byte[] buffer = Encoding.BigEndianUnicode.GetBytes(value);
|
||||
|
||||
// Write the length as a ushort
|
||||
if (!content.Write(ref offset, (ushort)value.Length))
|
||||
return false;
|
||||
|
||||
// Write the buffer
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a <typeparamref name="T"/> to the byte array
|
||||
/// </summary>
|
||||
|
||||
@@ -6,11 +6,11 @@ Relies on functionality found in `SabreTools.IO.Matching`, `SabreTools.Numerics`
|
||||
|
||||
| Class | Description |
|
||||
| --- | --- |
|
||||
| `BinaryReaderExtensions` | Extensions to `BinaryReader` for specialized string and type reading. |
|
||||
| `BinaryWriterExtensions` | Extensions to `BinaryWriter` for specialized string and type writing. |
|
||||
| `BinaryReaderExtensions` | Extensions to `BinaryReader` for specialized type reading. |
|
||||
| `BinaryWriterExtensions` | Extensions to `BinaryWriter` for specialized type writing. |
|
||||
| `ByteArrayExtensions` | Utility `byte[]` and `byte?[]` extensions commonly used in SabreTools projects. |
|
||||
| `ByteArrayReaderExtensions` | Extensions to `byte[]` for specialized string and type reading. |
|
||||
| `ByteArrayWriterExtensions` | Extensions to `byte[]` for specialized string and type writing. |
|
||||
| `ByteArrayReaderExtensions` | Extensions to `byte[]` for specialized type reading. |
|
||||
| `ByteArrayWriterExtensions` | Extensions to `byte[]` for specialized type writing. |
|
||||
| `DateTimeExtensions` | `DateTime` conversion, specifically between standard and MS-DOS formats. |
|
||||
| `DictionaryExtensions` | Utility `Dictionary<string, List<string>>` extensions commonly used in SabreTools projects. |
|
||||
| `EnumerableExtensions` | Specialized enumeration wrapper functionality. |
|
||||
@@ -19,8 +19,5 @@ Relies on functionality found in `SabreTools.IO.Matching`, `SabreTools.Numerics`
|
||||
| `ParentablePathExtensions` | Extensions to `SabreTools.IO.ParentablePath` for filtering. |
|
||||
| `ReadOnlyBitStreamExtensions` | Extensions to `SabreTools.IO.ReadOnlyBitStream` to allow non-bitwise reads. |
|
||||
| `StreamExtensions` | Utility `Stream` extensions commonly used in other SabreTools projects. |
|
||||
| `StreamReaderExtensions` | Extensions to `Stream` for specialized string and type reading. |
|
||||
| `StreamWriterExtensions` | Extensions to `Stream` for specialized string and type writing. |
|
||||
| `StringBuilderExtensions` | Formatting overloads for `StringBuilder` to allow for type-specific handling. |
|
||||
| `StringExtensions` | Optional overloads to `string.Contains`, `string.EndsWith`, `string.Equals`, and `string.StartsWith`. |
|
||||
| `XmlTextWriterExtensions` | Overloads to `XmlTextWriter.WriteAttributeString` and `XmlTextWriter.WriteElementString`. |
|
||||
| `StreamReaderExtensions` | Extensions to `Stream` for specialized type reading. |
|
||||
| `StreamWriterExtensions` | Extensions to `Stream` for specialized type writing. |
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
<ProjectReference Include="..\SabreTools.Numerics\SabreTools.Numerics.csproj" />
|
||||
<ProjectReference Include="..\SabreTools.Numerics.Extensions\SabreTools.Numerics.Extensions.csproj" />
|
||||
<ProjectReference Include="..\SabreTools.Text.Compare\SabreTools.Text.Compare.csproj" />
|
||||
<ProjectReference Include="..\SabreTools.Text.Extensions\SabreTools.Text.Extensions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,10 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using SabreTools.Numerics.Extensions;
|
||||
using SabreTools.Text.Extensions;
|
||||
|
||||
namespace SabreTools.IO.Extensions
|
||||
{
|
||||
@@ -13,183 +13,6 @@ namespace SabreTools.IO.Extensions
|
||||
/// </summary>
|
||||
public static class StreamReaderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Read a null-terminated string from the stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedString(this Stream stream, Encoding encoding)
|
||||
{
|
||||
// Short-circuit to explicit implementations
|
||||
if (encoding.CodePage == Encoding.ASCII.CodePage)
|
||||
return stream.ReadNullTerminatedAnsiString();
|
||||
#if NET5_0_OR_GREATER
|
||||
else if (encoding.CodePage == Encoding.Latin1.CodePage)
|
||||
return stream.ReadNullTerminatedLatin1String();
|
||||
#endif
|
||||
else if (encoding.CodePage == Encoding.UTF8.CodePage)
|
||||
return stream.ReadNullTerminatedUTF8String();
|
||||
else if (encoding.CodePage == Encoding.Unicode.CodePage)
|
||||
return stream.ReadNullTerminatedUnicodeString();
|
||||
else if (encoding.CodePage == Encoding.BigEndianUnicode.CodePage)
|
||||
return stream.ReadNullTerminatedBigEndianUnicodeString();
|
||||
else if (encoding.CodePage == Encoding.UTF32.CodePage)
|
||||
return stream.ReadNullTerminatedUTF32String();
|
||||
|
||||
if (stream.Position >= stream.Length)
|
||||
return null;
|
||||
|
||||
List<byte> buffer = [];
|
||||
while (stream.Position < stream.Length)
|
||||
{
|
||||
byte ch = stream.ReadByteValue();
|
||||
if (ch == '\0')
|
||||
break;
|
||||
|
||||
buffer.Add(ch);
|
||||
}
|
||||
|
||||
return encoding.GetString([.. buffer]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated ASCII string from the stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedAnsiString(this Stream stream)
|
||||
{
|
||||
if (stream.Position >= stream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull1Byte(stream);
|
||||
return Encoding.ASCII.GetString(buffer);
|
||||
}
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Read a null-terminated Latin1 string from the stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedLatin1String(this Stream stream)
|
||||
{
|
||||
if (stream.Position >= stream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull1Byte(stream);
|
||||
return Encoding.Latin1.GetString(buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated UTF-8 string from the stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedUTF8String(this Stream stream)
|
||||
{
|
||||
if (stream.Position >= stream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull1Byte(stream);
|
||||
return Encoding.UTF8.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated UTF-16 (Unicode) string from the stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedUnicodeString(this Stream stream)
|
||||
{
|
||||
if (stream.Position >= stream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull2Byte(stream);
|
||||
return Encoding.Unicode.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated UTF-16 (Unicode) string from the stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedBigEndianUnicodeString(this Stream stream)
|
||||
{
|
||||
if (stream.Position >= stream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull2Byte(stream);
|
||||
return Encoding.BigEndianUnicode.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated UTF-32 string from the stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedUTF32String(this Stream stream)
|
||||
{
|
||||
if (stream.Position >= stream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull4Byte(stream);
|
||||
return Encoding.UTF32.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a byte-prefixed ASCII string from the stream
|
||||
/// </summary>
|
||||
public static string? ReadPrefixedAnsiString(this Stream stream)
|
||||
{
|
||||
if (stream.Position >= stream.Length)
|
||||
return null;
|
||||
|
||||
byte size = stream.ReadByteValue();
|
||||
if (stream.Position + size >= stream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = stream.ReadBytes(size);
|
||||
return Encoding.ASCII.GetString(buffer);
|
||||
}
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Read a byte-prefixed Latin1 string from the stream
|
||||
/// </summary>
|
||||
public static string? ReadPrefixedLatin1String(this Stream stream)
|
||||
{
|
||||
if (stream.Position >= stream.Length)
|
||||
return null;
|
||||
|
||||
byte size = stream.ReadByteValue();
|
||||
if (stream.Position + size >= stream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = stream.ReadBytes(size);
|
||||
return Encoding.Latin1.GetString(buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Read a ushort-prefixed Unicode string from the stream
|
||||
/// </summary>
|
||||
public static string? ReadPrefixedUnicodeString(this Stream stream)
|
||||
{
|
||||
if (stream.Position >= stream.Length)
|
||||
return null;
|
||||
|
||||
ushort size = stream.ReadUInt16();
|
||||
if (stream.Position + (size * 2) >= stream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = stream.ReadBytes(size * 2);
|
||||
return Encoding.Unicode.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a ushort-prefixed Unicode string from the stream
|
||||
/// </summary>
|
||||
public static string? ReadPrefixedBigEndianUnicodeString(this Stream stream)
|
||||
{
|
||||
if (stream.Position >= stream.Length)
|
||||
return null;
|
||||
|
||||
ushort size = stream.ReadUInt16();
|
||||
if (stream.Position + (size * 2) >= stream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = stream.ReadBytes(size * 2);
|
||||
return Encoding.BigEndianUnicode.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a <typeparamref name="T"/> from the stream
|
||||
/// </summary>
|
||||
@@ -404,60 +227,6 @@ namespace SabreTools.IO.Extensions
|
||||
#pragma warning restore IDE0010
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read bytes until a 1-byte null terminator is found
|
||||
/// </summary>
|
||||
private static byte[] ReadUntilNull1Byte(Stream stream)
|
||||
{
|
||||
var bytes = new List<byte>();
|
||||
while (stream.Position < stream.Length)
|
||||
{
|
||||
byte next = stream.ReadByteValue();
|
||||
if (next == 0x00)
|
||||
break;
|
||||
|
||||
bytes.Add(next);
|
||||
}
|
||||
|
||||
return [.. bytes];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read bytes until a 2-byte null terminator is found
|
||||
/// </summary>
|
||||
private static byte[] ReadUntilNull2Byte(Stream stream)
|
||||
{
|
||||
var bytes = new List<byte>();
|
||||
while (stream.Position < stream.Length)
|
||||
{
|
||||
ushort next = stream.ReadUInt16();
|
||||
if (next == 0x0000)
|
||||
break;
|
||||
|
||||
bytes.AddRange(BitConverter.GetBytes(next));
|
||||
}
|
||||
|
||||
return [.. bytes];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read bytes until a 4-byte null terminator is found
|
||||
/// </summary>
|
||||
private static byte[] ReadUntilNull4Byte(Stream stream)
|
||||
{
|
||||
var bytes = new List<byte>();
|
||||
while (stream.Position < stream.Length)
|
||||
{
|
||||
uint next = stream.ReadUInt32();
|
||||
if (next == 0x00000000)
|
||||
break;
|
||||
|
||||
bytes.AddRange(BitConverter.GetBytes(next));
|
||||
}
|
||||
|
||||
return [.. bytes];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a number of bytes from the stream to a buffer
|
||||
/// </summary>
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using SabreTools.Numerics.Extensions;
|
||||
using SabreTools.Text.Extensions;
|
||||
|
||||
namespace SabreTools.IO.Extensions
|
||||
{
|
||||
@@ -12,141 +13,6 @@ namespace SabreTools.IO.Extensions
|
||||
/// </summary>
|
||||
public static class StreamWriterExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Write a null-terminated string to the stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedString(this Stream stream, string? value, Encoding encoding)
|
||||
{
|
||||
// If the value is null
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
// Add the null terminator and write
|
||||
value += "\0";
|
||||
byte[] buffer = encoding.GetBytes(value);
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated ASCII string to the stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedAnsiString(this Stream stream, string? value)
|
||||
=> stream.WriteNullTerminatedString(value, Encoding.ASCII);
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Write a null-terminated Latin1 string to the stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedLatin1String(this Stream stream, string? value)
|
||||
=> stream.WriteNullTerminatedString(value, Encoding.Latin1);
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-8 string to the stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedUTF8String(this Stream stream, string? value)
|
||||
=> stream.WriteNullTerminatedString(value, Encoding.UTF8);
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-16 (Unicode) string to the stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedUnicodeString(this Stream stream, string? value)
|
||||
=> stream.WriteNullTerminatedString(value, Encoding.Unicode);
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-16 (Unicode) string to the stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedBigEndianUnicodeString(this Stream stream, string? value)
|
||||
=> stream.WriteNullTerminatedString(value, Encoding.BigEndianUnicode);
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-32 string to the stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedUTF32String(this Stream stream, string? value)
|
||||
=> stream.WriteNullTerminatedString(value, Encoding.UTF32);
|
||||
|
||||
//// <summary>
|
||||
/// Write a byte-prefixed ASCII string to the stream
|
||||
/// </summary>
|
||||
public static bool WritePrefixedAnsiString(this Stream stream, string? value)
|
||||
{
|
||||
// If the value is null
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
// Get the buffer
|
||||
byte[] buffer = Encoding.ASCII.GetBytes(value);
|
||||
|
||||
// Write the length as a byte
|
||||
if (!stream.Write((byte)value.Length))
|
||||
return false;
|
||||
|
||||
// Write the buffer
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
//// <summary>
|
||||
/// Write a byte-prefixed Latin1 string to the stream
|
||||
/// </summary>
|
||||
public static bool WritePrefixedLatin1String(this Stream stream, string? value)
|
||||
{
|
||||
// If the value is null
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
// Get the buffer
|
||||
byte[] buffer = Encoding.Latin1.GetBytes(value);
|
||||
|
||||
// Write the length as a byte
|
||||
if (!stream.Write((byte)value.Length))
|
||||
return false;
|
||||
|
||||
// Write the buffer
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Write a ushort-prefixed Unicode string to the stream
|
||||
/// </summary>
|
||||
public static bool WritePrefixedUnicodeString(this Stream stream, string? value)
|
||||
{
|
||||
// If the value is null
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
// Get the buffer
|
||||
byte[] buffer = Encoding.Unicode.GetBytes(value);
|
||||
|
||||
// Write the length as a ushort
|
||||
if (!stream.Write((ushort)value.Length))
|
||||
return false;
|
||||
|
||||
// Write the buffer
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a ushort-prefixed Unicode string to the stream
|
||||
/// </summary>
|
||||
public static bool WritePrefixedBigEndianUnicodeString(this Stream stream, string? value)
|
||||
{
|
||||
// If the value is null
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
// Get the buffer
|
||||
byte[] buffer = Encoding.BigEndianUnicode.GetBytes(value);
|
||||
|
||||
// Write the length as a ushort
|
||||
if (!stream.Write((ushort)value.Length))
|
||||
return false;
|
||||
|
||||
// Write the buffer
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a <typeparamref name="T"/> to the stream
|
||||
/// </summary>
|
||||
|
||||
@@ -48,9 +48,9 @@
|
||||
<Compile Include="..\SabreTools.Security.Cryptography\*.cs" />
|
||||
<Compile Include="..\SabreTools.Text.ClrMamePro\*.cs" />
|
||||
<Compile Include="..\SabreTools.Text.Compare\*.cs" />
|
||||
<Compile Include="..\SabreTools.Text.Extensions\*.cs" Exclude="..\SabreTools.Text.Extensions\ExtensionAttribute.cs" />
|
||||
<Compile Include="..\SabreTools.Text.INI\*.cs" />
|
||||
<Compile Include="..\SabreTools.Text.SeparatedValue\*.cs" />
|
||||
<Compile Include="..\SabreTools.Text.Extensions\*.cs" Exclude="..\SabreTools.Text.Extensions\ExtensionAttribute.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Text.Extensions.Test
|
||||
{
|
||||
public class BinaryReaderExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void ReadNullTerminatedAnsiStringTest()
|
||||
{
|
||||
byte[] bytes = [0x41, 0x42, 0x43, 0x00];
|
||||
var stream = new MemoryStream(bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
string? actual = br.ReadNullTerminatedString(Encoding.ASCII);
|
||||
Assert.Equal("ABC", actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadNullTerminatedLatin1StringTest()
|
||||
{
|
||||
byte[] bytes = [0x41, 0x42, 0x43, 0x00];
|
||||
var stream = new MemoryStream(bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
string? actual = br.ReadNullTerminatedString(Encoding.Latin1);
|
||||
Assert.Equal("ABC", actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadNullTerminatedUTF8StringTest()
|
||||
{
|
||||
byte[] bytes = [0x41, 0x42, 0x43, 0x00];
|
||||
var stream = new MemoryStream(bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
string? actual = br.ReadNullTerminatedString(Encoding.UTF8);
|
||||
Assert.Equal("ABC", actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadNullTerminatedUnicodeStringTest()
|
||||
{
|
||||
byte[] bytes = [0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00, 0x00];
|
||||
var stream = new MemoryStream(bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
string? actual = br.ReadNullTerminatedString(Encoding.Unicode);
|
||||
Assert.Equal("ABC", actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadNullTerminatedBigEndianUnicodeStringTest()
|
||||
{
|
||||
byte[] bytes = [0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00];
|
||||
var stream = new MemoryStream(bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
string? actual = br.ReadNullTerminatedString(Encoding.BigEndianUnicode);
|
||||
Assert.Equal("ABC", actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadNullTerminatedUTF32StringTest()
|
||||
{
|
||||
byte[] bytes = [0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
||||
var stream = new MemoryStream(bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
string? actual = br.ReadNullTerminatedString(Encoding.UTF32);
|
||||
Assert.Equal("ABC", actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
using System.IO;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Text.Extensions.Test
|
||||
{
|
||||
public class BinaryWriterExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void WriteNullTerminatedAnsiStringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[4], 0, 4, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = [0x41, 0x42, 0x43, 0x00];
|
||||
|
||||
bool write = bw.WriteNullTerminatedAnsiString("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedUTF8StringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[4], 0, 4, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = [0x41, 0x42, 0x43, 0x00];
|
||||
|
||||
bool write = bw.WriteNullTerminatedUTF8String("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedUnicodeStringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[8], 0, 8, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = [0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00];
|
||||
|
||||
bool write = bw.WriteNullTerminatedUnicodeString("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedUTF32StringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = [0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
||||
|
||||
bool write = bw.WriteNullTerminatedUTF32String("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WritePrefixedAnsiStringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[4], 0, 4, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = [0x03, 0x41, 0x42, 0x43];
|
||||
|
||||
bool write = bw.WritePrefixedAnsiString("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WritePrefixedUnicodeStringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[8], 0, 8, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = [0x03, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00];
|
||||
|
||||
bool write = bw.WritePrefixedUnicodeString("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate that a set of actual bytes matches the expected bytes
|
||||
/// </summary>
|
||||
private static void ValidateBytes(byte[] expected, byte[] actual)
|
||||
{
|
||||
for (int i = 0; i < expected.Length; i++)
|
||||
{
|
||||
Assert.Equal(expected[i], actual[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using System.Text;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Text.Extensions.Test
|
||||
{
|
||||
public class ByteArrayReaderExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void ReadNullTerminatedAnsiStringTest()
|
||||
{
|
||||
int offset = 0;
|
||||
byte[] bytes = [0x41, 0x42, 0x43, 0x00];
|
||||
string? actual = bytes.ReadNullTerminatedString(ref offset, Encoding.ASCII);
|
||||
Assert.Equal("ABC", actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadNullTerminatedLatin1StringTest()
|
||||
{
|
||||
int offset = 0;
|
||||
byte[] bytes = [0x41, 0x42, 0x43, 0x00];
|
||||
string? actual = bytes.ReadNullTerminatedString(ref offset, Encoding.Latin1);
|
||||
Assert.Equal("ABC", actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadNullTerminatedUTF8StringTest()
|
||||
{
|
||||
int offset = 0;
|
||||
byte[] bytes = [0x41, 0x42, 0x43, 0x00];
|
||||
string? actual = bytes.ReadNullTerminatedString(ref offset, Encoding.UTF8);
|
||||
Assert.Equal("ABC", actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadNullTerminatedUnicodeStringTest()
|
||||
{
|
||||
int offset = 0;
|
||||
byte[] bytes = [0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00, 0x00];
|
||||
string? actual = bytes.ReadNullTerminatedString(ref offset, Encoding.Unicode);
|
||||
Assert.Equal("ABC", actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadNullTerminatedBigEndianUnicodeStringTest()
|
||||
{
|
||||
int offset = 0;
|
||||
byte[] bytes = [0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00];
|
||||
string? actual = bytes.ReadNullTerminatedString(ref offset, Encoding.BigEndianUnicode);
|
||||
Assert.Equal("ABC", actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadNullTerminatedUTF32StringTest()
|
||||
{
|
||||
int offset = 0;
|
||||
byte[] bytes = [0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
||||
string? actual = bytes.ReadNullTerminatedString(ref offset, Encoding.UTF32);
|
||||
Assert.Equal("ABC", actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Text.Extensions.Test
|
||||
{
|
||||
public class ByteArrayWriterExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void WriteNullTerminatedAnsiStringTest()
|
||||
{
|
||||
int offset = 0;
|
||||
byte[] buffer = new byte[4];
|
||||
byte[] expected = [0x41, 0x42, 0x43, 0x00];
|
||||
|
||||
bool write = buffer.WriteNullTerminatedAnsiString(ref offset, "ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedUTF8StringTest()
|
||||
{
|
||||
int offset = 0;
|
||||
byte[] buffer = new byte[4];
|
||||
byte[] expected = [0x41, 0x42, 0x43, 0x00];
|
||||
|
||||
bool write = buffer.WriteNullTerminatedUTF8String(ref offset, "ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedUnicodeStringTest()
|
||||
{
|
||||
int offset = 0;
|
||||
byte[] buffer = new byte[8];
|
||||
byte[] expected = [0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00];
|
||||
|
||||
bool write = buffer.WriteNullTerminatedUnicodeString(ref offset, "ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedUTF32StringTest()
|
||||
{
|
||||
int offset = 0;
|
||||
byte[] buffer = new byte[16];
|
||||
byte[] expected = [0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
||||
|
||||
bool write = buffer.WriteNullTerminatedUTF32String(ref offset, "ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WritePrefixedAnsiStringTest()
|
||||
{
|
||||
int offset = 0;
|
||||
byte[] buffer = new byte[4];
|
||||
byte[] expected = [0x03, 0x41, 0x42, 0x43];
|
||||
|
||||
bool write = buffer.WritePrefixedAnsiString(ref offset, "ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WritePrefixedUnicodeStringTest()
|
||||
{
|
||||
int offset = 0;
|
||||
byte[] buffer = new byte[8];
|
||||
byte[] expected = [0x03, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00];
|
||||
|
||||
bool write = buffer.WritePrefixedUnicodeString(ref offset, "ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate that a set of actual bytes matches the expected bytes
|
||||
/// </summary>
|
||||
private static void ValidateBytes(byte[] expected, byte[] actual)
|
||||
{
|
||||
for (int i = 0; i < expected.Length; i++)
|
||||
{
|
||||
Assert.Equal(expected[i], actual[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Text.Extensions.Test
|
||||
{
|
||||
public class StreamReaderExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void ReadNullTerminatedAnsiStringTest()
|
||||
{
|
||||
byte[] bytes = [0x41, 0x42, 0x43, 0x00];
|
||||
var stream = new MemoryStream(bytes);
|
||||
string? actual = stream.ReadNullTerminatedString(Encoding.ASCII);
|
||||
Assert.Equal("ABC", actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadNullTerminatedLatin1StringTest()
|
||||
{
|
||||
byte[] bytes = [0x41, 0x42, 0x43, 0x00];
|
||||
var stream = new MemoryStream(bytes);
|
||||
string? actual = stream.ReadNullTerminatedString(Encoding.Latin1);
|
||||
Assert.Equal("ABC", actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadNullTerminatedUTF8StringTest()
|
||||
{
|
||||
byte[] bytes = [0x41, 0x42, 0x43, 0x00];
|
||||
var stream = new MemoryStream(bytes);
|
||||
string? actual = stream.ReadNullTerminatedString(Encoding.UTF8);
|
||||
Assert.Equal("ABC", actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadNullTerminatedUnicodeStringTest()
|
||||
{
|
||||
byte[] bytes = [0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00, 0x00];
|
||||
var stream = new MemoryStream(bytes);
|
||||
string? actual = stream.ReadNullTerminatedString(Encoding.Unicode);
|
||||
Assert.Equal("ABC", actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadNullTerminatedBigEndianUnicodeStringTest()
|
||||
{
|
||||
byte[] bytes = [0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00];
|
||||
var stream = new MemoryStream(bytes);
|
||||
string? actual = stream.ReadNullTerminatedString(Encoding.BigEndianUnicode);
|
||||
Assert.Equal("ABC", actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadNullTerminatedUTF32StringTest()
|
||||
{
|
||||
byte[] bytes = [0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
||||
var stream = new MemoryStream(bytes);
|
||||
string? actual = stream.ReadNullTerminatedString(Encoding.UTF32);
|
||||
Assert.Equal("ABC", actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using System.IO;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Text.Extensions.Test
|
||||
{
|
||||
public class StreamWriterExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void WriteNullTerminatedAnsiStringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[4], 0, 4, true, true);
|
||||
byte[] expected = [0x41, 0x42, 0x43, 0x00];
|
||||
|
||||
bool write = stream.WriteNullTerminatedAnsiString("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedUTF8StringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[4], 0, 4, true, true);
|
||||
byte[] expected = [0x41, 0x42, 0x43, 0x00];
|
||||
|
||||
bool write = stream.WriteNullTerminatedUTF8String("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedUnicodeStringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[8], 0, 8, true, true);
|
||||
byte[] expected = [0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00];
|
||||
|
||||
bool write = stream.WriteNullTerminatedUnicodeString("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedUTF32StringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = [0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
||||
|
||||
bool write = stream.WriteNullTerminatedUTF32String("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WritePrefixedAnsiStringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[4], 0, 4, true, true);
|
||||
byte[] expected = [0x03, 0x41, 0x42, 0x43];
|
||||
|
||||
bool write = stream.WritePrefixedAnsiString("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WritePrefixedUnicodeStringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[8], 0, 8, true, true);
|
||||
byte[] expected = [0x03, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00];
|
||||
|
||||
bool write = stream.WritePrefixedUnicodeString("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate that a set of actual bytes matches the expected bytes
|
||||
/// </summary>
|
||||
private static void ValidateBytes(byte[] expected, byte[] actual)
|
||||
{
|
||||
for (int i = 0; i < expected.Length; i++)
|
||||
{
|
||||
Assert.Equal(expected[i], actual[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Extensions.Test
|
||||
namespace SabreTools.Text.Extensions.Test
|
||||
{
|
||||
public class StringExtensionsTests
|
||||
{
|
||||
@@ -4,7 +4,7 @@ using System.Text;
|
||||
using System.Xml;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Extensions.Test
|
||||
namespace SabreTools.Text.Extensions.Test
|
||||
{
|
||||
public class XmlTextWriterExtensionsTests
|
||||
{
|
||||
226
SabreTools.Text.Extensions/BinaryReaderExtensions.cs
Normal file
226
SabreTools.Text.Extensions/BinaryReaderExtensions.cs
Normal file
@@ -0,0 +1,226 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace SabreTools.Text.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions for BinaryReader
|
||||
/// </summary>
|
||||
public static class BinaryReaderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Read a null-terminated string from the underlying stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedString(this BinaryReader reader, Encoding encoding)
|
||||
{
|
||||
// Short-circuit to explicit implementations
|
||||
if (encoding.CodePage == Encoding.ASCII.CodePage)
|
||||
return reader.ReadNullTerminatedAnsiString();
|
||||
#if NET5_0_OR_GREATER
|
||||
else if (encoding.CodePage == Encoding.Latin1.CodePage)
|
||||
return reader.ReadNullTerminatedLatin1String();
|
||||
#endif
|
||||
else if (encoding.CodePage == Encoding.UTF8.CodePage)
|
||||
return reader.ReadNullTerminatedUTF8String();
|
||||
else if (encoding.CodePage == Encoding.Unicode.CodePage)
|
||||
return reader.ReadNullTerminatedUnicodeString();
|
||||
else if (encoding.CodePage == Encoding.BigEndianUnicode.CodePage)
|
||||
return reader.ReadNullTerminatedBigEndianUnicodeString();
|
||||
else if (encoding.CodePage == Encoding.UTF32.CodePage)
|
||||
return reader.ReadNullTerminatedUTF32String();
|
||||
|
||||
if (reader.BaseStream.Position >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
List<byte> buffer = [];
|
||||
while (reader.BaseStream.Position < reader.BaseStream.Length)
|
||||
{
|
||||
byte ch = reader.ReadByte();
|
||||
if (ch == '\0')
|
||||
break;
|
||||
|
||||
buffer.Add(ch);
|
||||
}
|
||||
|
||||
return encoding.GetString([.. buffer]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated ASCII string from the underlying stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedAnsiString(this BinaryReader reader)
|
||||
{
|
||||
if (reader.BaseStream.Position >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull1Byte(reader);
|
||||
return Encoding.ASCII.GetString(buffer);
|
||||
}
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Read a null-terminated Latin1 string from the underlying stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedLatin1String(this BinaryReader reader)
|
||||
{
|
||||
if (reader.BaseStream.Position >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull1Byte(reader);
|
||||
return Encoding.Latin1.GetString(buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated UTF-8 string from the underlying stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedUTF8String(this BinaryReader reader)
|
||||
{
|
||||
if (reader.BaseStream.Position >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull1Byte(reader);
|
||||
return Encoding.ASCII.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated UTF-16 (Unicode) string from the underlying stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedUnicodeString(this BinaryReader reader)
|
||||
{
|
||||
if (reader.BaseStream.Position >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull2Byte(reader);
|
||||
return Encoding.Unicode.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated UTF-16 (Unicode) string from the underlying stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedBigEndianUnicodeString(this BinaryReader reader)
|
||||
{
|
||||
if (reader.BaseStream.Position >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull2Byte(reader);
|
||||
return Encoding.BigEndianUnicode.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated UTF-32 string from the underlying stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedUTF32String(this BinaryReader reader)
|
||||
{
|
||||
if (reader.BaseStream.Position >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull4Byte(reader);
|
||||
return Encoding.UTF32.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a byte-prefixed ASCII string from the underlying stream
|
||||
/// </summary>
|
||||
public static string? ReadPrefixedAnsiString(this BinaryReader reader)
|
||||
{
|
||||
if (reader.BaseStream.Position >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
byte size = reader.ReadByte();
|
||||
if (reader.BaseStream.Position + size >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = reader.ReadBytes(size);
|
||||
return Encoding.ASCII.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a ushort-prefixed Unicode string from the underlying stream
|
||||
/// </summary>
|
||||
public static string? ReadPrefixedUnicodeString(this BinaryReader reader)
|
||||
{
|
||||
if (reader.BaseStream.Position >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
ushort size = reader.ReadUInt16();
|
||||
if (reader.BaseStream.Position + (size * 2) >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = reader.ReadBytes(size * 2);
|
||||
return Encoding.Unicode.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a ushort-prefixed Unicode string from the underlying stream
|
||||
/// </summary>
|
||||
public static string? ReadPrefixedBigEndianUnicodeString(this BinaryReader reader)
|
||||
{
|
||||
if (reader.BaseStream.Position >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
ushort size = reader.ReadUInt16();
|
||||
if (reader.BaseStream.Position + (size * 2) >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = reader.ReadBytes(size * 2);
|
||||
return Encoding.BigEndianUnicode.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read bytes until a 1-byte null terminator is found
|
||||
/// </summary>
|
||||
private static byte[] ReadUntilNull1Byte(BinaryReader reader)
|
||||
{
|
||||
var bytes = new List<byte>();
|
||||
while (reader.BaseStream.Position < reader.BaseStream.Length)
|
||||
{
|
||||
byte next = reader.ReadByte();
|
||||
if (next == 0x00)
|
||||
break;
|
||||
|
||||
bytes.Add(next);
|
||||
}
|
||||
|
||||
return [.. bytes];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read bytes until a 2-byte null terminator is found
|
||||
/// </summary>
|
||||
private static byte[] ReadUntilNull2Byte(BinaryReader reader)
|
||||
{
|
||||
var bytes = new List<byte>();
|
||||
while (reader.BaseStream.Position < reader.BaseStream.Length)
|
||||
{
|
||||
ushort next = reader.ReadUInt16();
|
||||
if (next == 0x0000)
|
||||
break;
|
||||
|
||||
bytes.AddRange(BitConverter.GetBytes(next));
|
||||
}
|
||||
|
||||
return [.. bytes];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read bytes until a 4-byte null terminator is found
|
||||
/// </summary>
|
||||
private static byte[] ReadUntilNull4Byte(BinaryReader reader)
|
||||
{
|
||||
var bytes = new List<byte>();
|
||||
while (reader.BaseStream.Position < reader.BaseStream.Length)
|
||||
{
|
||||
uint next = reader.ReadUInt32();
|
||||
if (next == 0x00000000)
|
||||
break;
|
||||
|
||||
bytes.AddRange(BitConverter.GetBytes(next));
|
||||
}
|
||||
|
||||
return [.. bytes];
|
||||
}
|
||||
}
|
||||
}
|
||||
160
SabreTools.Text.Extensions/BinaryWriterExtensions.cs
Normal file
160
SabreTools.Text.Extensions/BinaryWriterExtensions.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace SabreTools.Text.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions for BinaryWriter
|
||||
/// </summary>
|
||||
public static class BinaryWriterExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Write a null-terminated string to the underlying stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedString(this BinaryWriter writer, string? value, Encoding encoding)
|
||||
{
|
||||
// If the value is null
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
// Add the null terminator and write
|
||||
value += "\0";
|
||||
byte[] buffer = encoding.GetBytes(value);
|
||||
return WriteFromBuffer(writer, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated ASCII string to the underlying stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedAnsiString(this BinaryWriter writer, string? value)
|
||||
=> writer.WriteNullTerminatedString(value, Encoding.ASCII);
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Write a null-terminated Latin1 string to the underlying stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedLatin1String(this BinaryWriter writer, string? value)
|
||||
=> writer.WriteNullTerminatedString(value, Encoding.Latin1);
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-8 string to the underlying stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedUTF8String(this BinaryWriter writer, string? value)
|
||||
=> writer.WriteNullTerminatedString(value, Encoding.UTF8);
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-16 (Unicode) string to the underlying stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedUnicodeString(this BinaryWriter writer, string? value)
|
||||
=> writer.WriteNullTerminatedString(value, Encoding.Unicode);
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-16 (Unicode) string to the underlying stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedBigEndianUnicodeString(this BinaryWriter writer, string? value)
|
||||
=> writer.WriteNullTerminatedString(value, Encoding.BigEndianUnicode);
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-32 string to the underlying stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedUTF32String(this BinaryWriter writer, string? value)
|
||||
=> writer.WriteNullTerminatedString(value, Encoding.UTF32);
|
||||
|
||||
/// <summary>
|
||||
/// Write a byte-prefixed ASCII string to the underlying stream
|
||||
/// </summary>
|
||||
public static bool WritePrefixedAnsiString(this BinaryWriter writer, string? value)
|
||||
{
|
||||
// If the value is null
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
// Get the buffer
|
||||
byte[] buffer = Encoding.ASCII.GetBytes(value);
|
||||
|
||||
// Write the length as a byte
|
||||
writer.Write((byte)value.Length);
|
||||
|
||||
// Write the buffer
|
||||
return WriteFromBuffer(writer, buffer);
|
||||
}
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Write a byte-prefixed Latin1 string to the underlying stream
|
||||
/// </summary>
|
||||
public static bool WritePrefixedLatin1String(this BinaryWriter writer, string? value)
|
||||
{
|
||||
// If the value is null
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
// Get the buffer
|
||||
byte[] buffer = Encoding.Latin1.GetBytes(value);
|
||||
|
||||
// Write the length as a byte
|
||||
writer.Write((byte)value.Length);
|
||||
|
||||
// Write the buffer
|
||||
return WriteFromBuffer(writer, buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Write a ushort-prefixed Unicode string to the underlying stream
|
||||
/// </summary>
|
||||
public static bool WritePrefixedUnicodeString(this BinaryWriter writer, string? value)
|
||||
{
|
||||
// If the value is null
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
// Get the buffer
|
||||
byte[] buffer = Encoding.Unicode.GetBytes(value);
|
||||
|
||||
// Write the length as a ushort
|
||||
writer.Write((ushort)value.Length);
|
||||
|
||||
// Write the buffer
|
||||
return WriteFromBuffer(writer, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a ushort-prefixed Unicode string to the underlying stream
|
||||
/// </summary>
|
||||
public static bool WritePrefixedBigEndianUnicodeString(this BinaryWriter writer, string? value)
|
||||
{
|
||||
// If the value is null
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
// Get the buffer
|
||||
byte[] buffer = Encoding.BigEndianUnicode.GetBytes(value);
|
||||
|
||||
// Write the length as a ushort
|
||||
writer.Write((ushort)value.Length);
|
||||
|
||||
// Write the buffer
|
||||
return WriteFromBuffer(writer, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an array of bytes to the underlying stream
|
||||
/// </summary>
|
||||
private static bool WriteFromBuffer(BinaryWriter writer, byte[] value)
|
||||
{
|
||||
// If the stream is not writable
|
||||
if (!writer.BaseStream.CanWrite)
|
||||
return false;
|
||||
|
||||
// Handle the 0-byte case
|
||||
if (value.Length == 0)
|
||||
return true;
|
||||
|
||||
// Handle the general case, forcing a write of the correct length
|
||||
writer.Write(value, 0, value.Length);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
244
SabreTools.Text.Extensions/ByteArrayReaderExtensions.cs
Normal file
244
SabreTools.Text.Extensions/ByteArrayReaderExtensions.cs
Normal file
@@ -0,0 +1,244 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using SabreTools.Numerics.Extensions;
|
||||
|
||||
namespace SabreTools.Text.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions for byte arrays
|
||||
/// </summary>
|
||||
public static class ByteArrayReaderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Read a null-terminated string from the array
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedString(this byte[] content, ref int offset, Encoding encoding)
|
||||
{
|
||||
// Short-circuit to explicit implementations
|
||||
if (encoding.CodePage == Encoding.ASCII.CodePage)
|
||||
return content.ReadNullTerminatedAnsiString(ref offset);
|
||||
#if NET5_0_OR_GREATER
|
||||
else if (encoding.CodePage == Encoding.Latin1.CodePage)
|
||||
return content.ReadNullTerminatedAnsiString(ref offset);
|
||||
#endif
|
||||
else if (encoding.CodePage == Encoding.UTF8.CodePage)
|
||||
return content.ReadNullTerminatedUTF8String(ref offset);
|
||||
else if (encoding.CodePage == Encoding.Unicode.CodePage)
|
||||
return content.ReadNullTerminatedUnicodeString(ref offset);
|
||||
else if (encoding.CodePage == Encoding.BigEndianUnicode.CodePage)
|
||||
return content.ReadNullTerminatedBigEndianUnicodeString(ref offset);
|
||||
else if (encoding.CodePage == Encoding.UTF32.CodePage)
|
||||
return content.ReadNullTerminatedUTF32String(ref offset);
|
||||
|
||||
if (offset >= content.Length)
|
||||
return null;
|
||||
|
||||
List<byte> buffer = [];
|
||||
while (offset < content.Length)
|
||||
{
|
||||
byte ch = content.ReadByteValue(ref offset);
|
||||
if (ch == '\0')
|
||||
break;
|
||||
|
||||
buffer.Add(ch);
|
||||
}
|
||||
|
||||
return encoding.GetString([.. buffer]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated ASCII string from the byte array
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedAnsiString(this byte[] content, ref int offset)
|
||||
{
|
||||
if (offset >= content.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull1Byte(content, ref offset);
|
||||
return Encoding.ASCII.GetString(buffer);
|
||||
}
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Read a null-terminated Latin1 string from the byte array
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedLatin1String(this byte[] content, ref int offset)
|
||||
{
|
||||
if (offset >= content.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull1Byte(content, ref offset);
|
||||
return Encoding.Latin1.GetString(buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated UTF-8 string from the byte array
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedUTF8String(this byte[] content, ref int offset)
|
||||
{
|
||||
if (offset >= content.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull1Byte(content, ref offset);
|
||||
return Encoding.UTF8.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated UTF-16 (Unicode) string from the byte array
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedUnicodeString(this byte[] content, ref int offset)
|
||||
{
|
||||
if (offset >= content.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull2Byte(content, ref offset);
|
||||
return Encoding.Unicode.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated UTF-16 (Unicode) string from the byte array
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedBigEndianUnicodeString(this byte[] content, ref int offset)
|
||||
{
|
||||
if (offset >= content.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull2Byte(content, ref offset);
|
||||
return Encoding.BigEndianUnicode.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated UTF-32 string from the byte array
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedUTF32String(this byte[] content, ref int offset)
|
||||
{
|
||||
if (offset >= content.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull4Byte(content, ref offset);
|
||||
return Encoding.UTF32.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a byte-prefixed ASCII string from the byte array
|
||||
/// </summary>
|
||||
public static string? ReadPrefixedAnsiString(this byte[] content, ref int offset)
|
||||
{
|
||||
if (offset >= content.Length)
|
||||
return null;
|
||||
|
||||
byte size = content.ReadByteValue(ref offset);
|
||||
if (offset + size >= content.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = content.ReadBytes(ref offset, size);
|
||||
return Encoding.ASCII.GetString(buffer);
|
||||
}
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Read a byte-prefixed Latin1 string from the byte array
|
||||
/// </summary>
|
||||
public static string? ReadPrefixedLatin1String(this byte[] content, ref int offset)
|
||||
{
|
||||
if (offset >= content.Length)
|
||||
return null;
|
||||
|
||||
byte size = content.ReadByteValue(ref offset);
|
||||
if (offset + size >= content.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = content.ReadBytes(ref offset, size);
|
||||
return Encoding.Latin1.GetString(buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Read a ushort-prefixed Unicode string from the byte array
|
||||
/// </summary>
|
||||
public static string? ReadPrefixedUnicodeString(this byte[] content, ref int offset)
|
||||
{
|
||||
if (offset >= content.Length)
|
||||
return null;
|
||||
|
||||
ushort size = content.ReadUInt16(ref offset);
|
||||
if (offset + (size * 2) >= content.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = content.ReadBytes(ref offset, size * 2);
|
||||
return Encoding.Unicode.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a ushort-prefixed Unicode string from the byte array
|
||||
/// </summary>
|
||||
public static string? ReadPrefixedBigEndianUnicodeString(this byte[] content, ref int offset)
|
||||
{
|
||||
if (offset >= content.Length)
|
||||
return null;
|
||||
|
||||
ushort size = content.ReadUInt16(ref offset);
|
||||
if (offset + (size * 2) >= content.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = content.ReadBytes(ref offset, size * 2);
|
||||
return Encoding.BigEndianUnicode.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read bytes until a 1-byte null terminator is found
|
||||
/// </summary>
|
||||
private static byte[] ReadUntilNull1Byte(byte[] content, ref int offset)
|
||||
{
|
||||
var bytes = new List<byte>();
|
||||
while (offset < content.Length)
|
||||
{
|
||||
byte next = content.ReadByte(ref offset);
|
||||
if (next == 0x00)
|
||||
break;
|
||||
|
||||
bytes.Add(next);
|
||||
}
|
||||
|
||||
return [.. bytes];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read bytes until a 2-byte null terminator is found
|
||||
/// </summary>
|
||||
private static byte[] ReadUntilNull2Byte(byte[] content, ref int offset)
|
||||
{
|
||||
var bytes = new List<byte>();
|
||||
while (offset < content.Length)
|
||||
{
|
||||
ushort next = content.ReadUInt16(ref offset);
|
||||
if (next == 0x0000)
|
||||
break;
|
||||
|
||||
bytes.AddRange(BitConverter.GetBytes(next));
|
||||
}
|
||||
|
||||
return [.. bytes];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read bytes until a 4-byte null terminator is found
|
||||
/// </summary>
|
||||
private static byte[] ReadUntilNull4Byte(byte[] content, ref int offset)
|
||||
{
|
||||
var bytes = new List<byte>();
|
||||
while (offset < content.Length)
|
||||
{
|
||||
uint next = content.ReadUInt32(ref offset);
|
||||
if (next == 0x00000000)
|
||||
break;
|
||||
|
||||
bytes.AddRange(BitConverter.GetBytes(next));
|
||||
}
|
||||
|
||||
return [.. bytes];
|
||||
}
|
||||
}
|
||||
}
|
||||
171
SabreTools.Text.Extensions/ByteArrayWriterExtensions.cs
Normal file
171
SabreTools.Text.Extensions/ByteArrayWriterExtensions.cs
Normal file
@@ -0,0 +1,171 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using SabreTools.Numerics.Extensions;
|
||||
|
||||
namespace SabreTools.Text.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions for byte arrays
|
||||
/// </summary>
|
||||
public static class ByteArrayWriterExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Write a null-terminated string to the array
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedString(this byte[] content, ref int offset, string? value, Encoding encoding)
|
||||
{
|
||||
// If the value is null
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
// Add the null terminator and write
|
||||
value += "\0";
|
||||
byte[] buffer = encoding.GetBytes(value);
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated ASCII string to the byte array
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedAnsiString(this byte[] content, ref int offset, string? value)
|
||||
=> content.WriteNullTerminatedString(ref offset, value, Encoding.ASCII);
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Write a null-terminated Latin1 string to the byte array
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedLatin1String(this byte[] content, ref int offset, string? value)
|
||||
=> content.WriteNullTerminatedString(ref offset, value, Encoding.Latin1);
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-8 string to the byte array
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedUTF8String(this byte[] content, ref int offset, string? value)
|
||||
=> content.WriteNullTerminatedString(ref offset, value, Encoding.UTF8);
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-16 (Unicode) string to the byte array
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedUnicodeString(this byte[] content, ref int offset, string? value)
|
||||
=> content.WriteNullTerminatedString(ref offset, value, Encoding.Unicode);
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-16 (Unicode) string to the byte array
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedBigEndianUnicodeString(this byte[] content, ref int offset, string? value)
|
||||
=> content.WriteNullTerminatedString(ref offset, value, Encoding.BigEndianUnicode);
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-32 string to the byte array
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedUTF32String(this byte[] content, ref int offset, string? value)
|
||||
=> content.WriteNullTerminatedString(ref offset, value, Encoding.UTF32);
|
||||
|
||||
/// <summary>
|
||||
/// Write a byte-prefixed ASCII string to the byte array
|
||||
/// </summary>
|
||||
public static bool WritePrefixedAnsiString(this byte[] content, ref int offset, string? value)
|
||||
{
|
||||
// If the value is null
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
// Get the buffer
|
||||
byte[] buffer = Encoding.ASCII.GetBytes(value);
|
||||
|
||||
// Write the length as a byte
|
||||
if (!content.Write(ref offset, (byte)value.Length))
|
||||
return false;
|
||||
|
||||
// Write the buffer
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Write a byte-prefixed Latin1 string to the byte array
|
||||
/// </summary>
|
||||
public static bool WritePrefixedLatin1String(this byte[] content, ref int offset, string? value)
|
||||
{
|
||||
// If the value is null
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
// Get the buffer
|
||||
byte[] buffer = Encoding.Latin1.GetBytes(value);
|
||||
|
||||
// Write the length as a byte
|
||||
if (!content.Write(ref offset, (byte)value.Length))
|
||||
return false;
|
||||
|
||||
// Write the buffer
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Write a ushort-prefixed Unicode string to the byte array
|
||||
/// </summary>
|
||||
public static bool WritePrefixedUnicodeString(this byte[] content, ref int offset, string? value)
|
||||
{
|
||||
// If the value is null
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
// Get the buffer
|
||||
byte[] buffer = Encoding.Unicode.GetBytes(value);
|
||||
|
||||
// Write the length as a ushort
|
||||
if (!content.Write(ref offset, (ushort)value.Length))
|
||||
return false;
|
||||
|
||||
// Write the buffer
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a ushort-prefixed Unicode string to the byte array
|
||||
/// </summary>
|
||||
public static bool WritePrefixedBigEndianUnicodeString(this byte[] content, ref int offset, string? value)
|
||||
{
|
||||
// If the value is null
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
// Get the buffer
|
||||
byte[] buffer = Encoding.BigEndianUnicode.GetBytes(value);
|
||||
|
||||
// Write the length as a ushort
|
||||
if (!content.Write(ref offset, (ushort)value.Length))
|
||||
return false;
|
||||
|
||||
// Write the buffer
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an array of bytes to the byte array
|
||||
/// </summary>
|
||||
/// <exception cref="System.IO.EndOfStreamException">
|
||||
/// Thrown if <paramref name="offset"/> into <paramref name="content"/>
|
||||
/// would not accomodate <paramref name="value"/>.
|
||||
/// </exception>
|
||||
private static bool WriteFromBuffer(byte[] content, ref int offset, byte[] value)
|
||||
{
|
||||
// Handle the 0-byte case
|
||||
if (value.Length == 0)
|
||||
return true;
|
||||
|
||||
// If there are not enough bytes
|
||||
if (offset + value.Length > content.Length)
|
||||
throw new System.IO.EndOfStreamException(nameof(content));
|
||||
|
||||
// Handle the general case, forcing a write of the correct length
|
||||
Array.Copy(value, 0, content, offset, value.Length);
|
||||
offset += value.Length;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,5 +4,13 @@ This library contains various extensions and helper classes for dealing with str
|
||||
|
||||
| Class | Description |
|
||||
| --- | --- |
|
||||
| `BinaryReaderExtensions` | Extensions to `BinaryReader` for specialized string reading. |
|
||||
| `BinaryWriterExtensions` | Extensions to `BinaryWriter` for specialized string writing. |
|
||||
| `ByteArrayReaderExtensions` | Extensions to `byte[]` for specialized string reading. |
|
||||
| `ByteArrayWriterExtensions` | Extensions to `byte[]` for specialized string writing. |
|
||||
| `NumberHelper` | Numeric string helpers, including suffix parsing and human-readable outputs. |
|
||||
| `StreamReaderExtensions` | Extensions to `Stream` for specialized string reading. |
|
||||
| `StreamWriterExtensions` | Extensions to `Stream` for specialized string writing. |
|
||||
| `StringBuilderExtensions` | Formatting overloads for `StringBuilder` to allow for type-specific handling. |
|
||||
| `TextHelper` | String normalization commonly used in other projects. |
|
||||
| `XmlTextWriterExtensions` | Overloads to `XmlTextWriter.WriteAttributeString` and `XmlTextWriter.WriteElementString`. |
|
||||
|
||||
@@ -28,4 +28,8 @@
|
||||
<None Include="README.md" Pack="true" PackagePath="" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SabreTools.Numerics.Extensions\SabreTools.Numerics.Extensions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
245
SabreTools.Text.Extensions/StreamReaderExtensions.cs
Normal file
245
SabreTools.Text.Extensions/StreamReaderExtensions.cs
Normal file
@@ -0,0 +1,245 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.Numerics.Extensions;
|
||||
|
||||
namespace SabreTools.Text.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions for Streams
|
||||
/// </summary>
|
||||
public static class StreamReaderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Read a null-terminated string from the stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedString(this Stream stream, Encoding encoding)
|
||||
{
|
||||
// Short-circuit to explicit implementations
|
||||
if (encoding.CodePage == Encoding.ASCII.CodePage)
|
||||
return stream.ReadNullTerminatedAnsiString();
|
||||
#if NET5_0_OR_GREATER
|
||||
else if (encoding.CodePage == Encoding.Latin1.CodePage)
|
||||
return stream.ReadNullTerminatedLatin1String();
|
||||
#endif
|
||||
else if (encoding.CodePage == Encoding.UTF8.CodePage)
|
||||
return stream.ReadNullTerminatedUTF8String();
|
||||
else if (encoding.CodePage == Encoding.Unicode.CodePage)
|
||||
return stream.ReadNullTerminatedUnicodeString();
|
||||
else if (encoding.CodePage == Encoding.BigEndianUnicode.CodePage)
|
||||
return stream.ReadNullTerminatedBigEndianUnicodeString();
|
||||
else if (encoding.CodePage == Encoding.UTF32.CodePage)
|
||||
return stream.ReadNullTerminatedUTF32String();
|
||||
|
||||
if (stream.Position >= stream.Length)
|
||||
return null;
|
||||
|
||||
List<byte> buffer = [];
|
||||
while (stream.Position < stream.Length)
|
||||
{
|
||||
byte ch = stream.ReadByteValue();
|
||||
if (ch == '\0')
|
||||
break;
|
||||
|
||||
buffer.Add(ch);
|
||||
}
|
||||
|
||||
return encoding.GetString([.. buffer]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated ASCII string from the stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedAnsiString(this Stream stream)
|
||||
{
|
||||
if (stream.Position >= stream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull1Byte(stream);
|
||||
return Encoding.ASCII.GetString(buffer);
|
||||
}
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Read a null-terminated Latin1 string from the stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedLatin1String(this Stream stream)
|
||||
{
|
||||
if (stream.Position >= stream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull1Byte(stream);
|
||||
return Encoding.Latin1.GetString(buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated UTF-8 string from the stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedUTF8String(this Stream stream)
|
||||
{
|
||||
if (stream.Position >= stream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull1Byte(stream);
|
||||
return Encoding.UTF8.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated UTF-16 (Unicode) string from the stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedUnicodeString(this Stream stream)
|
||||
{
|
||||
if (stream.Position >= stream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull2Byte(stream);
|
||||
return Encoding.Unicode.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated UTF-16 (Unicode) string from the stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedBigEndianUnicodeString(this Stream stream)
|
||||
{
|
||||
if (stream.Position >= stream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull2Byte(stream);
|
||||
return Encoding.BigEndianUnicode.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated UTF-32 string from the stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedUTF32String(this Stream stream)
|
||||
{
|
||||
if (stream.Position >= stream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull4Byte(stream);
|
||||
return Encoding.UTF32.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a byte-prefixed ASCII string from the stream
|
||||
/// </summary>
|
||||
public static string? ReadPrefixedAnsiString(this Stream stream)
|
||||
{
|
||||
if (stream.Position >= stream.Length)
|
||||
return null;
|
||||
|
||||
byte size = stream.ReadByteValue();
|
||||
if (stream.Position + size >= stream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = stream.ReadBytes(size);
|
||||
return Encoding.ASCII.GetString(buffer);
|
||||
}
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Read a byte-prefixed Latin1 string from the stream
|
||||
/// </summary>
|
||||
public static string? ReadPrefixedLatin1String(this Stream stream)
|
||||
{
|
||||
if (stream.Position >= stream.Length)
|
||||
return null;
|
||||
|
||||
byte size = stream.ReadByteValue();
|
||||
if (stream.Position + size >= stream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = stream.ReadBytes(size);
|
||||
return Encoding.Latin1.GetString(buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Read a ushort-prefixed Unicode string from the stream
|
||||
/// </summary>
|
||||
public static string? ReadPrefixedUnicodeString(this Stream stream)
|
||||
{
|
||||
if (stream.Position >= stream.Length)
|
||||
return null;
|
||||
|
||||
ushort size = stream.ReadUInt16();
|
||||
if (stream.Position + (size * 2) >= stream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = stream.ReadBytes(size * 2);
|
||||
return Encoding.Unicode.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a ushort-prefixed Unicode string from the stream
|
||||
/// </summary>
|
||||
public static string? ReadPrefixedBigEndianUnicodeString(this Stream stream)
|
||||
{
|
||||
if (stream.Position >= stream.Length)
|
||||
return null;
|
||||
|
||||
ushort size = stream.ReadUInt16();
|
||||
if (stream.Position + (size * 2) >= stream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = stream.ReadBytes(size * 2);
|
||||
return Encoding.BigEndianUnicode.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read bytes until a 1-byte null terminator is found
|
||||
/// </summary>
|
||||
private static byte[] ReadUntilNull1Byte(Stream stream)
|
||||
{
|
||||
var bytes = new List<byte>();
|
||||
while (stream.Position < stream.Length)
|
||||
{
|
||||
byte next = stream.ReadByteValue();
|
||||
if (next == 0x00)
|
||||
break;
|
||||
|
||||
bytes.Add(next);
|
||||
}
|
||||
|
||||
return [.. bytes];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read bytes until a 2-byte null terminator is found
|
||||
/// </summary>
|
||||
private static byte[] ReadUntilNull2Byte(Stream stream)
|
||||
{
|
||||
var bytes = new List<byte>();
|
||||
while (stream.Position < stream.Length)
|
||||
{
|
||||
ushort next = stream.ReadUInt16();
|
||||
if (next == 0x0000)
|
||||
break;
|
||||
|
||||
bytes.AddRange(BitConverter.GetBytes(next));
|
||||
}
|
||||
|
||||
return [.. bytes];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read bytes until a 4-byte null terminator is found
|
||||
/// </summary>
|
||||
private static byte[] ReadUntilNull4Byte(Stream stream)
|
||||
{
|
||||
var bytes = new List<byte>();
|
||||
while (stream.Position < stream.Length)
|
||||
{
|
||||
uint next = stream.ReadUInt32();
|
||||
if (next == 0x00000000)
|
||||
break;
|
||||
|
||||
bytes.AddRange(BitConverter.GetBytes(next));
|
||||
}
|
||||
|
||||
return [.. bytes];
|
||||
}
|
||||
}
|
||||
}
|
||||
165
SabreTools.Text.Extensions/StreamWriterExtensions.cs
Normal file
165
SabreTools.Text.Extensions/StreamWriterExtensions.cs
Normal file
@@ -0,0 +1,165 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.Numerics.Extensions;
|
||||
|
||||
namespace SabreTools.Text.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions for Streams
|
||||
/// </summary>
|
||||
public static class StreamWriterExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Write a null-terminated string to the stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedString(this Stream stream, string? value, Encoding encoding)
|
||||
{
|
||||
// If the value is null
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
// Add the null terminator and write
|
||||
value += "\0";
|
||||
byte[] buffer = encoding.GetBytes(value);
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated ASCII string to the stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedAnsiString(this Stream stream, string? value)
|
||||
=> stream.WriteNullTerminatedString(value, Encoding.ASCII);
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Write a null-terminated Latin1 string to the stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedLatin1String(this Stream stream, string? value)
|
||||
=> stream.WriteNullTerminatedString(value, Encoding.Latin1);
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-8 string to the stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedUTF8String(this Stream stream, string? value)
|
||||
=> stream.WriteNullTerminatedString(value, Encoding.UTF8);
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-16 (Unicode) string to the stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedUnicodeString(this Stream stream, string? value)
|
||||
=> stream.WriteNullTerminatedString(value, Encoding.Unicode);
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-16 (Unicode) string to the stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedBigEndianUnicodeString(this Stream stream, string? value)
|
||||
=> stream.WriteNullTerminatedString(value, Encoding.BigEndianUnicode);
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-32 string to the stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedUTF32String(this Stream stream, string? value)
|
||||
=> stream.WriteNullTerminatedString(value, Encoding.UTF32);
|
||||
|
||||
//// <summary>
|
||||
/// Write a byte-prefixed ASCII string to the stream
|
||||
/// </summary>
|
||||
public static bool WritePrefixedAnsiString(this Stream stream, string? value)
|
||||
{
|
||||
// If the value is null
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
// Get the buffer
|
||||
byte[] buffer = Encoding.ASCII.GetBytes(value);
|
||||
|
||||
// Write the length as a byte
|
||||
if (!stream.Write((byte)value.Length))
|
||||
return false;
|
||||
|
||||
// Write the buffer
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
//// <summary>
|
||||
/// Write a byte-prefixed Latin1 string to the stream
|
||||
/// </summary>
|
||||
public static bool WritePrefixedLatin1String(this Stream stream, string? value)
|
||||
{
|
||||
// If the value is null
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
// Get the buffer
|
||||
byte[] buffer = Encoding.Latin1.GetBytes(value);
|
||||
|
||||
// Write the length as a byte
|
||||
if (!stream.Write((byte)value.Length))
|
||||
return false;
|
||||
|
||||
// Write the buffer
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Write a ushort-prefixed Unicode string to the stream
|
||||
/// </summary>
|
||||
public static bool WritePrefixedUnicodeString(this Stream stream, string? value)
|
||||
{
|
||||
// If the value is null
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
// Get the buffer
|
||||
byte[] buffer = Encoding.Unicode.GetBytes(value);
|
||||
|
||||
// Write the length as a ushort
|
||||
if (!stream.Write((ushort)value.Length))
|
||||
return false;
|
||||
|
||||
// Write the buffer
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a ushort-prefixed Unicode string to the stream
|
||||
/// </summary>
|
||||
public static bool WritePrefixedBigEndianUnicodeString(this Stream stream, string? value)
|
||||
{
|
||||
// If the value is null
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
// Get the buffer
|
||||
byte[] buffer = Encoding.BigEndianUnicode.GetBytes(value);
|
||||
|
||||
// Write the length as a ushort
|
||||
if (!stream.Write((ushort)value.Length))
|
||||
return false;
|
||||
|
||||
// Write the buffer
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an array of bytes to the stream
|
||||
/// </summary>
|
||||
private static bool WriteFromBuffer(Stream stream, byte[] value)
|
||||
{
|
||||
// If the stream is not writable
|
||||
if (!stream.CanWrite)
|
||||
return false;
|
||||
|
||||
// Handle the 0-byte case
|
||||
if (value.Length == 0)
|
||||
return true;
|
||||
|
||||
// Handle the general case, forcing a write of the correct length
|
||||
stream.Write(value, 0, value.Length);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ using System;
|
||||
using System.Text;
|
||||
using SabreTools.Numerics;
|
||||
|
||||
namespace SabreTools.IO.Extensions
|
||||
namespace SabreTools.Text.Extensions
|
||||
{
|
||||
// TODO: Add extension for printing enums, if possible
|
||||
public static class StringBuilderExtensions
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace SabreTools.IO.Extensions
|
||||
namespace SabreTools.Text.Extensions
|
||||
{
|
||||
public static class StringExtensions
|
||||
{
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Xml;
|
||||
|
||||
namespace SabreTools.IO.Extensions
|
||||
namespace SabreTools.Text.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Additional methods for XmlTextWriter
|
||||
Reference in New Issue
Block a user