diff --git a/README.md b/README.md index 05253bc..6a1e74a 100644 --- a/README.md +++ b/README.md @@ -54,13 +54,13 @@ class MyBasicObj // String encoded in ASCII // Reads chars until the stream encounters a '\0' // Writing will append a '\0' at the end of the string - [BinaryEncoding(EncodingType.ASCII)] + [BinaryEncoding("ASCII")] [BinaryStringNullTerminated(true)] public string NullTerminatedASCIIString { get; set; } // String encoded in UTF16-LE that will only read/write 10 chars // The BinaryStringTrimNullTerminatorsAttribute will indicate that every char from the first \0 will be removed from the string. This attribute also works with char arrays - [BinaryEncoding(EncodingType.UTF16)] + [BinaryEncoding("UTF-16")] [BinaryStringFixedLength(10)] [BinaryStringTrimNullTerminators(true)] public string UTF16String { get; set; } @@ -111,8 +111,8 @@ obj.ArrayWith16Elements = reader.ReadUInt32s(16); // Reads 16 'uint's (4 bytes e obj.Bool32 = reader.ReadBoolean(); // Reads a 'bool' (4 bytes in this case, since the reader was initiated with a default of BooleanSize.U32, but there is an overload to pass in one) -obj.NullTerminatedASCIIString = reader.ReadStringNullTerminated(EncodingType.ASCII); // Reads ASCII chars until a '\0' is read, then returns a 'string' -obj.UTF16String = reader.ReadString(10, true, EncodingType.UTF16); // Reads 10 UTF16-LE chars as a 'string' with the '\0's removed +obj.NullTerminatedASCIIString = reader.ReadStringNullTerminated(Encoding.ASCII); // Reads ASCII chars until a '\0' is read, then returns a 'string' +obj.UTF16String = reader.ReadString(10, true, Encoding.Unicode); // Reads 10 UTF16-LE chars as a 'string' with the '\0's removed ``` ### Reading Automatically (With Reflection): ```cs @@ -148,8 +148,8 @@ writer.Write(obj.Version); // Writes a 'short' (2 bytes) writer.Write(obj.Date); // Writes a 'DateTime' (8 bytes) writer.Write(obj.ArrayWith16Elements); // Writes 16 'uint's (4 bytes each) writer.Write(obj.Bool32); // Writes a 'bool' (4 bytes in this case, since the reader was initiated with a default of BooleanSize.U32, but there is an overload to pass in one) -writer.Write(obj.NullTerminatedASCIIString, true, EncodingType.ASCII); // Writes the chars in the 'string' as ASCII and appends a '\0' at the end -writer.Write(obj.UTF16String, 10, EncodingType.UTF16); // Writes 10 UTF16-LE chars as a 'string'. If the string has more than 10 chars, it is truncated; if it has less, it is padded with '\0' +writer.Write(obj.NullTerminatedASCIIString, true, Encoding.ASCII); // Writes the chars in the 'string' as ASCII and appends a '\0' at the end +writer.Write(obj.UTF16String, 10, Encoding.Unicode); // Writes 10 UTF16-LE chars as a 'string'. If the string has more than 10 chars, it is truncated; if it has less, it is padded with '\0' ``` ### Writing Automatically (With Reflection): ```cs diff --git a/Source/Attributes.cs b/Source/Attributes.cs index 20a9726..8e3e98f 100644 --- a/Source/Attributes.cs +++ b/Source/Attributes.cs @@ -1,4 +1,5 @@ using System; +using System.Text; namespace Kermalis.EndianBinaryIO { @@ -22,7 +23,7 @@ public sealed class BinaryBooleanSizeAttribute : Attribute, IBinaryAttribute= BooleanSize.MAX) { @@ -32,17 +33,17 @@ public BinaryBooleanSizeAttribute(BooleanSize booleanSize = BooleanSize.U8) } } [AttributeUsage(AttributeTargets.Property)] - public sealed class BinaryEncodingAttribute : Attribute, IBinaryAttribute + public sealed class BinaryEncodingAttribute : Attribute, IBinaryAttribute { - public EncodingType Value { get; } + public Encoding Value { get; } - public BinaryEncodingAttribute(EncodingType encodingType = EncodingType.ASCII) + public BinaryEncodingAttribute(string encodingName) { - if (encodingType >= EncodingType.MAX) - { - throw new ArgumentOutOfRangeException($"{nameof(BinaryEncodingAttribute)} cannot be created with a size of {encodingType}."); - } - Value = encodingType; + Value = Encoding.GetEncoding(encodingName); + } + public BinaryEncodingAttribute(int encodingCodepage) + { + Value = Encoding.GetEncoding(encodingCodepage); } } [AttributeUsage(AttributeTargets.Property)] diff --git a/Source/EndianBinaryReader.cs b/Source/EndianBinaryReader.cs index 291b00e..1acb3fa 100644 --- a/Source/EndianBinaryReader.cs +++ b/Source/EndianBinaryReader.cs @@ -21,19 +21,6 @@ public Endianness Endianness _endianness = value; } } - private EncodingType _encoding; - public EncodingType Encoding - { - get => _encoding; - set - { - if (value >= EncodingType.MAX) - { - throw new ArgumentOutOfRangeException(nameof(value)); - } - _encoding = value; - } - } private BooleanSize _booleanSize; public BooleanSize BooleanSize { @@ -47,10 +34,11 @@ public BooleanSize BooleanSize _booleanSize = value; } } + public Encoding Encoding { get; set; } private byte[] _buffer; - public EndianBinaryReader(Stream baseStream, Endianness endianness = Endianness.LittleEndian, EncodingType encoding = EncodingType.ASCII, BooleanSize booleanSize = BooleanSize.U8) + public EndianBinaryReader(Stream baseStream, Endianness endianness = Endianness.LittleEndian, BooleanSize booleanSize = BooleanSize.U8) { if (baseStream is null) { @@ -62,8 +50,23 @@ public EndianBinaryReader(Stream baseStream, Endianness endianness = Endianness. } BaseStream = baseStream; Endianness = endianness; - Encoding = encoding; BooleanSize = booleanSize; + Encoding = Encoding.Default; + } + public EndianBinaryReader(Stream baseStream, Encoding encoding, Endianness endianness = Endianness.LittleEndian, BooleanSize booleanSize = BooleanSize.U8) + { + if (baseStream is null) + { + throw new ArgumentNullException(nameof(baseStream)); + } + if (!baseStream.CanRead) + { + throw new ArgumentException(nameof(baseStream)); + } + BaseStream = baseStream; + Endianness = endianness; + BooleanSize = booleanSize; + Encoding = encoding; } private void ReadBytesIntoBuffer(int byteCount) @@ -77,9 +80,9 @@ private void ReadBytesIntoBuffer(int byteCount) throw new EndOfStreamException(); } } - private char[] DecodeChars(EncodingType encodingType, int charCount) + private char[] DecodeChars(Encoding encoding, int charCount) { - Encoding encoding = Utils.EncodingFromEnum(encodingType); + Utils.ThrowIfCannotUseEncoding(encoding); int maxBytes = encoding.GetMaxByteCount(charCount); byte[] buffer = new byte[maxBytes]; int amtRead = BaseStream.Read(buffer, 0, maxBytes); // Do not throw EndOfStreamException if there aren't enough bytes at the end of the stream @@ -97,7 +100,7 @@ private char[] DecodeChars(EncodingType encodingType, int charCount) // For example, if we want 1 char and the max bytes is 2, but we manage to read 2 1-byte chars, we'd want to shrink back to 1 char Array.Resize(ref chars, charCount); int actualBytes = encoding.GetByteCount(chars); - if (maxBytes != actualBytes) + if (amtRead != actualBytes) { BaseStream.Position -= amtRead - actualBytes; // Set the stream back to compensate for the extra bytes we read } @@ -140,17 +143,17 @@ public char PeekChar(long offset) BaseStream.Position = offset; return PeekChar(); } - public char PeekChar(EncodingType encodingType) + public char PeekChar(Encoding encoding) { long pos = BaseStream.Position; - char c = ReadChar(encodingType); + char c = ReadChar(encoding); BaseStream.Position = pos; return c; } - public char PeekChar(EncodingType encodingType, long offset) + public char PeekChar(Encoding encoding, long offset) { BaseStream.Position = offset; - return PeekChar(encodingType); + return PeekChar(encoding); } public bool ReadBoolean() @@ -280,14 +283,14 @@ public char ReadChar(long offset) BaseStream.Position = offset; return ReadChar(); } - public char ReadChar(EncodingType encodingType) + public char ReadChar(Encoding encoding) { - return DecodeChars(encodingType, 1)[0]; + return DecodeChars(encoding, 1)[0]; } - public char ReadChar(EncodingType encodingType, long offset) + public char ReadChar(Encoding encoding, long offset) { BaseStream.Position = offset; - return ReadChar(encodingType); + return ReadChar(encoding); } public char[] ReadChars(int count, bool trimNullTerminators) { @@ -298,13 +301,13 @@ public char[] ReadChars(int count, bool trimNullTerminators, long offset) BaseStream.Position = offset; return ReadChars(count, trimNullTerminators); } - public char[] ReadChars(int count, bool trimNullTerminators, EncodingType encodingType) + public char[] ReadChars(int count, bool trimNullTerminators, Encoding encoding) { if (Utils.ValidateReadArraySize(count, out char[] array)) { return array; } - array = DecodeChars(encodingType, count); + array = DecodeChars(encoding, count); if (trimNullTerminators) { int i = Array.IndexOf(array, '\0'); @@ -315,10 +318,10 @@ public char[] ReadChars(int count, bool trimNullTerminators, EncodingType encodi } return array; } - public char[] ReadChars(int count, bool trimNullTerminators, EncodingType encodingType, long offset) + public char[] ReadChars(int count, bool trimNullTerminators, Encoding encoding, long offset) { BaseStream.Position = offset; - return ReadChars(count, trimNullTerminators, encodingType); + return ReadChars(count, trimNullTerminators, encoding); } public string ReadStringNullTerminated() { @@ -329,12 +332,12 @@ public string ReadStringNullTerminated(long offset) BaseStream.Position = offset; return ReadStringNullTerminated(); } - public string ReadStringNullTerminated(EncodingType encodingType) + public string ReadStringNullTerminated(Encoding encoding) { string text = string.Empty; while (true) { - char c = ReadChar(encodingType); + char c = ReadChar(encoding); if (c == '\0') { break; @@ -343,10 +346,10 @@ public string ReadStringNullTerminated(EncodingType encodingType) } return text; } - public string ReadStringNullTerminated(EncodingType encodingType, long offset) + public string ReadStringNullTerminated(Encoding encoding, long offset) { BaseStream.Position = offset; - return ReadStringNullTerminated(encodingType); + return ReadStringNullTerminated(encoding); } public string ReadString(int charCount, bool trimNullTerminators) { @@ -357,14 +360,14 @@ public string ReadString(int charCount, bool trimNullTerminators, long offset) BaseStream.Position = offset; return ReadString(charCount, trimNullTerminators); } - public string ReadString(int charCount, bool trimNullTerminators, EncodingType encodingType) + public string ReadString(int charCount, bool trimNullTerminators, Encoding encoding) { - return new string(ReadChars(charCount, trimNullTerminators, encodingType)); + return new string(ReadChars(charCount, trimNullTerminators, encoding)); } - public string ReadString(int charCount, bool trimNullTerminators, EncodingType encodingType, long offset) + public string ReadString(int charCount, bool trimNullTerminators, Encoding encoding, long offset) { BaseStream.Position = offset; - return ReadString(charCount, trimNullTerminators, encodingType); + return ReadString(charCount, trimNullTerminators, encoding); } public string[] ReadStringsNullTerminated(int count) { @@ -375,22 +378,22 @@ public string[] ReadStringsNullTerminated(int count, long offset) BaseStream.Position = offset; return ReadStringsNullTerminated(count); } - public string[] ReadStringsNullTerminated(int count, EncodingType encodingType) + public string[] ReadStringsNullTerminated(int count, Encoding encoding) { if (!Utils.ValidateReadArraySize(count, out string[] array)) { array = new string[count]; for (int i = 0; i < count; i++) { - array[i] = ReadStringNullTerminated(encodingType); + array[i] = ReadStringNullTerminated(encoding); } } return array; } - public string[] ReadStringsNullTerminated(int count, EncodingType encodingType, long offset) + public string[] ReadStringsNullTerminated(int count, Encoding encoding, long offset) { BaseStream.Position = offset; - return ReadStringsNullTerminated(count, encodingType); + return ReadStringsNullTerminated(count, encoding); } public string[] ReadStrings(int count, int charCount, bool trimNullTerminators) { @@ -401,22 +404,22 @@ public string[] ReadStrings(int count, int charCount, bool trimNullTerminators, BaseStream.Position = offset; return ReadStrings(count, charCount, trimNullTerminators); } - public string[] ReadStrings(int count, int charCount, bool trimNullTerminators, EncodingType encodingType) + public string[] ReadStrings(int count, int charCount, bool trimNullTerminators, Encoding encoding) { if (!Utils.ValidateReadArraySize(count, out string[] array)) { array = new string[count]; for (int i = 0; i < count; i++) { - array[i] = ReadString(charCount, trimNullTerminators, encodingType); + array[i] = ReadString(charCount, trimNullTerminators, encoding); } } return array; } - public string[] ReadStrings(int count, int charCount, bool trimNullTerminators, EncodingType encodingType, long offset) + public string[] ReadStrings(int count, int charCount, bool trimNullTerminators, Encoding encoding, long offset) { BaseStream.Position = offset; - return ReadStrings(count, charCount, trimNullTerminators, encodingType); + return ReadStrings(count, charCount, trimNullTerminators, encoding); } public short ReadInt16() { @@ -757,9 +760,9 @@ public void ReadIntoObject(object obj) case TypeCode.SByte: value = ReadSBytes(arrayLength); break; case TypeCode.Char: { - EncodingType encodingType = Utils.AttributeValueOrDefault(propertyInfo, Encoding); + Encoding encoding = Utils.AttributeValueOrDefault(propertyInfo, Encoding); bool trimNullTerminators = Utils.AttributeValueOrDefault(propertyInfo, false); - value = ReadChars(arrayLength, trimNullTerminators, encodingType); + value = ReadChars(arrayLength, trimNullTerminators, encoding); break; } case TypeCode.Int16: value = ReadInt16s(arrayLength); break; @@ -775,15 +778,15 @@ public void ReadIntoObject(object obj) case TypeCode.String: { Utils.GetStringLength(obj, objType, propertyInfo, true, out bool? nullTerminated, out int stringLength); - EncodingType encodingType = Utils.AttributeValueOrDefault(propertyInfo, Encoding); + Encoding encoding = Utils.AttributeValueOrDefault(propertyInfo, Encoding); if (nullTerminated == true) { - value = ReadStringsNullTerminated(arrayLength, encodingType); + value = ReadStringsNullTerminated(arrayLength, encoding); } else { bool trimNullTerminators = Utils.AttributeValueOrDefault(propertyInfo, false); - value = ReadStrings(arrayLength, stringLength, trimNullTerminators, encodingType); + value = ReadStrings(arrayLength, stringLength, trimNullTerminators, encoding); } break; } @@ -831,8 +834,8 @@ public void ReadIntoObject(object obj) case TypeCode.SByte: value = ReadSByte(); break; case TypeCode.Char: { - EncodingType encodingType = Utils.AttributeValueOrDefault(propertyInfo, Encoding); - value = ReadChar(encodingType); + Encoding encoding = Utils.AttributeValueOrDefault(propertyInfo, Encoding); + value = ReadChar(encoding); break; } case TypeCode.Int16: value = ReadInt16(); break; @@ -848,15 +851,15 @@ public void ReadIntoObject(object obj) case TypeCode.String: { Utils.GetStringLength(obj, objType, propertyInfo, true, out bool? nullTerminated, out int stringLength); - EncodingType encodingType = Utils.AttributeValueOrDefault(propertyInfo, Encoding); + Encoding encoding = Utils.AttributeValueOrDefault(propertyInfo, Encoding); if (nullTerminated == true) { - value = ReadStringNullTerminated(encodingType); + value = ReadStringNullTerminated(encoding); } else { bool trimNullTerminators = Utils.AttributeValueOrDefault(propertyInfo, false); - value = ReadString(stringLength, trimNullTerminators, encodingType); + value = ReadString(stringLength, trimNullTerminators, encoding); } break; } diff --git a/Source/EndianBinaryWriter.cs b/Source/EndianBinaryWriter.cs index ce170d1..7800f28 100644 --- a/Source/EndianBinaryWriter.cs +++ b/Source/EndianBinaryWriter.cs @@ -21,19 +21,6 @@ public Endianness Endianness _endianness = value; } } - private EncodingType _encoding; - public EncodingType Encoding - { - get => _encoding; - set - { - if (value >= EncodingType.MAX) - { - throw new ArgumentOutOfRangeException(nameof(value)); - } - _encoding = value; - } - } private BooleanSize _booleanSize; public BooleanSize BooleanSize { @@ -47,10 +34,11 @@ public BooleanSize BooleanSize _booleanSize = value; } } + public Encoding Encoding { get; set; } private byte[] _buffer; - public EndianBinaryWriter(Stream baseStream, Endianness endianness = Endianness.LittleEndian, EncodingType encoding = EncodingType.ASCII, BooleanSize booleanSize = BooleanSize.U8) + public EndianBinaryWriter(Stream baseStream, Endianness endianness = Endianness.LittleEndian, BooleanSize booleanSize = BooleanSize.U8) { if (baseStream is null) { @@ -62,8 +50,23 @@ public EndianBinaryWriter(Stream baseStream, Endianness endianness = Endianness. } BaseStream = baseStream; Endianness = endianness; - Encoding = encoding; BooleanSize = booleanSize; + Encoding = Encoding.Default; + } + public EndianBinaryWriter(Stream baseStream, Encoding encoding, Endianness endianness = Endianness.LittleEndian, BooleanSize booleanSize = BooleanSize.U8) + { + if (baseStream is null) + { + throw new ArgumentNullException(nameof(baseStream)); + } + if (!baseStream.CanWrite) + { + throw new ArgumentException(nameof(baseStream)); + } + BaseStream = baseStream; + Endianness = endianness; + BooleanSize = booleanSize; + Encoding = encoding; } private void SetBufferSize(int size) @@ -246,16 +249,16 @@ public void Write(char value, long offset) BaseStream.Position = offset; Write(value, Encoding); } - public void Write(char value, EncodingType encodingType) + public void Write(char value, Encoding encoding) { - Encoding encoding = Utils.EncodingFromEnum(encodingType); + Utils.ThrowIfCannotUseEncoding(encoding); _buffer = encoding.GetBytes(new[] { value }); WriteBytesFromBuffer(_buffer.Length); } - public void Write(char value, EncodingType encodingType, long offset) + public void Write(char value, Encoding encoding, long offset) { BaseStream.Position = offset; - Write(value, encodingType); + Write(value, encoding); } public void Write(char[] value) { @@ -266,14 +269,14 @@ public void Write(char[] value, long offset) BaseStream.Position = offset; Write(value, 0, value.Length, Encoding); } - public void Write(char[] value, EncodingType encodingType) + public void Write(char[] value, Encoding encoding) { - Write(value, 0, value.Length, encodingType); + Write(value, 0, value.Length, encoding); } - public void Write(char[] value, EncodingType encodingType, long offset) + public void Write(char[] value, Encoding encoding, long offset) { BaseStream.Position = offset; - Write(value, 0, value.Length, encodingType); + Write(value, 0, value.Length, encoding); } public void Write(char[] value, int startIndex, int count) { @@ -284,20 +287,20 @@ public void Write(char[] value, int startIndex, int count, long offset) BaseStream.Position = offset; Write(value, startIndex, count, Encoding); } - public void Write(char[] value, int startIndex, int count, EncodingType encodingType) + public void Write(char[] value, int startIndex, int count, Encoding encoding) { if (Utils.ValidateArrayIndexAndCount(value, startIndex, count)) { return; } - Encoding encoding = Utils.EncodingFromEnum(encodingType); + Utils.ThrowIfCannotUseEncoding(encoding); _buffer = encoding.GetBytes(value, startIndex, count); WriteBytesFromBuffer(_buffer.Length); } - public void Write(char[] value, int startIndex, int count, EncodingType encodingType, long offset) + public void Write(char[] value, int startIndex, int count, Encoding encoding, long offset) { BaseStream.Position = offset; - Write(value, startIndex, count, encodingType); + Write(value, startIndex, count, encoding); } public void Write(string value, bool nullTerminated) { @@ -308,18 +311,18 @@ public void Write(string value, bool nullTerminated, long offset) BaseStream.Position = offset; Write(value, nullTerminated, Encoding); } - public void Write(string value, bool nullTerminated, EncodingType encodingType) + public void Write(string value, bool nullTerminated, Encoding encoding) { - Write(value.ToCharArray(), encodingType); + Write(value.ToCharArray(), encoding); if (nullTerminated) { - Write('\0', encodingType); + Write('\0', encoding); } } - public void Write(string value, bool nullTerminated, EncodingType encodingType, long offset) + public void Write(string value, bool nullTerminated, Encoding encoding, long offset) { BaseStream.Position = offset; - Write(value, nullTerminated, encodingType); + Write(value, nullTerminated, encoding); } public void Write(string value, int charCount) { @@ -330,15 +333,15 @@ public void Write(string value, int charCount, long offset) BaseStream.Position = offset; Write(value, charCount, Encoding); } - public void Write(string value, int charCount, EncodingType encodingType) + public void Write(string value, int charCount, Encoding encoding) { Utils.TruncateString(value, charCount, out char[] chars); - Write(chars, encodingType); + Write(chars, encoding); } - public void Write(string value, int charCount, EncodingType encodingType, long offset) + public void Write(string value, int charCount, Encoding encoding, long offset) { BaseStream.Position = offset; - Write(value, charCount, encodingType); + Write(value, charCount, encoding); } public void Write(string[] value, int startIndex, int count, bool nullTerminated) { @@ -349,7 +352,7 @@ public void Write(string[] value, int startIndex, int count, bool nullTerminated BaseStream.Position = offset; Write(value, startIndex, count, nullTerminated, Encoding); } - public void Write(string[] value, int startIndex, int count, bool nullTerminated, EncodingType encodingType) + public void Write(string[] value, int startIndex, int count, bool nullTerminated, Encoding encoding) { if (Utils.ValidateArrayIndexAndCount(value, startIndex, count)) { @@ -357,13 +360,13 @@ public void Write(string[] value, int startIndex, int count, bool nullTerminated } for (int i = 0; i < count; i++) { - Write(value[i + startIndex], nullTerminated, encodingType); + Write(value[i + startIndex], nullTerminated, encoding); } } - public void Write(string[] value, int startIndex, int count, bool nullTerminated, EncodingType encodingType, long offset) + public void Write(string[] value, int startIndex, int count, bool nullTerminated, Encoding encoding, long offset) { BaseStream.Position = offset; - Write(value, startIndex, count, nullTerminated, encodingType); + Write(value, startIndex, count, nullTerminated, encoding); } public void Write(string[] value, int startIndex, int count, int charCount) { @@ -374,7 +377,7 @@ public void Write(string[] value, int startIndex, int count, int charCount, long BaseStream.Position = offset; Write(value, startIndex, count, charCount, Encoding); } - public void Write(string[] value, int startIndex, int count, int charCount, EncodingType encodingType) + public void Write(string[] value, int startIndex, int count, int charCount, Encoding encoding) { if (Utils.ValidateArrayIndexAndCount(value, startIndex, count)) { @@ -382,13 +385,13 @@ public void Write(string[] value, int startIndex, int count, int charCount, Enco } for (int i = 0; i < count; i++) { - Write(value[i + startIndex], charCount, encodingType); + Write(value[i + startIndex], charCount, encoding); } } - public void Write(string[] value, int startIndex, int count, int charCount, EncodingType encodingType, long offset) + public void Write(string[] value, int startIndex, int count, int charCount, Encoding encoding, long offset) { BaseStream.Position = offset; - Write(value, startIndex, count, charCount, encodingType); + Write(value, startIndex, count, charCount, encoding); } public void Write(short value) { @@ -799,8 +802,8 @@ public void Write(object obj) case TypeCode.SByte: Write((sbyte[])value, 0, arrayLength); break; case TypeCode.Char: { - EncodingType encodingType = Utils.AttributeValueOrDefault(propertyInfo, Encoding); - Write((char[])value, 0, arrayLength, encodingType); + Encoding encoding = Utils.AttributeValueOrDefault(propertyInfo, Encoding); + Write((char[])value, 0, arrayLength, encoding); break; } case TypeCode.Int16: Write((short[])value, 0, arrayLength); break; @@ -816,14 +819,14 @@ public void Write(object obj) case TypeCode.String: { Utils.GetStringLength(obj, objType, propertyInfo, false, out bool? nullTerminated, out int stringLength); - EncodingType encodingType = Utils.AttributeValueOrDefault(propertyInfo, Encoding); + Encoding encoding = Utils.AttributeValueOrDefault(propertyInfo, Encoding); if (nullTerminated.HasValue) { - Write((string[])value, 0, arrayLength, nullTerminated.Value, encodingType); + Write((string[])value, 0, arrayLength, nullTerminated.Value, encoding); } else { - Write((string[])value, 0, arrayLength, stringLength, encodingType); + Write((string[])value, 0, arrayLength, stringLength, encoding); } break; } @@ -869,8 +872,8 @@ public void Write(object obj) case TypeCode.SByte: Write((sbyte)value); break; case TypeCode.Char: { - EncodingType encodingType = Utils.AttributeValueOrDefault(propertyInfo, Encoding); - Write((char)value, encodingType); + Encoding encoding = Utils.AttributeValueOrDefault(propertyInfo, Encoding); + Write((char)value, encoding); break; } case TypeCode.Int16: Write((short)value); break; @@ -886,14 +889,14 @@ public void Write(object obj) case TypeCode.String: { Utils.GetStringLength(obj, objType, propertyInfo, false, out bool? nullTerminated, out int stringLength); - EncodingType encodingType = Utils.AttributeValueOrDefault(propertyInfo, Encoding); + Encoding encoding = Utils.AttributeValueOrDefault(propertyInfo, Encoding); if (nullTerminated.HasValue) { - Write((string)value, nullTerminated.Value, encodingType); + Write((string)value, nullTerminated.Value, encoding); } else { - Write((string)value, stringLength, encodingType); + Write((string)value, stringLength, encoding); } break; } diff --git a/Source/Enums.cs b/Source/Enums.cs index 48eaa24..5a93b91 100644 --- a/Source/Enums.cs +++ b/Source/Enums.cs @@ -14,16 +14,5 @@ public enum Endianness : byte BigEndian, MAX } - - public enum EncodingType : byte - { - ASCII, - UTF7, - UTF8, - UTF16, - BigEndianUTF16, - UTF32, - MAX - } } diff --git a/Source/Utils.cs b/Source/Utils.cs index e9d9db5..bdaa7aa 100644 --- a/Source/Utils.cs +++ b/Source/Utils.cs @@ -6,19 +6,6 @@ namespace Kermalis.EndianBinaryIO { internal sealed class Utils { - public static Encoding EncodingFromEnum(EncodingType encodingType) - { - switch (encodingType) - { - case EncodingType.ASCII: return Encoding.ASCII; - case EncodingType.UTF7: return Encoding.UTF7; - case EncodingType.UTF8: return Encoding.UTF8; - case EncodingType.UTF16: return Encoding.Unicode; - case EncodingType.BigEndianUTF16: return Encoding.BigEndianUnicode; - case EncodingType.UTF32: return Encoding.UTF32; - default: throw new ArgumentOutOfRangeException(nameof(encodingType)); - } - } public static void TruncateString(string str, int charCount, out char[] toArray) { toArray = new char[charCount]; @@ -60,6 +47,13 @@ public static void ThrowIfCannotReadWriteType(Type type) throw new ArgumentException(nameof(type), $"Cannot read/write \"{type.FullName}\" objects."); } } + public static void ThrowIfCannotUseEncoding(Encoding encoding) + { + if (encoding is null) + { + throw new ArgumentNullException(nameof(encoding), "EndianBinaryIO could not read/write chars because an encoding was null; make sure you pass one in properly."); + } + } // Returns true if count is 0 public static bool ValidateReadArraySize(int count, out T[] array) diff --git a/Testing/BasicTests.cs b/Testing/BasicTests.cs index 4fce93a..24dbeca 100644 --- a/Testing/BasicTests.cs +++ b/Testing/BasicTests.cs @@ -2,6 +2,7 @@ using System; using System.IO; using System.Linq; +using System.Text; using Xunit; namespace Kermalis.EndianBinaryIOTests @@ -30,12 +31,12 @@ private sealed class MyBasicObj // String encoded in ASCII // Reads chars until the stream encounters a '\0' // Writing will append a '\0' at the end of the string - [BinaryEncoding(EncodingType.ASCII)] + [BinaryEncoding("ASCII")] [BinaryStringNullTerminated(true)] public string NullTerminatedASCIIString { get; set; } // String encoded in UTF16-LE that will only read/write 10 chars - [BinaryEncoding(EncodingType.UTF16)] + [BinaryEncoding("UTF-16")] [BinaryStringFixedLength(10)] [BinaryStringTrimNullTerminators(true)] public string UTF16String { get; set; } @@ -146,9 +147,9 @@ public void ReadManually() obj.Bool32 = reader.ReadBoolean(); Assert.False(obj.Bool32); // bool32 works - obj.NullTerminatedASCIIString = reader.ReadStringNullTerminated(EncodingType.ASCII); + obj.NullTerminatedASCIIString = reader.ReadStringNullTerminated(Encoding.ASCII); Assert.Equal("EndianBinaryIO", obj.NullTerminatedASCIIString); // Stops reading at null terminator - obj.UTF16String = reader.ReadString(10, true, EncodingType.UTF16); + obj.UTF16String = reader.ReadString(10, true, Encoding.Unicode); Assert.Equal("Kermalis", obj.UTF16String); // Fixed size (10 chars) UTF16-LE, with the \0s trimmed } } @@ -179,8 +180,8 @@ public void WriteManually() writer.Write(obj.Date); writer.Write(obj.ArrayWith16Elements); writer.Write(obj.Bool32); - writer.Write(obj.NullTerminatedASCIIString, true, EncodingType.ASCII); - writer.Write(obj.UTF16String, 10, EncodingType.UTF16); + writer.Write(obj.NullTerminatedASCIIString, true, Encoding.ASCII); + writer.Write(obj.UTF16String, 10, Encoding.Unicode); } Assert.True(bytes.SequenceEqual(_bytes)); diff --git a/Testing/EncodingTests.cs b/Testing/EncodingTests.cs index ba8bfe2..1f197e5 100644 --- a/Testing/EncodingTests.cs +++ b/Testing/EncodingTests.cs @@ -2,18 +2,30 @@ using System; using System.IO; using System.Linq; +using System.Text; using Xunit; namespace Kermalis.EndianBinaryIOTests { public sealed class EncodingTests { - private sealed class CharObj + private interface ICharObj + { + string Str { get; set; } + } + private sealed class CharObj : ICharObj { public byte Len { get; set; } [BinaryStringVariableLength(nameof(Len))] public string Str { get; set; } } + private sealed class CustomEncodingCharObj : ICharObj + { + public byte Len { get; set; } + [BinaryEncoding("CHPOOSH")] + [BinaryStringVariableLength(nameof(Len))] + public string Str { get; set; } + } #region Constants private const string TestString_ASCII = "Jummy\r\nFunnies"; @@ -43,7 +55,7 @@ private sealed class CharObj 0xF0, 0x9F, 0x98, 0xB3, // "😳" 0x46, 0x75, 0x6E, 0x6E, 0x69, 0x65, 0x73 // "Funnies" }; - private static readonly byte[] _testBytes_UTF16 = new byte[] + private static readonly byte[] _testBytes_UTF16LE = new byte[] { 0x12, // Len 0x4A, 0x00, 0x75, 0x00, 0x6D, 0x00, 0x6D, 0x00, 0x79, 0x00, // "Jummy" @@ -61,7 +73,7 @@ private sealed class CharObj 0xD8, 0x3D, 0xDE, 0x33, // "😳" 0x00, 0x46, 0x00, 0x75, 0x00, 0x6E, 0x00, 0x6E, 0x00, 0x69, 0x00, 0x65, 0x00, 0x73 // "Funnies" }; - private static readonly byte[] _testBytes_UTF32 = new byte[] + private static readonly byte[] _testBytes_UTF32LE = new byte[] { 0x12, // Len 0x4A, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, // "Jummy" @@ -70,49 +82,68 @@ private sealed class CharObj 0x33, 0xF6, 0x01, 0x00, // "😳" 0x46, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00 // "Funnies" }; + private static readonly byte[] _testBytes_UTF32BE = new byte[] + { + 0x12, // Len + 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, 0x79, // "Jummy" + 0x00, 0x01, 0xF6, 0x00, // "😀" + 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0A, // "\r\n" + 0x00, 0x01, 0xF6, 0x33, // "😳" + 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x73 // "Funnies" + }; + #endregion + + #region Chpoosh Encoding + private sealed class ChpooshProvider : EncodingProvider + { + private readonly Encoding _encoding; + public ChpooshProvider(Encoding encoding) + { + _encoding = encoding; + } + + public override Encoding GetEncoding(int codepage) + { + return default; + } + public override Encoding GetEncoding(string name) + { + if (name == "CHPOOSH") + { + return _encoding; + } + return default; + } + } + private Encoding CreateChpoosh() + { + return new UTF32Encoding(true, false, true); + } #endregion - private void Get(EncodingType encodingType, out byte[] input, out string str) + private void Get(string encodingType, out Encoding encoding, out byte[] input, out string str) { switch (encodingType) { - case EncodingType.ASCII: input = _testBytes_ASCII; str = TestString_ASCII; break; - case EncodingType.UTF7: input = _testBytes_UTF7; str = TestString_UTF; break; - case EncodingType.UTF8: input = _testBytes_UTF8; str = TestString_UTF; break; - case EncodingType.UTF16: input = _testBytes_UTF16; str = TestString_UTF; break; - case EncodingType.BigEndianUTF16: input = _testBytes_UTF16BE; str = TestString_UTF; break; - case EncodingType.UTF32: input = _testBytes_UTF32; str = TestString_UTF; break; + case "ASCII": encoding = Encoding.ASCII; input = _testBytes_ASCII; str = TestString_ASCII; break; + case "UTF7": encoding = Encoding.UTF7; input = _testBytes_UTF7; str = TestString_UTF; break; + case "UTF8": encoding = Encoding.UTF8; input = _testBytes_UTF8; str = TestString_UTF; break; + case "UTF16-LE": encoding = Encoding.Unicode; input = _testBytes_UTF16LE; str = TestString_UTF; break; + case "UTF16-BE": encoding = Encoding.BigEndianUnicode; input = _testBytes_UTF16BE; str = TestString_UTF; break; + case "UTF32-LE": encoding = Encoding.UTF32; input = _testBytes_UTF32LE; str = TestString_UTF; break; default: throw new Exception(); } } - - [Theory] - [InlineData(EncodingType.ASCII)] - [InlineData(EncodingType.UTF7)] - [InlineData(EncodingType.UTF8)] - [InlineData(EncodingType.UTF16)] - [InlineData(EncodingType.BigEndianUTF16)] - [InlineData(EncodingType.UTF32)] - public void Read(EncodingType encodingType) - { - Get(encodingType, out byte[] input, out string str); + private void TestRead(Encoding encoding, byte[] input, string str) where T : ICharObj, new() + { using (var stream = new MemoryStream(input)) { - CharObj obj = new EndianBinaryReader(stream, endianness: Endianness.LittleEndian, encoding: encodingType).ReadObject(); + T obj = new EndianBinaryReader(stream, encoding, endianness: Endianness.LittleEndian).ReadObject(); Assert.Equal(str, obj.Str); } } - - [Theory] - [InlineData(EncodingType.ASCII)] - [InlineData(EncodingType.UTF7)] - [InlineData(EncodingType.UTF8)] - [InlineData(EncodingType.UTF16)] - [InlineData(EncodingType.BigEndianUTF16)] - [InlineData(EncodingType.UTF32)] - public void Write(EncodingType encodingType) - { - Get(encodingType, out byte[] input, out string str); + private void TestWrite(Encoding encoding, byte[] input, string str) + { byte[] bytes = new byte[input.Length]; using (var stream = new MemoryStream(bytes)) { @@ -121,9 +152,50 @@ public void Write(EncodingType encodingType) Len = (byte)str.Length, Str = str }; - new EndianBinaryWriter(stream, endianness: Endianness.LittleEndian, encoding: encodingType).Write(obj); + new EndianBinaryWriter(stream, encoding, endianness: Endianness.LittleEndian).Write(obj); } Assert.True(bytes.SequenceEqual(input)); } + + [Theory] + [InlineData("ASCII")] + [InlineData("UTF7")] + [InlineData("UTF8")] + [InlineData("UTF16-LE")] + [InlineData("UTF16-BE")] + [InlineData("UTF32-LE")] + public void ReadDefaults(string encodingType) + { + Get(encodingType, out Encoding encoding, out byte[] input, out string str); + TestRead(encoding, input, str); + } + + [Fact] + public void ReadCustom() + { + Encoding encoding = CreateChpoosh(); + Encoding.RegisterProvider(new ChpooshProvider(encoding)); + TestRead(encoding, _testBytes_UTF32BE, TestString_UTF); + } + + [Theory] + [InlineData("ASCII")] + [InlineData("UTF7")] + [InlineData("UTF8")] + [InlineData("UTF16-LE")] + [InlineData("UTF16-BE")] + [InlineData("UTF32-LE")] + public void WriteDefaults(string encodingType) + { + Get(encodingType, out Encoding encoding, out byte[] input, out string str); + TestWrite(encoding, input, str); + } + + [Fact] + public void WriteCustom() + { + Encoding encoding = CreateChpoosh(); + TestWrite(encoding, _testBytes_UTF32BE, TestString_UTF); + } } } diff --git a/Testing/LengthsTests.cs b/Testing/LengthsTests.cs index 7710be8..65e9152 100644 --- a/Testing/LengthsTests.cs +++ b/Testing/LengthsTests.cs @@ -11,12 +11,12 @@ public sealed class LengthsTests private sealed class MyLengthyObj { [BinaryArrayFixedLength(3)] - [BinaryEncoding(EncodingType.ASCII)] + [BinaryEncoding("ASCII")] [BinaryStringNullTerminated(true)] public string[] NullTerminatedStringArray { get; set; } [BinaryArrayFixedLength(3)] - [BinaryEncoding(EncodingType.ASCII)] + [BinaryEncoding("ASCII")] [BinaryStringFixedLength(5)] public string[] SizedStringArray { get; set; }