From 6c9526015786d30be8933b09754d5060e16e1359 Mon Sep 17 00:00:00 2001 From: Frederik Carlier Date: Wed, 23 Nov 2016 22:20:14 +0100 Subject: [PATCH 1/3] Maintain binary compatibility with the Apple format: disable optimizations and add the same UID, NSArray and NSString values multiple times to the property list file. --- plist-cil/BinaryPropertyListWriter.cs | 33 ++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/plist-cil/BinaryPropertyListWriter.cs b/plist-cil/BinaryPropertyListWriter.cs index e2f430e..4bfa4b6 100644 --- a/plist-cil/BinaryPropertyListWriter.cs +++ b/plist-cil/BinaryPropertyListWriter.cs @@ -61,6 +61,21 @@ namespace Claunia.PropertyList /// public const int VERSION_20 = 20; + /// + /// Gets or sets a value indicating whether two equivalent objects should be serialized once in the binary property list file, or whether + /// the value should be stored multiple times in the binary property list file. The default is . + /// + /// + /// In most scenarios, you want this to be , as it reduces the size of the binary proeprty list file. However, + /// by default, the Apple tools do not seem to implement this optimization, so set this value to if you + /// want to maintain binary compatibility with the Apple tools. + /// + public bool ReuseObjectIds + { + get; + set; + } + /// /// Finds out the minimum binary property list format version that /// can be used to save the given NSObject tree. @@ -269,7 +284,14 @@ namespace Claunia.PropertyList internal void AssignID(NSObject obj) { - if (obj is UID || obj is NSArray) + // If binary compatibility with the Apple format is required, + // UID, NSArray and NSString objects are assigned a new ID, + // even if they already exist in the file. + if (!this.ReuseObjectIds && (obj is UID || obj is NSArray)) + { + idMap.Add(obj); + } + else if(!this.ReuseObjectIds && obj is NSString && !IsSerializationPrimitive((NSString)obj)) { idMap.Add(obj); } @@ -279,15 +301,20 @@ namespace Claunia.PropertyList } } + internal bool IsSerializationPrimitive(NSString obj) + { + return obj != null && obj.Content.StartsWith("$") || obj.Content.StartsWith("NS"); + } + internal int GetID(NSObject obj) { - if (obj is UID) + if (!this.ReuseObjectIds && obj is UID) { var uid = obj as UID; var first = idMap.OfType().First(v => NSObject.ArrayEquals(v.Bytes, uid.Bytes)); return idMap.IndexOf(first); } - else if (obj is NSArray) + else if (!this.ReuseObjectIds && obj is NSArray) { int index = 0; From 8dc5f8ba24accc65a48dcb87ce9ec88bdfe689dd Mon Sep 17 00:00:00 2001 From: Frederik Carlier Date: Thu, 24 Nov 2016 00:24:12 +0100 Subject: [PATCH 2/3] Make sure that the NSString which represents keys in a NSDictionary is always the same value, by caching the string to NSString conversion in a dictionary --- plist-cil/BinaryPropertyListWriter.cs | 11 ++++++++--- plist-cil/NSDictionary.cs | 21 ++++++++++++++++++--- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/plist-cil/BinaryPropertyListWriter.cs b/plist-cil/BinaryPropertyListWriter.cs index 4bfa4b6..ca7adcb 100644 --- a/plist-cil/BinaryPropertyListWriter.cs +++ b/plist-cil/BinaryPropertyListWriter.cs @@ -291,7 +291,7 @@ namespace Claunia.PropertyList { idMap.Add(obj); } - else if(!this.ReuseObjectIds && obj is NSString && !IsSerializationPrimitive((NSString)obj)) + else if (!this.ReuseObjectIds && obj is NSString && !IsSerializationPrimitive((NSString)obj)) { idMap.Add(obj); } @@ -314,9 +314,9 @@ namespace Claunia.PropertyList var first = idMap.OfType().First(v => NSObject.ArrayEquals(v.Bytes, uid.Bytes)); return idMap.IndexOf(first); } - else if (!this.ReuseObjectIds && obj is NSArray) + else if (!this.ReuseObjectIds && (obj is NSArray || (obj is NSString && !IsSerializationPrimitive((NSString)obj)))) { - int index = 0; + int index = -1; for (int i = 0; i < idMap.Count; i++) { @@ -327,6 +327,11 @@ namespace Claunia.PropertyList } } + if (index == -1) + { + throw new InvalidOperationException(); + } + return index; } else diff --git a/plist-cil/NSDictionary.cs b/plist-cil/NSDictionary.cs index 7804c85..2868268 100644 --- a/plist-cil/NSDictionary.cs +++ b/plist-cil/NSDictionary.cs @@ -45,12 +45,17 @@ namespace Claunia.PropertyList { readonly Dictionary dict; + // Maps the keys in this dictionary to their NSString equivalent. Makes sure the NSString + // object remains constant accross calls to AssignIDs and ToBinary + readonly Dictionary keys; + /// /// Creates a new empty NSDictionary. /// public NSDictionary() { dict = new Dictionary(); + keys = new Dictionary(); } /// @@ -366,7 +371,7 @@ namespace Claunia.PropertyList foreach (KeyValuePair entry in dict) { - new NSString(entry.Key).AssignIDs(outPlist); + keys[entry.Key].AssignIDs(outPlist); } foreach (KeyValuePair entry in dict) @@ -380,7 +385,7 @@ namespace Claunia.PropertyList outPlist.WriteIntHeader(0xD, dict.Count); foreach (KeyValuePair entry in dict) { - outPlist.WriteID(outPlist.GetID(new NSString(entry.Key))); + outPlist.WriteID(outPlist.GetID(keys[entry.Key])); } foreach (KeyValuePair entry in dict) { @@ -488,6 +493,7 @@ namespace Claunia.PropertyList public void Add(string key, NSObject value) { dict.Add(key, value); + keys.Add(key, new NSString(key)); } /// @@ -516,6 +522,7 @@ namespace Claunia.PropertyList /// Key. public bool Remove(string key) { + keys.Remove(key); return dict.Remove(key); } @@ -534,7 +541,7 @@ namespace Claunia.PropertyList /// Gets or sets the at the specified index. /// /// Index. - public NSObject this [string index] + public NSObject this[string index] { get { @@ -542,6 +549,11 @@ namespace Claunia.PropertyList } set { + if (!keys.ContainsKey(index)) + { + keys.Add(index, new NSString(index)); + } + dict[index] = value; } } @@ -579,6 +591,7 @@ namespace Claunia.PropertyList /// Item. public void Add(KeyValuePair item) { + keys.Add(item.Key, new NSString(item.Key)); dict.Add(item.Key, item.Value); } @@ -587,6 +600,7 @@ namespace Claunia.PropertyList /// public void Clear() { + keys.Clear(); dict.Clear(); } @@ -618,6 +632,7 @@ namespace Claunia.PropertyList /// true if successfully removed, false if not, or if item is not in current instance. public bool Remove(KeyValuePair item) { + keys.Remove(item.Key); return dict.Remove(item.Key); } From b4974acba9bb1c20cd0ec591d17a403fcdeb9b9e Mon Sep 17 00:00:00 2001 From: Frederik Carlier Date: Fri, 25 Nov 2016 17:44:27 +0100 Subject: [PATCH 3/3] Don't reuse object IDs for NSNumber objects Make the constructors and Write methods public --- plist-cil/BinaryPropertyListWriter.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plist-cil/BinaryPropertyListWriter.cs b/plist-cil/BinaryPropertyListWriter.cs index ca7adcb..5ea1ae7 100644 --- a/plist-cil/BinaryPropertyListWriter.cs +++ b/plist-cil/BinaryPropertyListWriter.cs @@ -188,18 +188,18 @@ namespace Claunia.PropertyList /// /// The output stream into which the binary property list will be written /// If an error occured while writing to the stream - BinaryPropertyListWriter(Stream outStr) + public BinaryPropertyListWriter(Stream outStr) { outStream = outStr; } - BinaryPropertyListWriter(Stream outStr, int version) + public BinaryPropertyListWriter(Stream outStr, int version) { this.version = version; outStream = outStr; } - void Write(NSObject root) + public void Write(NSObject root) { // magic bytes Write(new[] { (byte)'b', (byte)'p', (byte)'l', (byte)'i', (byte)'s', (byte)'t' }); @@ -287,7 +287,7 @@ namespace Claunia.PropertyList // If binary compatibility with the Apple format is required, // UID, NSArray and NSString objects are assigned a new ID, // even if they already exist in the file. - if (!this.ReuseObjectIds && (obj is UID || obj is NSArray)) + if (!this.ReuseObjectIds && (obj is UID || obj is NSNumber || obj is NSArray)) { idMap.Add(obj); }