diff --git a/plist-cil/ASCIIPropertyListParser.cs b/plist-cil/ASCIIPropertyListParser.cs
index 3adada0..bd5031d 100644
--- a/plist-cil/ASCIIPropertyListParser.cs
+++ b/plist-cil/ASCIIPropertyListParser.cs
@@ -92,7 +92,35 @@ namespace Claunia.PropertyList
/// When an error occurs during parsing.
public static NSObject Parse(byte[] bytes)
{
+ return Parse(bytes.AsSpan());
+ }
+
+ ///
+ /// Parses an ASCII property list from a byte array.
+ ///
+ /// The ASCII property list data.
+ /// The offset at which to start reading the property list.
+ /// The length of the property list.
+ /// The root object of the property list. This is usually a NSDictionary but can also be a NSArray.
+ /// When an error occurs during parsing.
+ public static NSObject Parse(byte[] bytes, int offset, int count)
+ {
+ return Parse(bytes.AsSpan(offset, count));
+ }
+
+ ///
+ /// Parses an ASCII property list from a byte span.
+ ///
+ /// The ASCII property list data.
+ /// The root object of the property list. This is usually a NSDictionary but can also be a NSArray.
+ /// When an error occurs during parsing.
+ public static NSObject Parse(ReadOnlySpan bytes)
+ {
+#if NATIVE_SPAN
return ParseString(Encoding.UTF8.GetString(bytes));
+#else
+ return ParseString(Encoding.UTF8.GetString(bytes.ToArray()));
+#endif
}
///
diff --git a/plist-cil/BinaryPropertyListParser.cs b/plist-cil/BinaryPropertyListParser.cs
index 1c5539e..d0dc7d2 100644
--- a/plist-cil/BinaryPropertyListParser.cs
+++ b/plist-cil/BinaryPropertyListParser.cs
@@ -53,11 +53,6 @@ namespace Claunia.PropertyList
///
int minorVersion;
- ///
- /// Property list in bytes
- ///
- byte[] bytes;
-
///
/// Length of an object reference in bytes
///
@@ -84,6 +79,30 @@ namespace Claunia.PropertyList
/// The root object of the property list. This is usually a NSDictionary but can also be a NSArray.
/// When the property list's format could not be parsed.
public static NSObject Parse(byte[] data)
+ {
+ return Parse(data.AsSpan());
+ }
+
+ ///
+ /// Parses a binary property list from a byte array.
+ ///
+ /// The binary property list's data.
+ /// The length of the property list.
+ /// The offset at which to start reading the property list.
+ /// The root object of the property list. This is usually a NSDictionary but can also be a NSArray.
+ /// When the property list's format could not be parsed.
+ public static NSObject Parse(byte[] data, int offset, int length)
+ {
+ return Parse(data.AsSpan(offset, length));
+ }
+
+ ///
+ /// Parses a binary property list from a byte span.
+ ///
+ /// The binary property list's data.
+ /// The root object of the property list. This is usually a NSDictionary but can also be a NSArray.
+ /// When the property list's format could not be parsed.
+ public static NSObject Parse(ReadOnlySpan 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.
///
/// The root object of the property list. This is usually a NSDictionary but can also be a NSArray.
- /// The binary property list's data.
+ /// The binary property list's data.
/// When the property list's format could not be parsed.
- NSObject DoParse(byte[] data)
+ private NSObject DoParse(ReadOnlySpan 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);
}
///
@@ -180,7 +198,7 @@ namespace Claunia.PropertyList
/// The parsed object.
/// The object ID.
/// When the property list's format could not be parsed.
- NSObject ParseObject(int obj)
+ NSObject ParseObject(ReadOnlySpan 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
/// An array with the length two. First entry is the length, second entry the offset at which the content starts.
/// Object information byte.
/// Offset in the byte array at which the object is located.
- int[] ReadLengthAndOffset(int objInfo, int offset)
+ int[] ReadLengthAndOffset(ReadOnlySpan bytes, int objInfo, int offset)
{
int lengthValue = objInfo;
int offsetValue = 1;
@@ -434,7 +452,7 @@ namespace Claunia.PropertyList
/// Array containing the UTF-8 string.
/// Offset in the array where the UTF-8 string resides.
/// How many UTF-8 characters are in the string.
- int CalculateUtf8StringLength(byte[] bytes, int offset, int numCharacters)
+ int CalculateUtf8StringLength(ReadOnlySpan bytes, int offset, int numCharacters)
{
int length = 0;
for (int i = 0; i < numCharacters; i++)
@@ -603,16 +621,15 @@ namespace Claunia.PropertyList
/// The source array.
/// The index from which to start copying.
/// The index until which to copy.
- public static byte[] CopyOfRange(byte[] src, int startIndex, int endIndex)
+ public static byte[] CopyOfRange(ReadOnlySpan 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();
}
}
}
diff --git a/plist-cil/PropertyListParser.cs b/plist-cil/PropertyListParser.cs
index c24b02b..ac7f572 100644
--- a/plist-cil/PropertyListParser.cs
+++ b/plist-cil/PropertyListParser.cs
@@ -72,7 +72,7 @@ namespace Claunia.PropertyList
///
/// The very first bytes of data of the property list (minus any whitespace)
/// The type of the property list
- static int DetermineType(byte[] bytes)
+ static int DetermineType(ReadOnlySpan 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
}
///
@@ -194,6 +200,38 @@ namespace Claunia.PropertyList
}
}
+ ///
+ /// Parses a property list from a byte array.
+ ///
+ /// The property list data as a byte array.
+ /// The length of the property list.
+ /// The offset at which to start reading the property list.
+ /// The root object in the property list. This is usually a NSDictionary but can also be a NSArray.
+ public static NSObject Parse(byte[] bytes, int offset, int length)
+ {
+ return Parse(bytes.AsSpan(offset, length));
+ }
+
+ ///
+ /// Parses a property list from a byte span.
+ ///
+ /// The property list data as a byte array.
+ /// The root object in the property list. This is usually a NSDictionary but can also be a NSArray.
+ public static NSObject Parse(ReadOnlySpan 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.");
+ }
+ }
+
///
/// Parses a property list from an Stream.
///
diff --git a/plist-cil/plist-cil.csproj b/plist-cil/plist-cil.csproj
index ad54278..da888c4 100644
--- a/plist-cil/plist-cil.csproj
+++ b/plist-cil/plist-cil.csproj
@@ -1,7 +1,7 @@
- netcoreapp1.0;netstandard1.3;netstandard1.4;netstandard1.6;netcoreapp2.0;netstandard2.0
+ netcoreapp1.0;netstandard1.3;netstandard1.4;netstandard1.6;netcoreapp2.0;netstandard2.0;netcoreapp2.11.16Natalia PortilloClaunia.com
@@ -12,22 +12,23 @@
http://www.github.com/claunia/plist-cilgitapple propertylist property list gnustep plist
- 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.
+
+ 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.
+ en-USTrueplist-cil.snkTrueTrue
- true
- $(TargetFrameworks);net45;net40
+ $(TargetFrameworks);net45
@@ -42,22 +43,18 @@ Added examples to README.
$(DefineConstants);NETCORE
-
- bin\Release\netcoreapp1.0\plist-cil.xml
+
+ $(DefineConstants);NATIVE_SPAN
-
- bin\Release\net40\plist-cil.xml
-
-
-
+
-
-
+
+
@@ -74,6 +71,10 @@ Added examples to README.
+
+
+
+
LICENSE