Performance improvements

- Set the capacity of the dictionary of known
- Don't allocate a string to check whether a file is a valid binary property list
This commit is contained in:
Frederik Carlier
2018-06-19 14:40:51 +02:00
parent 5c68ff1b62
commit 19c1e47db2
3 changed files with 46 additions and 27 deletions

View File

@@ -119,20 +119,20 @@ namespace Claunia.PropertyList
/// <exception cref="PropertyListFormatException">When the property list's format could not be parsed.</exception> /// <exception cref="PropertyListFormatException">When the property list's format could not be parsed.</exception>
private NSObject DoParse(ReadOnlySpan<byte> bytes) private NSObject DoParse(ReadOnlySpan<byte> bytes)
{ {
string magic = Encoding.ASCII.GetString( if(bytes.Length < 8
#if NATIVE_SPAN || bytes[0] != 'b'
bytes.Slice(0, 8)); || bytes[1] != 'p'
#else || bytes[2] != 'l'
bytes.Slice(0, 8).ToArray()); || bytes[3] != 'i'
#endif || bytes[4] != 's'
|| bytes[5] != 't')
if (!magic.StartsWith("bplist", StringComparison.Ordinal))
{ {
var magic = Encoding.ASCII.GetString(bytes.Slice(0, 8).ToArray());
throw new PropertyListFormatException("The given data is no binary property list. Wrong magic bytes: " + magic); throw new PropertyListFormatException("The given data is no binary property list. Wrong magic bytes: " + magic);
} }
majorVersion = magic[6] - 0x30; //ASCII number majorVersion = bytes[6] - 0x30; //ASCII number
minorVersion = magic[7] - 0x30; //ASCII number minorVersion = bytes[7] - 0x30; //ASCII number
// 0.0 - OS X Tiger and earlier // 0.0 - OS X Tiger and earlier
// 0.1 - Leopard // 0.1 - Leopard
@@ -366,7 +366,7 @@ namespace Claunia.PropertyList
ReadLengthAndOffset(bytes, objInfo, offset, out int length, out int contentOffset); ReadLengthAndOffset(bytes, objInfo, offset, out int length, out int contentOffset);
//System.out.println("Parsing dictionary #"+obj); //System.out.println("Parsing dictionary #"+obj);
NSDictionary dict = new NSDictionary(); NSDictionary dict = new NSDictionary(length);
for (int i = 0; i < length; i++) for (int i = 0; i < length; i++)
{ {
int keyRef = (int)ParseUnsignedInt(bytes.Slice(offset + contentOffset + i * objectRefSize, objectRefSize)); int keyRef = (int)ParseUnsignedInt(bytes.Slice(offset + contentOffset + i * objectRefSize, objectRefSize));

View File

@@ -49,13 +49,24 @@ namespace Claunia.PropertyList
// object remains constant accross calls to AssignIDs and ToBinary // object remains constant accross calls to AssignIDs and ToBinary
readonly Dictionary<string, NSString> keys; readonly Dictionary<string, NSString> keys;
/// <summary>
/// Creates a new empty NSDictionary with a specific capacity.
/// </summary>
/// <param name="capacity">
/// The capacity of the dictionary.
/// </param>
public NSDictionary(int capacity)
{
dict = new Dictionary<string, NSObject>(capacity);
keys = new Dictionary<string, NSString>(capacity);
}
/// <summary> /// <summary>
/// Creates a new empty NSDictionary. /// Creates a new empty NSDictionary.
/// </summary> /// </summary>
public NSDictionary() public NSDictionary()
: this(0)
{ {
dict = new Dictionary<string, NSObject>();
keys = new Dictionary<string, NSString>();
} }
/// <summary> /// <summary>

View File

@@ -49,22 +49,34 @@ namespace Claunia.PropertyList
/// </summary> /// </summary>
/// <returns>The type of the property list</returns> /// <returns>The type of the property list</returns>
/// <param name="dataBeginning">The very first bytes of data of the property list (minus any whitespace) as a string</param> /// <param name="dataBeginning">The very first bytes of data of the property list (minus any whitespace) as a string</param>
static int DetermineType(string dataBeginning) static int DetermineTypeExact(ReadOnlySpan<byte> dataBeginning)
{ {
dataBeginning = dataBeginning.Trim();
if (dataBeginning.Length == 0) if (dataBeginning.Length == 0)
{ {
return TYPE_ERROR_BLANK; return TYPE_ERROR_BLANK;
} }
if (dataBeginning.StartsWith("bplist")) else if (dataBeginning[0] == '(' || dataBeginning[0] == '{' || dataBeginning[0] == '/')
{
return TYPE_BINARY;
}
if (dataBeginning.StartsWith("(") || dataBeginning.StartsWith("{") || dataBeginning.StartsWith("/"))
{ {
return TYPE_ASCII; return TYPE_ASCII;
} }
return dataBeginning.StartsWith("<") ? TYPE_XML : TYPE_ERROR_UNKNOWN; else if (dataBeginning[0] == '<')
{
return TYPE_XML;
}
else if (dataBeginning.Length >= 6
&& dataBeginning[0] == 'b'
&& dataBeginning[1] == 'p'
&& dataBeginning[2] == 'l'
&& dataBeginning[3] == 'i'
&& dataBeginning[4] == 's'
&& dataBeginning[5] == 't')
{
return TYPE_BINARY;
}
else
{
return TYPE_ERROR_UNKNOWN;
}
} }
/// <summary> /// <summary>
@@ -90,11 +102,7 @@ namespace Claunia.PropertyList
} }
var header = bytes.Slice(offset, Math.Min(8, bytes.Length - offset)); var header = bytes.Slice(offset, Math.Min(8, bytes.Length - offset));
#if NATIVE_SPAN return DetermineTypeExact(header);
return DetermineType(Encoding.ASCII.GetString(header));
#else
return DetermineType(Encoding.ASCII.GetString(header.ToArray(), 0, header.Length));
#endif
} }
/// <summary> /// <summary>
@@ -138,7 +146,7 @@ namespace Claunia.PropertyList
magicBytes[0] = (byte)b; magicBytes[0] = (byte)b;
int read = fs.Read(magicBytes, 1, 7); int read = fs.Read(magicBytes, 1, 7);
int type = DetermineType(Encoding.ASCII.GetString(magicBytes, 0, read)); int type = DetermineTypeExact(magicBytes.AsSpan(0, read));
fs.Seek(mark, SeekOrigin.Begin); fs.Seek(mark, SeekOrigin.Begin);
return type; return type;
} }