Refactur UIDs

This commit is contained in:
Frederik Carlier
2018-06-19 22:54:05 +02:00
parent 22fbc6afa5
commit 2b4fe6595e
3 changed files with 115 additions and 118 deletions

View File

@@ -9,65 +9,57 @@ namespace plistcil.test
[Fact] [Fact]
public void ByteUidTest() public void ByteUidTest()
{ {
var uid = new UID("byte", (byte)0xAB); var uid = new UID((byte)0xAB);
Assert.Equal(new byte[] { 0xAB }, uid.Bytes); Assert.Equal(new byte[] { 0xAB }, uid.Bytes);
Assert.Equal("byte", uid.Name);
} }
[Fact] [Fact]
public void SByteUidTest() public void SByteUidTest()
{ {
var uid = new UID("sbyte", unchecked((sbyte)0xAB)); var uid = new UID("test", unchecked((sbyte)0x0F));
Assert.Equal(new byte[] { 0xAB }, uid.Bytes); Assert.Equal(new byte[] { 0x0F }, uid.Bytes);
Assert.Equal("sbyte", uid.Name);
} }
[Fact] [Fact]
public void ShortUidTest() public void ShortUidTest()
{ {
var uid = new UID("short", unchecked((short)0xABCD)); var uid = new UID("test", unchecked((short)0x0F0F));
Assert.Equal(new byte[] { 0xAB, 0xCD }, uid.Bytes); Assert.Equal(new byte[] { 0x0F, 0x0F }, uid.Bytes);
Assert.Equal("short", uid.Name);
} }
[Fact] [Fact]
public void UShortUidTest() public void UShortUidTest()
{ {
var uid = new UID("ushort", 0xABCDu); var uid = new UID(0xABCDu);
Assert.Equal(new byte[] { 0xAB, 0xCD }, uid.Bytes); Assert.Equal(new byte[] { 0xAB, 0xCD }, uid.Bytes);
Assert.Equal("ushort", uid.Name);
} }
[Fact] [Fact]
public void UIntUidTest() public void UIntUidTest()
{ {
var uid = new UID("uint", 0xABCDEF00u); var uid = new UID(0xABCDEF00u);
Assert.Equal(new byte[] { 0xAB, 0xCD, 0xEF, 0x00 }, uid.Bytes); Assert.Equal(new byte[] { 0xAB, 0xCD, 0xEF, 0x00 }, uid.Bytes);
Assert.Equal("uint", uid.Name);
} }
[Fact] [Fact]
public void IntUidTest() public void IntUidTest()
{ {
var uid = new UID("int", 0xABCDEF00); var uid = new UID(0xABCDEF00);
Assert.Equal(new byte[] { 0xAB, 0xCD, 0xEF, 0x00 }, uid.Bytes); Assert.Equal(new byte[] { 0xAB, 0xCD, 0xEF, 0x00 }, uid.Bytes);
Assert.Equal("int", uid.Name);
} }
[Fact] [Fact]
public void ULongUidTest() public void ULongUidTest()
{ {
var uid = new UID("ulong", 0xABCDEF0000EFCDABu); var uid = new UID(0xABCDEF0000EFCDABu);
Assert.Equal(new byte[] { 0xAB, 0xCD, 0xEF, 0x00, 0x00, 0xEF, 0xCD, 0xAB }, uid.Bytes); Assert.Equal(new byte[] { 0xAB, 0xCD, 0xEF, 0x00, 0x00, 0xEF, 0xCD, 0xAB }, uid.Bytes);
Assert.Equal("ulong", uid.Name);
} }
[Fact] [Fact]
public void LongUidTest() public void LongUidTest()
{ {
var uid = new UID("int", 0xABCDEF0000EFCDAB); var uid = new UID(0xABCDEF0000EFCDAB);
Assert.Equal(new byte[] { 0xAB, 0xCD, 0xEF, 0x00, 0x00, 0xEF, 0xCD, 0xAB }, uid.Bytes); Assert.Equal(new byte[] { 0xAB, 0xCD, 0xEF, 0x00, 0x00, 0xEF, 0xCD, 0xAB }, uid.Bytes);
Assert.Equal("int", uid.Name);
} }
[Theory] [Theory]
@@ -75,24 +67,16 @@ namespace plistcil.test
[InlineData(new byte[] { 0xAB, 0xCD })] [InlineData(new byte[] { 0xAB, 0xCD })]
[InlineData(new byte[] { 0xAB, 0xCD, 0xEF, 0xFE })] [InlineData(new byte[] { 0xAB, 0xCD, 0xEF, 0xFE })]
[InlineData(new byte[] { 0xAB, 0xCD, 0xEF, 0xFE, 0xFE, 0xEF, 0xCD, 0xAB })] [InlineData(new byte[] { 0xAB, 0xCD, 0xEF, 0xFE, 0xFE, 0xEF, 0xCD, 0xAB })]
[InlineData(new byte[] { 0x00 })]
[InlineData(new byte[] { 0x00, 0x00 })]
[InlineData(new byte[] { 0x00, 0xCD })]
[InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00 })]
[InlineData(new byte[] { 0x00, 0x00, 0x00, 0xCD })]
[InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 })]
[InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCD })]
public void UidFromArrayTest(byte[] array) public void UidFromArrayTest(byte[] array)
{ {
var uid = new UID("array", array); var uid = new UID(array);
Assert.Equal(array, uid.Bytes); Assert.Equal(array, uid.Bytes);
Assert.Equal("array", uid.Name);
} }
[Fact] [Fact]
public void BinaryRoundTripTest() public void BinaryRoundTripTest()
{ {
var original = new UID("0", 0xabcd); var original = new UID(0xabcd);
using (MemoryStream stream = new MemoryStream()) using (MemoryStream stream = new MemoryStream())
{ {
@@ -106,14 +90,14 @@ namespace plistcil.test
[Fact] [Fact]
public void XmlRoundTripTest() public void XmlRoundTripTest()
{ {
var original = new UID("0", 0xabcd); var original = new UID(0xabcd);
var plist = original.ToXmlPropertyList(); var plist = original.ToXmlPropertyList();
// UIDs don't exist in XML property lists, but they are represented as strings // UIDs don't exist in XML property lists, but they are represented as strings
// for compability purposes // for compability purposes
var roundtrip = XmlPropertyListParser.ParseString(plist) as NSString; var roundtrip = XmlPropertyListParser.ParseString(plist) as NSString;
Assert.Equal("0000abcd", roundtrip.ToObject()); Assert.Equal("abcd", roundtrip.ToObject());
} }
} }
} }

