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.1 1.16 Natalia Portillo Claunia.com @@ -12,22 +12,23 @@ http://www.github.com/claunia/plist-cil git apple 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-US True plist-cil.snk True True - 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