diff --git a/Octokit/SimpleJson.cs b/Octokit/SimpleJson.cs index b8300f34..9266c3dc 100644 --- a/Octokit/SimpleJson.cs +++ b/Octokit/SimpleJson.cs @@ -17,7 +17,7 @@ // https://github.com/facebook-csharp-sdk/simple-json //----------------------------------------------------------------------- -// VERSION: 0.30.0 +// VERSION: 0.34.0 // NOTE: uncomment the following line to make SimpleJson class internal. //#define SIMPLE_JSON_INTERNAL @@ -31,6 +31,9 @@ // NOTE: uncomment the following line to enable DataContract support. //#define SIMPLE_JSON_DATACONTRACT +// NOTE: uncomment the following line to enable IReadOnlyCollection and IReadOnlyList support. +//#define SIMPLE_JSON_READONLY_COLLECTIONS + // NOTE: uncomment the following line to disable linq expressions/compiled lambda (better performance) instead of method.invoke(). // define if you are using .net framework <= 3.0 or < WP7.5 //#define SIMPLE_JSON_NO_LINQ_EXPRESSION @@ -51,7 +54,6 @@ using System; using System.CodeDom.Compiler; using System.Collections; using System.Collections.Generic; -using System.Linq; #if !SIMPLE_JSON_NO_LINQ_EXPRESSION using System.Linq.Expressions; #endif @@ -513,6 +515,22 @@ namespace Octokit private const int TOKEN_NULL = 11; private const int BUILDER_CAPACITY = 2000; + private static readonly char[] EscapeTable; + private static readonly char[] EscapeCharacters = new char[] { '"', '\\', '\b', '\f', '\n', '\r', '\t' }; + private static readonly string EscapeCharactersString = new string(EscapeCharacters); + + static SimpleJson() + { + EscapeTable = new char[93]; + EscapeTable['"'] = '"'; + EscapeTable['\\'] = '\\'; + EscapeTable['\b'] = 'b'; + EscapeTable['\f'] = 'f'; + EscapeTable['\n'] = 'n'; + EscapeTable['\r'] = 'r'; + EscapeTable['\t'] = 't'; + } + /// /// Parses the string json into a value /// @@ -1074,29 +1092,50 @@ namespace Octokit static bool SerializeString(string aString, StringBuilder builder) { - builder.Append("\""); + // Happy path if there's nothing to be escaped. IndexOfAny is highly optimized (and unmanaged) + if (aString.IndexOfAny(EscapeCharacters) == -1) + { + builder.Append('"'); + builder.Append(aString); + builder.Append('"'); + + return true; + } + + builder.Append('"'); + int safeCharacterCount = 0; char[] charArray = aString.ToCharArray(); + for (int i = 0; i < charArray.Length; i++) { char c = charArray[i]; - if (c == '"') - builder.Append("\\\""); - else if (c == '\\') - builder.Append("\\\\"); - else if (c == '\b') - builder.Append("\\b"); - else if (c == '\f') - builder.Append("\\f"); - else if (c == '\n') - builder.Append("\\n"); - else if (c == '\r') - builder.Append("\\r"); - else if (c == '\t') - builder.Append("\\t"); + + // Non ascii characters are fine, buffer them up and send them to the builder + // in larger chunks if possible. The escape table is a 1:1 translation table + // with \0 [default(char)] denoting a safe character. + if (c >= EscapeTable.Length || EscapeTable[c] == default(char)) + { + safeCharacterCount++; + } else - builder.Append(c); + { + if (safeCharacterCount > 0) + { + builder.Append(charArray, i - safeCharacterCount, safeCharacterCount); + safeCharacterCount = 0; + } + + builder.Append('\\'); + builder.Append(EscapeTable[c]); + } } - builder.Append("\""); + + if (safeCharacterCount > 0) + { + builder.Append(charArray, charArray.Length - safeCharacterCount, safeCharacterCount); + } + + builder.Append('"'); return true; } @@ -1647,7 +1686,14 @@ namespace Octokit Type genericDefinition = type.GetGenericTypeDefinition(); - return (genericDefinition == typeof(IList<>) || genericDefinition == typeof(ICollection<>) || genericDefinition == typeof(IEnumerable<>)); + return (genericDefinition == typeof(IList<>) + || genericDefinition == typeof(ICollection<>) + || genericDefinition == typeof(IEnumerable<>) +#if SIMPLE_JSON_READONLY_COLLECTIONS + || genericDefinition == typeof(IReadOnlyCollection<>) + || genericDefinition == typeof(IReadOnlyList<>) +#endif + ); } public static bool IsAssignableFrom(Type type1, Type type2) @@ -1727,13 +1773,7 @@ namespace Octokit public static IEnumerable GetProperties(Type type) { #if SIMPLE_JSON_TYPEINFO - var info = type.GetTypeInfo(); - - var baseProperties = info.BaseType != null && info.BaseType != typeof(Object) - ? GetProperties(info.BaseType) - : new PropertyInfo[0]; - - return info.DeclaredProperties.Concat(baseProperties); + return type.GetTypeInfo().DeclaredProperties; #else return type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); #endif diff --git a/Octokit/packages.config b/Octokit/packages.config index fe57a08e..3771b8bf 100644 --- a/Octokit/packages.config +++ b/Octokit/packages.config @@ -6,5 +6,5 @@ - + \ No newline at end of file diff --git a/packages/SimpleJson.0.30.0/SimpleJson.0.30.0.nupkg b/packages/SimpleJson.0.30.0/SimpleJson.0.30.0.nupkg deleted file mode 100644 index b35a0dda..00000000 Binary files a/packages/SimpleJson.0.30.0/SimpleJson.0.30.0.nupkg and /dev/null differ diff --git a/packages/SimpleJson.0.30.0/SimpleJson.0.30.0.nuspec b/packages/SimpleJson.0.30.0/SimpleJson.0.30.0.nuspec deleted file mode 100644 index bfc5f00d..00000000 --- a/packages/SimpleJson.0.30.0/SimpleJson.0.30.0.nuspec +++ /dev/null @@ -1,15 +0,0 @@ - - - - SimpleJson - 0.30.0 - Jim Zimmerman, Nathan Totten, Prabir Shrestha - Jim Zimmerman, Nathan Totten, Prabir Shrestha - https://raw.github.com/facebook-csharp-sdk/simple-json/master/LICENSE.txt - https://raw.github.com/facebook-csharp-sdk/simple-json - false - Super lightweight Json library for .NET 2.0+/SL4+/WP7/WindowsStore Apps/Portable Class Libraries along with dynamic and DataContract support - en-US - json - - \ No newline at end of file diff --git a/packages/SimpleJson.0.34.0/SimpleJson.0.34.0.nupkg b/packages/SimpleJson.0.34.0/SimpleJson.0.34.0.nupkg new file mode 100644 index 00000000..3dbe14c4 Binary files /dev/null and b/packages/SimpleJson.0.34.0/SimpleJson.0.34.0.nupkg differ diff --git a/packages/SimpleJson.0.30.0/SimpleJson.psm1 b/packages/SimpleJson.0.34.0/SimpleJson.psm1 similarity index 97% rename from packages/SimpleJson.0.30.0/SimpleJson.psm1 rename to packages/SimpleJson.0.34.0/SimpleJson.psm1 index d34066dd..7cfbf98b 100644 --- a/packages/SimpleJson.0.30.0/SimpleJson.psm1 +++ b/packages/SimpleJson.0.34.0/SimpleJson.psm1 @@ -1,6 +1,6 @@ # SimpleJson https://github.com/facebook-csharp-sdk/simple-json # License: MIT License -# Version: 0.30.0 +# Version: 0.34.0 function ConvertFrom-Json { @@ -87,7 +87,7 @@ $source = @" // https://github.com/facebook-csharp-sdk/simple-json //----------------------------------------------------------------------- -// VERSION: 0.30.0 +// VERSION: 0.34.0 // NOTE: uncomment the following line to make SimpleJson class internal. //#define SIMPLE_JSON_INTERNAL @@ -101,6 +101,9 @@ $source = @" // NOTE: uncomment the following line to enable DataContract support. //#define SIMPLE_JSON_DATACONTRACT +// NOTE: uncomment the following line to enable IReadOnlyCollection and IReadOnlyList support. +//#define SIMPLE_JSON_READONLY_COLLECTIONS + // NOTE: uncomment the following line to disable linq expressions/compiled lambda (better performance) instead of method.invoke(). // define if you are using .net framework <= 3.0 or < WP7.5 //#define SIMPLE_JSON_NO_LINQ_EXPRESSION @@ -582,6 +585,22 @@ namespace SimpleJson private const int TOKEN_NULL = 11; private const int BUILDER_CAPACITY = 2000; + private static readonly char[] EscapeTable; + private static readonly char[] EscapeCharacters = new char[] { '"', '\\', '\b', '\f', '\n', '\r', '\t' }; + private static readonly string EscapeCharactersString = new string(EscapeCharacters); + + static SimpleJson() + { + EscapeTable = new char[93]; + EscapeTable['"'] = '"'; + EscapeTable['\\'] = '\\'; + EscapeTable['\b'] = 'b'; + EscapeTable['\f'] = 'f'; + EscapeTable['\n'] = 'n'; + EscapeTable['\r'] = 'r'; + EscapeTable['\t'] = 't'; + } + /// /// Parses the string json into a value /// @@ -1143,29 +1162,50 @@ namespace SimpleJson static bool SerializeString(string aString, StringBuilder builder) { - builder.Append("\""); + // Happy path if there's nothing to be escaped. IndexOfAny is highly optimized (and unmanaged) + if (aString.IndexOfAny(EscapeCharacters) == -1) + { + builder.Append('"'); + builder.Append(aString); + builder.Append('"'); + + return true; + } + + builder.Append('"'); + int safeCharacterCount = 0; char[] charArray = aString.ToCharArray(); + for (int i = 0; i < charArray.Length; i++) { char c = charArray[i]; - if (c == '"') - builder.Append("\\\""); - else if (c == '\\') - builder.Append("\\\\"); - else if (c == '\b') - builder.Append("\\b"); - else if (c == '\f') - builder.Append("\\f"); - else if (c == '\n') - builder.Append("\\n"); - else if (c == '\r') - builder.Append("\\r"); - else if (c == '\t') - builder.Append("\\t"); + + // Non ascii characters are fine, buffer them up and send them to the builder + // in larger chunks if possible. The escape table is a 1:1 translation table + // with \0 [default(char)] denoting a safe character. + if (c >= EscapeTable.Length || EscapeTable[c] == default(char)) + { + safeCharacterCount++; + } else - builder.Append(c); + { + if (safeCharacterCount > 0) + { + builder.Append(charArray, i - safeCharacterCount, safeCharacterCount); + safeCharacterCount = 0; + } + + builder.Append('\\'); + builder.Append(EscapeTable[c]); + } } - builder.Append("\""); + + if (safeCharacterCount > 0) + { + builder.Append(charArray, charArray.Length - safeCharacterCount, safeCharacterCount); + } + + builder.Append('"'); return true; } @@ -1716,7 +1756,14 @@ namespace SimpleJson Type genericDefinition = type.GetGenericTypeDefinition(); - return (genericDefinition == typeof(IList<>) || genericDefinition == typeof(ICollection<>) || genericDefinition == typeof(IEnumerable<>)); + return (genericDefinition == typeof(IList<>) + || genericDefinition == typeof(ICollection<>) + || genericDefinition == typeof(IEnumerable<>) +#if SIMPLE_JSON_READONLY_COLLECTIONS + || genericDefinition == typeof(IReadOnlyCollection<>) + || genericDefinition == typeof(IReadOnlyList<>) +#endif + ); } public static bool IsAssignableFrom(Type type1, Type type2) diff --git a/packages/SimpleJson.0.30.0/content/SimpleJson.cs.pp b/packages/SimpleJson.0.34.0/content/SimpleJson.cs.pp similarity index 97% rename from packages/SimpleJson.0.30.0/content/SimpleJson.cs.pp rename to packages/SimpleJson.0.34.0/content/SimpleJson.cs.pp index 47dbe189..b7c86642 100644 --- a/packages/SimpleJson.0.30.0/content/SimpleJson.cs.pp +++ b/packages/SimpleJson.0.34.0/content/SimpleJson.cs.pp @@ -17,7 +17,7 @@ // https://github.com/facebook-csharp-sdk/simple-json //----------------------------------------------------------------------- -// VERSION: 0.30.0 +// VERSION: 0.34.0 // NOTE: uncomment the following line to make SimpleJson class internal. //#define SIMPLE_JSON_INTERNAL @@ -31,6 +31,9 @@ // NOTE: uncomment the following line to enable DataContract support. //#define SIMPLE_JSON_DATACONTRACT +// NOTE: uncomment the following line to enable IReadOnlyCollection and IReadOnlyList support. +//#define SIMPLE_JSON_READONLY_COLLECTIONS + // NOTE: uncomment the following line to disable linq expressions/compiled lambda (better performance) instead of method.invoke(). // define if you are using .net framework <= 3.0 or < WP7.5 //#define SIMPLE_JSON_NO_LINQ_EXPRESSION @@ -512,6 +515,22 @@ namespace $rootnamespace$ private const int TOKEN_NULL = 11; private const int BUILDER_CAPACITY = 2000; + private static readonly char[] EscapeTable; + private static readonly char[] EscapeCharacters = new char[] { '"', '\\', '\b', '\f', '\n', '\r', '\t' }; + private static readonly string EscapeCharactersString = new string(EscapeCharacters); + + static SimpleJson() + { + EscapeTable = new char[93]; + EscapeTable['"'] = '"'; + EscapeTable['\\'] = '\\'; + EscapeTable['\b'] = 'b'; + EscapeTable['\f'] = 'f'; + EscapeTable['\n'] = 'n'; + EscapeTable['\r'] = 'r'; + EscapeTable['\t'] = 't'; + } + /// /// Parses the string json into a value /// @@ -1073,29 +1092,50 @@ namespace $rootnamespace$ static bool SerializeString(string aString, StringBuilder builder) { - builder.Append("\""); + // Happy path if there's nothing to be escaped. IndexOfAny is highly optimized (and unmanaged) + if (aString.IndexOfAny(EscapeCharacters) == -1) + { + builder.Append('"'); + builder.Append(aString); + builder.Append('"'); + + return true; + } + + builder.Append('"'); + int safeCharacterCount = 0; char[] charArray = aString.ToCharArray(); + for (int i = 0; i < charArray.Length; i++) { char c = charArray[i]; - if (c == '"') - builder.Append("\\\""); - else if (c == '\\') - builder.Append("\\\\"); - else if (c == '\b') - builder.Append("\\b"); - else if (c == '\f') - builder.Append("\\f"); - else if (c == '\n') - builder.Append("\\n"); - else if (c == '\r') - builder.Append("\\r"); - else if (c == '\t') - builder.Append("\\t"); + + // Non ascii characters are fine, buffer them up and send them to the builder + // in larger chunks if possible. The escape table is a 1:1 translation table + // with \0 [default(char)] denoting a safe character. + if (c >= EscapeTable.Length || EscapeTable[c] == default(char)) + { + safeCharacterCount++; + } else - builder.Append(c); + { + if (safeCharacterCount > 0) + { + builder.Append(charArray, i - safeCharacterCount, safeCharacterCount); + safeCharacterCount = 0; + } + + builder.Append('\\'); + builder.Append(EscapeTable[c]); + } } - builder.Append("\""); + + if (safeCharacterCount > 0) + { + builder.Append(charArray, charArray.Length - safeCharacterCount, safeCharacterCount); + } + + builder.Append('"'); return true; } @@ -1646,7 +1686,14 @@ namespace $rootnamespace$ Type genericDefinition = type.GetGenericTypeDefinition(); - return (genericDefinition == typeof(IList<>) || genericDefinition == typeof(ICollection<>) || genericDefinition == typeof(IEnumerable<>)); + return (genericDefinition == typeof(IList<>) + || genericDefinition == typeof(ICollection<>) + || genericDefinition == typeof(IEnumerable<>) +#if SIMPLE_JSON_READONLY_COLLECTIONS + || genericDefinition == typeof(IReadOnlyCollection<>) + || genericDefinition == typeof(IReadOnlyList<>) +#endif + ); } public static bool IsAssignableFrom(Type type1, Type type2)