View File

@@ -316,7 +316,7 @@ namespace Claunia.PropertyList
{ {
//UID (v1.0 and later) //UID (v1.0 and later)
int length = objInfo + 1; int length = objInfo + 1;
return new UID(obj.ToString(), bytes.Slice(offset + 1, length)); return new UID(bytes.Slice(offset + 1, length));
} }
case 0xA: case 0xA:
{ {

View File

@@ -36,20 +36,28 @@ namespace Claunia.PropertyList
public class UID : NSObject public class UID : NSObject
{ {
readonly ulong value; readonly ulong value;
readonly byte length;
readonly string name;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Claunia.PropertyList.UID"/> class. /// Initializes a new instance of the <see cref="Claunia.PropertyList.UID"/> class.
/// </summary> /// </summary>
/// <param name="name">Name.</param> /// <param name="name">Name.</param>
/// <param name="bytes">Bytes.</param> /// <param name="bytes">Bytes.</param>
[Obsolete("UIDs have not meaningful names")]
public UID(String name, ReadOnlySpan<byte> bytes) public UID(String name, ReadOnlySpan<byte> bytes)
{ {
if (bytes.Length != 1 && bytes.Length != 2 && bytes.Length != 4 && bytes.Length != 8) if (bytes.Length != 1 && bytes.Length != 2 && bytes.Length != 4 && bytes.Length != 8)
throw new ArgumentException("Type argument is not valid."); throw new ArgumentException("Type argument is not valid.");
this.name = name; this.value = (ulong)BinaryPropertyListParser.ParseLong(bytes);
this.length = (byte)bytes.Length; }
/// <summary>
/// Initializes a new instance of the <see cref="Claunia.PropertyList.UID"/> class.
/// </summary>
/// <param name="bytes">Bytes.</param>
public UID(ReadOnlySpan<byte> bytes)
{
if (bytes.Length != 1 && bytes.Length != 2 && bytes.Length != 4 && bytes.Length != 8)
throw new ArgumentException("Type argument is not valid.");
this.value = (ulong)BinaryPropertyListParser.ParseLong(bytes); this.value = (ulong)BinaryPropertyListParser.ParseLong(bytes);
} }
@@ -58,11 +66,19 @@ namespace Claunia.PropertyList
/// </summary> /// </summary>
/// <param name="name">Name.</param> /// <param name="name">Name.</param>
/// <param name="number">Unsigned 8-bit number.</param> /// <param name="number">Unsigned 8-bit number.</param>
[Obsolete("UIDs have no meaningful names")]
public UID(String name, byte number) public UID(String name, byte number)
{ {
this.name = name;
this.value = number; this.value = number;
this.length = 1; }
/// <summary>
/// Initializes a new instance of the <see cref="Claunia.PropertyList.UID"/> class using an unsigned 8-bit number.
/// </summary>
/// <param name="number">Unsigned 8-bit number.</param>
public UID(byte number)
{
this.value = number;
} }
/// <summary> /// <summary>
@@ -70,11 +86,10 @@ namespace Claunia.PropertyList
/// </summary> /// </summary>
/// <param name="name">Name.</param> /// <param name="name">Name.</param>
/// <param name="number">Unsigned 8-bit number.</param> /// <param name="number">Unsigned 8-bit number.</param>
[Obsolete("UIDs must be unsigned values")]
public UID(String name, sbyte number) public UID(String name, sbyte number)
{ {
this.name = name;
this.value = (ulong)number; this.value = (ulong)number;
this.length = 1;
} }
/// <summary> /// <summary>
@@ -82,16 +97,20 @@ namespace Claunia.PropertyList
/// </summary> /// </summary>
/// <param name="name">Name.</param> /// <param name="name">Name.</param>
/// <param name="number">Unsigned 16-bit number.</param> /// <param name="number">Unsigned 16-bit number.</param>
[Obsolete("UIDs have no meaningful names")]
public UID(String name, ushort number) public UID(String name, ushort number)
{ {
this.name = name;
this.value = number; this.value = number;
if (number <= byte.MaxValue) }
this.length = 1;
else /// <summary>
{ /// Initializes a new instance of the <see cref="Claunia.PropertyList.UID"/> class using an unsigned 16-bit number.
this.length = 2; /// </summary>
} /// <param name="name">Name.</param>
/// <param name="number">Unsigned 16-bit number.</param>
public UID(ushort number)
{
this.value = number;
} }
/// <summary> /// <summary>
@@ -99,16 +118,10 @@ namespace Claunia.PropertyList
/// </summary> /// </summary>
/// <param name="name">Name.</param> /// <param name="name">Name.</param>
/// <param name="number">Signed 16-bit number.</param> /// <param name="number">Signed 16-bit number.</param>
[Obsolete("UIDs must be unsigned values")]
public UID(String name, short number) public UID(String name, short number)
{ {
this.name = name;
this.value = (ulong)number; this.value = (ulong)number;
if (number >= sbyte.MinValue && number <= sbyte.MaxValue)
this.length = 1;
else
{
this.length = 2;
}
} }
/// <summary> /// <summary>
@@ -116,20 +129,19 @@ namespace Claunia.PropertyList
/// </summary> /// </summary>
/// <param name="name">Name.</param> /// <param name="name">Name.</param>
/// <param name="number">Unsigned 32-bit number.</param> /// <param name="number">Unsigned 32-bit number.</param>
[Obsolete("UIDs have no meaningful names")]
public UID(String name, uint number) public UID(String name, uint number)
{ {
this.name = name;
this.value = number; this.value = number;
if (number <= byte.MaxValue) }
this.length = 1;
else if (number <= ushort.MaxValue) /// <summary>
{ /// Initializes a new instance of the <see cref="Claunia.PropertyList.UID"/> class using an unsigned 32-bit number.
this.length = 2; /// </summary>
} /// <param name="number">Unsigned 32-bit number.</param>
else public UID(uint number)
{ {
this.length = 4; this.value = number;
}
} }
/// <summary> /// <summary>
@@ -137,20 +149,10 @@ namespace Claunia.PropertyList
/// </summary> /// </summary>
/// <param name="name">Name.</param> /// <param name="name">Name.</param>
/// <param name="number">Signed 32-bit number.</param> /// <param name="number">Signed 32-bit number.</param>
[Obsolete("UIDs must be unsigned values")]
public UID(String name, int number) public UID(String name, int number)
{ {
this.name = name;
this.value = (ulong)number; this.value = (ulong)number;
if (number >= sbyte.MinValue && number <= sbyte.MaxValue)
this.length = 1;
if (number >= short.MinValue && number <= short.MaxValue)
{
this.length = 2;
}
else
{
this.length = 4;
}
} }
/// <summary> /// <summary>
@@ -158,24 +160,19 @@ namespace Claunia.PropertyList
/// </summary> /// </summary>
/// <param name="name">Name.</param> /// <param name="name">Name.</param>
/// <param name="number">Unsigned 64-bit number.</param> /// <param name="number">Unsigned 64-bit number.</param>
[Obsolete("UIDs have no meaningful names")]
public UID(String name, ulong number) public UID(String name, ulong number)
{ {
this.name = name;
this.value = number; this.value = number;
if (number <= byte.MaxValue) }
this.length = 1;
else if (number <= ushort.MaxValue) /// <summary>
{ /// Initializes a new instance of the <see cref="Claunia.PropertyList.UID"/> class using an unsigned 64-bit number.
this.length = 2; /// </summary>
} /// <param name="number">Unsigned 64-bit number.</param>
else if (number <= uint.MaxValue) public UID(ulong number)
{ {
this.length = 4; this.value = number;
}
else
{
this.length = 8;
}
} }
/// <summary> /// <summary>
@@ -183,24 +180,10 @@ namespace Claunia.PropertyList
/// </summary> /// </summary>
/// <param name="name">Name.</param> /// <param name="name">Name.</param>
/// <param name="number">Signed 64-bit number.</param> /// <param name="number">Signed 64-bit number.</param>
[Obsolete("UIDs must be unsigned values")]
public UID(String name, long number) public UID(String name, long number)
{ {
this.name = name;
this.value = (ulong)number; this.value = (ulong)number;
if (number >= sbyte.MinValue && number <= sbyte.MaxValue)
this.length = 1;
if (number >= short.MinValue && number <= short.MaxValue)
{
this.length = 2;
}
if (number >= int.MinValue && number <= int.MaxValue)
{
this.length = 4;
}
else
{
this.length = 8;
}
} }
/// <summary> /// <summary>
@@ -211,15 +194,47 @@ namespace Claunia.PropertyList
{ {
get get
{ {
byte[] bytes = new byte[this.length]; byte[] bytes = new byte[this.ByteCount];
this.GetBytes(bytes); this.GetBytes(bytes);
return bytes; return bytes;
} }
} }
/// <summary>
/// Gets the number of bytes required to represent this <see cref="UID"/>.
/// </summary>
public int ByteCount
{
get
{
if (this.value <= byte.MaxValue)
{
return 1;
}
else if (this.value <= ushort.MaxValue)
{
return 2;
}
else if (this.value <= uint.MaxValue)
{
return 4;
}
else
{
return 8;
}
}
}
/// <summary>
/// Writes the bytes required to represent this <see cref="UID"/> to a byte span.
/// </summary>
/// <param name="bytes">
/// The byte span to which to write the byte representation of this UID.
/// </param>
public void GetBytes(Span<byte> bytes) public void GetBytes(Span<byte> bytes)
{ {
switch (this.length) switch (this.ByteCount)
{ {
case 1: case 1:
bytes[0] = (byte)this.value; bytes[0] = (byte)this.value;
@@ -246,11 +261,12 @@ namespace Claunia.PropertyList
/// Gets the name. /// Gets the name.
/// </summary> /// </summary>
/// <value>The name.</value> /// <value>The name.</value>
[Obsolete("UIDs have no meaningful names")]
public string Name public string Name
{ {
get get
{ {
return name; return this.value.ToString();
} }
} }
@@ -264,7 +280,7 @@ namespace Claunia.PropertyList
{ {
Indent(xml, level); Indent(xml, level);
xml.Append("<string>"); xml.Append("<string>");
Span<byte> bytes = stackalloc byte[this.length]; Span<byte> bytes = stackalloc byte[this.ByteCount];
this.GetBytes(bytes); this.GetBytes(bytes);
foreach (byte b in bytes) foreach (byte b in bytes)
xml.Append(String.Format("{0:x2}", b)); xml.Append(String.Format("{0:x2}", b));
@@ -273,8 +289,8 @@ namespace Claunia.PropertyList
internal override void ToBinary(BinaryPropertyListWriter outPlist) internal override void ToBinary(BinaryPropertyListWriter outPlist)
{ {
outPlist.Write(0x80 + this.length - 1); outPlist.Write(0x80 + this.ByteCount - 1);
Span<byte> bytes = stackalloc byte[this.length]; Span<byte> bytes = stackalloc byte[this.ByteCount];
this.GetBytes(bytes); this.GetBytes(bytes);
outPlist.Write(bytes); outPlist.Write(bytes);
} }
@@ -283,7 +299,7 @@ namespace Claunia.PropertyList
{ {
Indent(ascii, level); Indent(ascii, level);
ascii.Append("\""); ascii.Append("\"");
Span<byte> bytes = stackalloc byte[this.length]; Span<byte> bytes = stackalloc byte[this.ByteCount];
this.GetBytes(bytes); this.GetBytes(bytes);
foreach (byte b in bytes) foreach (byte b in bytes)
ascii.Append(String.Format("{0:x2}", b)); ascii.Append(String.Format("{0:x2}", b));
@@ -314,17 +330,14 @@ namespace Claunia.PropertyList
if (uid == null) if (uid == null)
return false; return false;
return uid.name == name return uid.value == value;
&& uid.length == length
&& uid.value == value;
} }
#if HAS_HASHCODE /// <inheritdoc/>
public override int GetHashCode() public override int GetHashCode()
{ {
return HashCode.Combine(this.name, this.length, this.value); return this.value.GetHashCode();
} }
#endif
} }
} }