mirror of
https://github.com/claunia/plist-cil.git
synced 2025-12-16 19:14:26 +00:00
Merge pull request #36 from quamotion/fixes/array-length
Add support for Span<byte>
This commit is contained in:
@@ -92,7 +92,35 @@ namespace Claunia.PropertyList
|
||||
/// <exception cref="FormatException">When an error occurs during parsing.</exception>
|
||||
public static NSObject Parse(byte[] bytes)
|
||||
{
|
||||
return Parse(bytes.AsSpan());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses an ASCII property list from a byte array.
|
||||
/// </summary>
|
||||
/// <param name="bytes">The ASCII property list data.</param>
|
||||
/// <param name="count">The offset at which to start reading the property list.</param>
|
||||
/// <param name="offset">The length of the property list.</param>
|
||||
/// <returns>The root object of the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
||||
/// <exception cref="FormatException">When an error occurs during parsing.</exception>
|
||||
public static NSObject Parse(byte[] bytes, int offset, int count)
|
||||
{
|
||||
return Parse(bytes.AsSpan(offset, count));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses an ASCII property list from a byte span.
|
||||
/// </summary>
|
||||
/// <param name="bytes">The ASCII property list data.</param>
|
||||
/// <returns>The root object of the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
||||
/// <exception cref="FormatException">When an error occurs during parsing.</exception>
|
||||
public static NSObject Parse(ReadOnlySpan<byte> bytes)
|
||||
{
|
||||
#if NATIVE_SPAN
|
||||
return ParseString(Encoding.UTF8.GetString(bytes));
|
||||
#else
|
||||
return ParseString(Encoding.UTF8.GetString(bytes.ToArray()));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -53,11 +53,6 @@ namespace Claunia.PropertyList
|
||||
/// </summary>
|
||||
int minorVersion;
|
||||
|
||||
/// <summary>
|
||||
/// Property list in bytes
|
||||
/// </summary>
|
||||
byte[] bytes;
|
||||
|
||||
/// <summary>
|
||||
/// Length of an object reference in bytes
|
||||
/// </summary>
|
||||
@@ -84,6 +79,30 @@ namespace Claunia.PropertyList
|
||||
/// <returns>The root object of the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
||||
/// <exception cref="PropertyListFormatException">When the property list's format could not be parsed.</exception>
|
||||
public static NSObject Parse(byte[] data)
|
||||
{
|
||||
return Parse(data.AsSpan());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a binary property list from a byte array.
|
||||
/// </summary>
|
||||
/// <param name="data">The binary property list's data.</param>
|
||||
/// <param name="offset">The length of the property list.</param>
|
||||
/// <param name="count">The offset at which to start reading the property list.</param>
|
||||
/// <returns>The root object of the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
||||
/// <exception cref="PropertyListFormatException">When the property list's format could not be parsed.</exception>
|
||||
public static NSObject Parse(byte[] data, int offset, int length)
|
||||
{
|
||||
return Parse(data.AsSpan(offset, length));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a binary property list from a byte span.
|
||||
/// </summary>
|
||||
/// <param name="data">The binary property list's data.</param>
|
||||
/// <returns>The root object of the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
||||
/// <exception cref="PropertyListFormatException">When the property list's format could not be parsed.</exception>
|
||||
public static NSObject Parse(ReadOnlySpan<byte> data)
|
||||
{
|
||||
BinaryPropertyListParser parser = new BinaryPropertyListParser();
|
||||
return parser.DoParse(data);
|
||||
@@ -93,11 +112,10 @@ namespace Claunia.PropertyList
|
||||
/// Parses a binary property list from a byte array.
|
||||
/// </summary>
|
||||
/// <returns>The root object of the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
||||
/// <param name="data">The binary property list's data.</param>
|
||||
/// <param name="bytes">The binary property list's data.</param>
|
||||
/// <exception cref="PropertyListFormatException">When the property list's format could not be parsed.</exception>
|
||||
NSObject DoParse(byte[] data)
|
||||
private NSObject DoParse(ReadOnlySpan<byte> bytes)
|
||||
{
|
||||
bytes = data;
|
||||
string magic = Encoding.ASCII.GetString(CopyOfRange(bytes, 0, 8));
|
||||
if (!magic.StartsWith("bplist", StringComparison.Ordinal))
|
||||
{
|
||||
@@ -142,7 +160,7 @@ namespace Claunia.PropertyList
|
||||
offsetTable[i] = (int)ParseUnsignedInt(offsetBytes);
|
||||
}
|
||||
|
||||
return ParseObject(topObject);
|
||||
return ParseObject(bytes, topObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -180,7 +198,7 @@ namespace Claunia.PropertyList
|
||||
/// <returns>The parsed object.</returns>
|
||||
/// <param name="obj">The object ID.</param>
|
||||
/// <exception cref="PropertyListFormatException">When the property list's format could not be parsed.</exception>
|
||||
NSObject ParseObject(int obj)
|
||||
NSObject ParseObject(ReadOnlySpan<byte> bytes, int obj)
|
||||
{
|
||||
int offset = offsetTable[obj];
|
||||
byte type = bytes[offset];
|
||||
@@ -258,7 +276,7 @@ namespace Claunia.PropertyList
|
||||
case 0x4:
|
||||
{
|
||||
//Data
|
||||
int[] lengthAndOffset = ReadLengthAndOffset(objInfo, offset);
|
||||
int[] lengthAndOffset = ReadLengthAndOffset(bytes, objInfo, offset);
|
||||
int length = lengthAndOffset[0];
|
||||
int dataoffset = lengthAndOffset[1];
|
||||
|
||||
@@ -267,7 +285,7 @@ namespace Claunia.PropertyList
|
||||
case 0x5:
|
||||
{
|
||||
//ASCII String
|
||||
int[] lengthAndOffset = ReadLengthAndOffset(objInfo, offset);
|
||||
int[] lengthAndOffset = ReadLengthAndOffset(bytes, objInfo, offset);
|
||||
int length = lengthAndOffset[0]; //Each character is 1 byte
|
||||
int stroffset = lengthAndOffset[1];
|
||||
|
||||
@@ -276,7 +294,7 @@ namespace Claunia.PropertyList
|
||||
case 0x6:
|
||||
{
|
||||
//UTF-16-BE String
|
||||
int[] lengthAndOffset = ReadLengthAndOffset(objInfo, offset);
|
||||
int[] lengthAndOffset = ReadLengthAndOffset(bytes, objInfo, offset);
|
||||
int length = lengthAndOffset[0];
|
||||
int stroffset = lengthAndOffset[1];
|
||||
|
||||
@@ -288,7 +306,7 @@ namespace Claunia.PropertyList
|
||||
case 0x7:
|
||||
{
|
||||
//UTF-8 string (v1.0 and later)
|
||||
int[] lengthAndOffset = ReadLengthAndOffset(objInfo, offset);
|
||||
int[] lengthAndOffset = ReadLengthAndOffset(bytes, objInfo, offset);
|
||||
int strOffset = lengthAndOffset[1];
|
||||
int characters = lengthAndOffset[0];
|
||||
//UTF-8 characters can have variable length, so we need to calculate the byte length dynamically
|
||||
@@ -305,7 +323,7 @@ namespace Claunia.PropertyList
|
||||
case 0xA:
|
||||
{
|
||||
//Array
|
||||
int[] lengthAndOffset = ReadLengthAndOffset(objInfo, offset);
|
||||
int[] lengthAndOffset = ReadLengthAndOffset(bytes, objInfo, offset);
|
||||
int length = lengthAndOffset[0];
|
||||
int arrayOffset = lengthAndOffset[1];
|
||||
|
||||
@@ -315,7 +333,7 @@ namespace Claunia.PropertyList
|
||||
int objRef = (int)ParseUnsignedInt(CopyOfRange(bytes,
|
||||
offset + arrayOffset + i * objectRefSize,
|
||||
offset + arrayOffset + (i + 1) * objectRefSize));
|
||||
array.Add(ParseObject(objRef));
|
||||
array.Add(ParseObject(bytes, objRef));
|
||||
}
|
||||
return array;
|
||||
|
||||
@@ -323,7 +341,7 @@ namespace Claunia.PropertyList
|
||||
case 0xB:
|
||||
{
|
||||
//Ordered set (v1.0 and later)
|
||||
int[] lengthAndOffset = ReadLengthAndOffset(objInfo, offset);
|
||||
int[] lengthAndOffset = ReadLengthAndOffset(bytes, objInfo, offset);
|
||||
int length = lengthAndOffset[0];
|
||||
int contentOffset = lengthAndOffset[1];
|
||||
|
||||
@@ -333,14 +351,14 @@ namespace Claunia.PropertyList
|
||||
int objRef = (int)ParseUnsignedInt(CopyOfRange(bytes,
|
||||
offset + contentOffset + i * objectRefSize,
|
||||
offset + contentOffset + (i + 1) * objectRefSize));
|
||||
set.AddObject(ParseObject(objRef));
|
||||
set.AddObject(ParseObject(bytes, objRef));
|
||||
}
|
||||
return set;
|
||||
}
|
||||
case 0xC:
|
||||
{
|
||||
//Set (v1.0 and later)
|
||||
int[] lengthAndOffset = ReadLengthAndOffset(objInfo, offset);
|
||||
int[] lengthAndOffset = ReadLengthAndOffset(bytes, objInfo, offset);
|
||||
int length = lengthAndOffset[0];
|
||||
int contentOffset = lengthAndOffset[1];
|
||||
|
||||
@@ -350,14 +368,14 @@ namespace Claunia.PropertyList
|
||||
int objRef = (int)ParseUnsignedInt(CopyOfRange(bytes,
|
||||
offset + contentOffset + i * objectRefSize,
|
||||
offset + contentOffset + (i + 1) * objectRefSize));
|
||||
set.AddObject(ParseObject(objRef));
|
||||
set.AddObject(ParseObject(bytes, objRef));
|
||||
}
|
||||
return set;
|
||||
}
|
||||
case 0xD:
|
||||
{
|
||||
//Dictionary
|
||||
int[] lengthAndOffset = ReadLengthAndOffset(objInfo, offset);
|
||||
int[] lengthAndOffset = ReadLengthAndOffset(bytes, objInfo, offset);
|
||||
int length = lengthAndOffset[0];
|
||||
int contentOffset = lengthAndOffset[1];
|
||||
|
||||
@@ -371,8 +389,8 @@ namespace Claunia.PropertyList
|
||||
int valRef = (int)ParseUnsignedInt(CopyOfRange(bytes,
|
||||
offset + contentOffset + (length * objectRefSize) + i * objectRefSize,
|
||||
offset + contentOffset + (length * objectRefSize) + (i + 1) * objectRefSize));
|
||||
NSObject key = ParseObject(keyRef);
|
||||
NSObject val = ParseObject(valRef);
|
||||
NSObject key = ParseObject(bytes, keyRef);
|
||||
NSObject val = ParseObject(bytes, valRef);
|
||||
dict.Add(key.ToString(), val);
|
||||
}
|
||||
return dict;
|
||||
@@ -392,7 +410,7 @@ namespace Claunia.PropertyList
|
||||
/// <returns>An array with the length two. First entry is the length, second entry the offset at which the content starts.</returns>
|
||||
/// <param name="objInfo">Object information byte.</param>
|
||||
/// <param name="offset">Offset in the byte array at which the object is located.</param>
|
||||
int[] ReadLengthAndOffset(int objInfo, int offset)
|
||||
int[] ReadLengthAndOffset(ReadOnlySpan<byte> bytes, int objInfo, int offset)
|
||||
{
|
||||
int lengthValue = objInfo;
|
||||
int offsetValue = 1;
|
||||
@@ -434,7 +452,7 @@ namespace Claunia.PropertyList
|
||||
/// <param name="bytes">Array containing the UTF-8 string.</param>
|
||||
/// <param name="offset">Offset in the array where the UTF-8 string resides.</param>
|
||||
/// <param name="numCharacters">How many UTF-8 characters are in the string.</param>
|
||||
int CalculateUtf8StringLength(byte[] bytes, int offset, int numCharacters)
|
||||
int CalculateUtf8StringLength(ReadOnlySpan<byte> bytes, int offset, int numCharacters)
|
||||
{
|
||||
int length = 0;
|
||||
for (int i = 0; i < numCharacters; i++)
|
||||
@@ -603,16 +621,15 @@ namespace Claunia.PropertyList
|
||||
/// <param name="src">The source array.</param>
|
||||
/// <param name="startIndex">The index from which to start copying.</param>
|
||||
/// <param name="endIndex">The index until which to copy.</param>
|
||||
public static byte[] CopyOfRange(byte[] src, int startIndex, int endIndex)
|
||||
public static byte[] CopyOfRange(ReadOnlySpan<byte> src, int startIndex, int endIndex)
|
||||
{
|
||||
int length = endIndex - startIndex;
|
||||
if (length < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("startIndex (" + startIndex + ")" + " > endIndex (" + endIndex + ")");
|
||||
}
|
||||
byte[] dest = new byte[length];
|
||||
Array.Copy(src, startIndex, dest, 0, length);
|
||||
return dest;
|
||||
|
||||
return src.Slice(startIndex, endIndex - startIndex).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace Claunia.PropertyList
|
||||
/// </summary>
|
||||
/// <returns>The very first bytes of data of the property list (minus any whitespace)</returns>
|
||||
/// <param name="bytes">The type of the property list</param>
|
||||
static int DetermineType(byte[] bytes)
|
||||
static int DetermineType(ReadOnlySpan<byte> bytes)
|
||||
{
|
||||
if (bytes.Length == 0)
|
||||
return TYPE_ERROR_BLANK;
|
||||
@@ -88,7 +88,13 @@ namespace Claunia.PropertyList
|
||||
{
|
||||
offset++;
|
||||
}
|
||||
return DetermineType(Encoding.ASCII.GetString(bytes, offset, Math.Min(8, bytes.Length - offset)));
|
||||
|
||||
var header = bytes.Slice(offset, Math.Min(8, bytes.Length - offset));
|
||||
#if NATIVE_SPAN
|
||||
return DetermineType(Encoding.ASCII.GetString(header));
|
||||
#else
|
||||
return DetermineType(Encoding.ASCII.GetString(header.ToArray(), 0, header.Length));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -194,6 +200,38 @@ namespace Claunia.PropertyList
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a property list from a byte array.
|
||||
/// </summary>
|
||||
/// <param name="bytes">The property list data as a byte array.</param>
|
||||
/// <param name="offset">The length of the property list.</param>
|
||||
/// <param name="count">The offset at which to start reading the property list.</param>
|
||||
/// <returns>The root object in the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
||||
public static NSObject Parse(byte[] bytes, int offset, int length)
|
||||
{
|
||||
return Parse(bytes.AsSpan(offset, length));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a property list from a byte span.
|
||||
/// </summary>
|
||||
/// <param name="bytes">The property list data as a byte array.</param>
|
||||
/// <returns>The root object in the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
||||
public static NSObject Parse(ReadOnlySpan<byte> bytes)
|
||||
{
|
||||
switch (DetermineType(bytes))
|
||||
{
|
||||
case TYPE_BINARY:
|
||||
return BinaryPropertyListParser.Parse(bytes);
|
||||
case TYPE_XML:
|
||||
return XmlPropertyListParser.Parse(bytes.ToArray());
|
||||
case TYPE_ASCII:
|
||||
return ASCIIPropertyListParser.Parse(bytes);
|
||||
default:
|
||||
throw new PropertyListFormatException("The given data is not a property list of a supported format.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a property list from an Stream.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netcoreapp1.0;netstandard1.3;netstandard1.4;netstandard1.6;netcoreapp2.0;netstandard2.0</TargetFrameworks>
|
||||
<TargetFrameworks>netcoreapp1.0;netstandard1.3;netstandard1.4;netstandard1.6;netcoreapp2.0;netstandard2.0;netcoreapp2.1</TargetFrameworks>
|
||||
<Version>1.16</Version>
|
||||
<Authors>Natalia Portillo</Authors>
|
||||
<Company>Claunia.com</Company>
|
||||
@@ -12,22 +12,23 @@
|
||||
<RepositoryUrl>http://www.github.com/claunia/plist-cil</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageTags>apple propertylist property list gnustep plist</PackageTags>
|
||||
<PackageReleaseNotes>Added support for .NETStandard1.4.
|
||||
Enhanced binary compatibility with Apple.
|
||||
Correct opening of plists with unescaped UTF8 strings.
|
||||
Added support for parsing hex numbers.
|
||||
Added explicit casts for NSNumber, NSData, NSDate and NSString.
|
||||
Added examples to README.</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>
|
||||
Added support for .NETStandard1.4.
|
||||
Enhanced binary compatibility with Apple.
|
||||
Correct opening of plists with unescaped UTF8 strings.
|
||||
Added support for parsing hex numbers.
|
||||
Added explicit casts for NSNumber, NSData, NSDate and NSString.
|
||||
Added examples to README.
|
||||
</PackageReleaseNotes>
|
||||
<NeutralLanguage>en-US</NeutralLanguage>
|
||||
<SignAssembly>True</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>plist-cil.snk</AssemblyOriginatorKeyFile>
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
|
||||
<DisableImplicitFrameworkReferences>true</DisableImplicitFrameworkReferences>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(OS)' == 'Windows_NT'">
|
||||
<TargetFrameworks>$(TargetFrameworks);net45;net40</TargetFrameworks>
|
||||
<TargetFrameworks>$(TargetFrameworks);net45</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
@@ -42,22 +43,18 @@ Added examples to README.</PackageReleaseNotes>
|
||||
<DefineConstants>$(DefineConstants);NETCORE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|netcoreapp1.0|AnyCPU'">
|
||||
<DocumentationFile>bin\Release\netcoreapp1.0\plist-cil.xml</DocumentationFile>
|
||||
<PropertyGroup Condition="'$(TargetFramework)' == 'netcoreapp2.1'">
|
||||
<DefineConstants>$(DefineConstants);NATIVE_SPAN</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net40|AnyCPU'">
|
||||
<DocumentationFile>bin\Release\net40\plist-cil.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net40' Or '$(TargetFramework)' == 'net45'">
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net45'">
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Core" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="$(TargetFramework.StartsWith('netcoreapp')) Or $(TargetFramework.StartsWith('netstandard'))">
|
||||
|
||||
<ItemGroup Condition="$(TargetFramework.StartsWith('netstandard1.')) Or $(TargetFramework.StartsWith('netcoreapp1.'))">
|
||||
<PackageReference Include="System.Diagnostics.Debug" Version="4.3.0" />
|
||||
<PackageReference Include="System.Linq" Version="4.3.0" />
|
||||
<PackageReference Include="System.Globalization" Version="4.3.0" />
|
||||
@@ -74,6 +71,10 @@ Added examples to README.</PackageReleaseNotes>
|
||||
<PackageReference Include="System.Runtime.Numerics" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' != 'netcoreapp2.1'">
|
||||
<PackageReference Include="System.Memory" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\LICENSE">
|
||||
<Link>LICENSE</Link>
|
||||
|
||||
Reference in New Issue
Block a user