Explorar o código

Reduce string allocations at startup

Bond_009 %!s(int64=6) %!d(string=hai) anos
pai
achega
320707d44c

+ 66 - 95
Emby.Server.Implementations/Services/ServicePath.cs

@@ -16,7 +16,7 @@ namespace Emby.Server.Implementations.Services
         private const char ComponentSeperator = '.';
         private const string VariablePrefix = "{";
 
-        readonly bool[] componentsWithSeparators;
+        private readonly bool[] componentsWithSeparators;
 
         private readonly string restPath;
         public bool IsWildCardPath { get; private set; }
@@ -54,10 +54,6 @@ namespace Emby.Server.Implementations.Services
         public string Description { get; private set; }
         public bool IsHidden { get; private set; }
 
-        public int Priority { get; set; } //passed back to RouteAttribute
-
-        public IEnumerable<string> PathVariables => this.variablesNames.Where(e => !string.IsNullOrWhiteSpace(e));
-
         public static string[] GetPathPartsForMatching(string pathInfo)
         {
             return pathInfo.ToLowerInvariant().Split(new[] { PathSeperatorChar }, StringSplitOptions.RemoveEmptyEntries);
@@ -83,9 +79,12 @@ namespace Emby.Server.Implementations.Services
             {
                 list.Add(hashPrefix + part);
 
-                var subParts = part.Split(ComponentSeperator);
-                if (subParts.Length == 1) continue;
+                if (part.IndexOf(ComponentSeperator) == -1)
+                {
+                    continue;
+                }
 
+                var subParts = part.Split(ComponentSeperator);
                 foreach (var subPart in subParts)
                 {
                     list.Add(hashPrefix + subPart);
@@ -114,7 +113,7 @@ namespace Emby.Server.Implementations.Services
             {
                 if (string.IsNullOrEmpty(component)) continue;
 
-                if (StringContains(component, VariablePrefix)
+                if (component.IndexOf(VariablePrefix, StringComparison.OrdinalIgnoreCase) != -1
                     && component.IndexOf(ComponentSeperator) != -1)
                 {
                     hasSeparators.Add(true);
@@ -165,7 +164,11 @@ namespace Emby.Server.Implementations.Services
 
             for (var i = 0; i < components.Length - 1; i++)
             {
-                if (!this.isWildcard[i]) continue;
+                if (!this.isWildcard[i])
+                {
+                    continue;
+                }
+
                 if (this.literalsToMatch[i + 1] == null)
                 {
                     throw new ArgumentException(
@@ -173,7 +176,7 @@ namespace Emby.Server.Implementations.Services
                 }
             }
 
-            this.wildcardCount = this.isWildcard.Count(x => x);
+            this.wildcardCount = this.isWildcard.Length;
             this.IsWildCardPath = this.wildcardCount > 0;
 
             this.FirstMatchHashKey = !this.IsWildCardPath
@@ -181,19 +184,14 @@ namespace Emby.Server.Implementations.Services
                 : WildCardChar + PathSeperator + firstLiteralMatch;
 
             this.typeDeserializer = new StringMapTypeDeserializer(createInstanceFn, getParseFn, this.RequestType);
-            RegisterCaseInsenstivePropertyNameMappings();
-        }
 
-        private void RegisterCaseInsenstivePropertyNameMappings()
-        {
-            foreach (var propertyInfo in GetSerializableProperties(RequestType))
-            {
-                var propertyName = propertyInfo.Name;
-                propertyNamesMap.Add(propertyName.ToLowerInvariant(), propertyName);
-            }
+            _propertyNamesMap = new HashSet<string>(
+                    GetSerializableProperties(RequestType).Select(x => x.Name),
+                    StringComparer.OrdinalIgnoreCase);
         }
 
-        internal static string[] IgnoreAttributesNamed = new[] {
+        internal static string[] IgnoreAttributesNamed = new[]
+        {
             "IgnoreDataMemberAttribute",
             "JsonIgnoreAttribute"
         };
@@ -201,19 +199,12 @@ namespace Emby.Server.Implementations.Services
 
         private static Type excludeType = typeof(Stream);
 
-        internal static List<PropertyInfo> GetSerializableProperties(Type type)
+        internal static IEnumerable<PropertyInfo> GetSerializableProperties(Type type)
         {
-            var list = new List<PropertyInfo>();
-            var props = GetPublicProperties(type);
-
-            foreach (var prop in props)
+            foreach (var prop in GetPublicProperties(type))
             {
-                if (prop.GetMethod == null)
-                {
-                    continue;
-                }
-
-                if (excludeType == prop.PropertyType)
+                if (prop.GetMethod == null
+                    || excludeType == prop.PropertyType)
                 {
                     continue;
                 }
@@ -230,23 +221,21 @@ namespace Emby.Server.Implementations.Services
 
                 if (!ignored)
                 {
-                    list.Add(prop);
+                    yield return prop;
                 }
             }
-
-            // else return those properties that are not decorated with IgnoreDataMember
-            return list;
         }
 
-        private static List<PropertyInfo> GetPublicProperties(Type type)
+        private static IEnumerable<PropertyInfo> GetPublicProperties(Type type)
         {
-            if (type.GetTypeInfo().IsInterface)
+            if (type.IsInterface)
             {
                 var propertyInfos = new List<PropertyInfo>();
-
-                var considered = new List<Type>();
+                var considered = new List<Type>()
+                {
+                    type
+                };
                 var queue = new Queue<Type>();
-                considered.Add(type);
                 queue.Enqueue(type);
 
                 while (queue.Count > 0)
@@ -254,15 +243,16 @@ namespace Emby.Server.Implementations.Services
                     var subType = queue.Dequeue();
                     foreach (var subInterface in subType.GetTypeInfo().ImplementedInterfaces)
                     {
-                        if (considered.Contains(subInterface)) continue;
+                        if (considered.Contains(subInterface))
+                        {
+                            continue;
+                        }
 
                         considered.Add(subInterface);
                         queue.Enqueue(subInterface);
                     }
 
-                    var typeProperties = GetTypesPublicProperties(subType);
-
-                    var newPropertyInfos = typeProperties
+                    var newPropertyInfos = GetTypesPublicProperties(subType)
                         .Where(x => !propertyInfos.Contains(x));
 
                     propertyInfos.InsertRange(0, newPropertyInfos);
@@ -271,28 +261,22 @@ namespace Emby.Server.Implementations.Services
                 return propertyInfos;
             }
 
-            var list = new List<PropertyInfo>();
-
-            foreach (var t in GetTypesPublicProperties(type))
-            {
-                if (t.GetIndexParameters().Length == 0)
-                {
-                    list.Add(t);
-                }
-            }
-            return list;
+            return GetTypesPublicProperties(type)
+                .Where(x => x.GetIndexParameters().Length == 0);
         }
 
-        private static PropertyInfo[] GetTypesPublicProperties(Type subType)
+        private static IEnumerable<PropertyInfo> GetTypesPublicProperties(Type subType)
         {
-            var pis = new List<PropertyInfo>();
             foreach (var pi in subType.GetRuntimeProperties())
             {
                 var mi = pi.GetMethod ?? pi.SetMethod;
-                if (mi != null && mi.IsStatic) continue;
-                pis.Add(pi);
+                if (mi != null && mi.IsStatic)
+                {
+                    continue;
+                }
+
+                yield return pi;
             }
-            return pis.ToArray();
         }
 
         /// <summary>
@@ -302,7 +286,7 @@ namespace Emby.Server.Implementations.Services
 
         private readonly StringMapTypeDeserializer typeDeserializer;
 
-        private readonly Dictionary<string, string> propertyNamesMap = new Dictionary<string, string>();
+        private readonly HashSet<string> _propertyNamesMap;
 
         public int MatchScore(string httpMethod, string[] withPathInfoParts)
         {
@@ -312,13 +296,10 @@ namespace Emby.Server.Implementations.Services
                 return -1;
             }
 
-            var score = 0;
-
             //Routes with least wildcard matches get the highest score
-            score += Math.Max((100 - wildcardMatchCount), 1) * 1000;
-
-            //Routes with less variable (and more literal) matches
-            score += Math.Max((10 - VariableArgsCount), 1) * 100;
+            var score = Math.Max((100 - wildcardMatchCount), 1) * 1000
+                        //Routes with less variable (and more literal) matches
+                        + Math.Max((10 - VariableArgsCount), 1) * 100;
 
             //Exact verb match is better than ANY
             if (Verbs.Length == 1 && string.Equals(httpMethod, Verbs[0], StringComparison.OrdinalIgnoreCase))
@@ -333,11 +314,6 @@ namespace Emby.Server.Implementations.Services
             return score;
         }
 
-        private bool StringContains(string str1, string str2)
-        {
-            return str1.IndexOf(str2, StringComparison.OrdinalIgnoreCase) != -1;
-        }
-
         /// <summary>
         /// For performance withPathInfoParts should already be a lower case string
         /// to minimize redundant matching operations.
@@ -374,7 +350,8 @@ namespace Emby.Server.Implementations.Services
                     if (i < this.TotalComponentsCount - 1)
                     {
                         // Continue to consume up until a match with the next literal
-                        while (pathIx < withPathInfoParts.Length && !LiteralsEqual(withPathInfoParts[pathIx], this.literalsToMatch[i + 1]))
+                        while (pathIx < withPathInfoParts.Length
+                            && !string.Equals(withPathInfoParts[pathIx], this.literalsToMatch[i + 1], StringComparison.InvariantCultureIgnoreCase))
                         {
                             pathIx++;
                             wildcardMatchCount++;
@@ -403,10 +380,12 @@ namespace Emby.Server.Implementations.Services
                         continue;
                     }
 
-                    if (withPathInfoParts.Length <= pathIx || !LiteralsEqual(withPathInfoParts[pathIx], literalToMatch))
+                    if (withPathInfoParts.Length <= pathIx
+                        || !string.Equals(withPathInfoParts[pathIx], literalToMatch, StringComparison.InvariantCultureIgnoreCase))
                     {
                         return false;
                     }
+
                     pathIx++;
                 }
             }
@@ -414,35 +393,26 @@ namespace Emby.Server.Implementations.Services
             return pathIx == withPathInfoParts.Length;
         }
 
-        private static bool LiteralsEqual(string str1, string str2)
-        {
-            // Most cases
-            if (string.Equals(str1, str2, StringComparison.OrdinalIgnoreCase))
-            {
-                return true;
-            }
-
-            // Handle turkish i
-            str1 = str1.ToUpperInvariant();
-            str2 = str2.ToUpperInvariant();
-
-            // Invariant IgnoreCase would probably be better but it's not available in PCL
-            return string.Equals(str1, str2, StringComparison.CurrentCultureIgnoreCase);
-        }
-
         private bool ExplodeComponents(ref string[] withPathInfoParts)
         {
             var totalComponents = new List<string>();
             for (var i = 0; i < withPathInfoParts.Length; i++)
             {
                 var component = withPathInfoParts[i];
-                if (string.IsNullOrEmpty(component)) continue;
+                if (string.IsNullOrEmpty(component))
+                {
+                    continue;
+                }
 
                 if (this.PathComponentsCount != this.TotalComponentsCount
                     && this.componentsWithSeparators[i])
                 {
                     var subComponents = component.Split(ComponentSeperator);
-                    if (subComponents.Length < 2) return false;
+                    if (subComponents.Length < 2)
+                    {
+                        return false;
+                    }
+
                     totalComponents.AddRange(subComponents);
                 }
                 else
@@ -483,7 +453,7 @@ namespace Emby.Server.Implementations.Services
                     continue;
                 }
 
-                if (!this.propertyNamesMap.TryGetValue(variableName.ToLowerInvariant(), out var propertyNameOnRequest))
+                if (!this._propertyNamesMap.Contains(variableName))
                 {
                     if (string.Equals("ignore", variableName, StringComparison.OrdinalIgnoreCase))
                     {
@@ -507,6 +477,7 @@ namespace Emby.Server.Implementations.Services
                         {
                             sb.Append(PathSeperatorChar + requestComponents[j]);
                         }
+
                         value = sb.ToString();
                     }
                     else
@@ -517,13 +488,13 @@ namespace Emby.Server.Implementations.Services
                         var stopLiteral = i == this.TotalComponentsCount - 1 ? null : this.literalsToMatch[i + 1];
                         if (!string.Equals(requestComponents[pathIx], stopLiteral, StringComparison.OrdinalIgnoreCase))
                         {
-                            var sb = new StringBuilder();
-                            sb.Append(value);
+                            var sb = new StringBuilder(value);
                             pathIx++;
                             while (!string.Equals(requestComponents[pathIx], stopLiteral, StringComparison.OrdinalIgnoreCase))
                             {
                                 sb.Append(PathSeperatorChar + requestComponents[pathIx++]);
                             }
+
                             value = sb.ToString();
                         }
                         else
@@ -538,7 +509,7 @@ namespace Emby.Server.Implementations.Services
                     pathIx++;
                 }
 
-                requestKeyValuesMap[propertyNameOnRequest] = value;
+                requestKeyValuesMap[variableName] = value;
             }
 
             if (queryStringAndFormData != null)

+ 25 - 27
Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs

@@ -11,15 +11,16 @@ namespace Emby.Server.Implementations.Services
     {
         internal class PropertySerializerEntry
         {
-            public PropertySerializerEntry(Action<object, object> propertySetFn, Func<string, object> propertyParseStringFn)
+            public PropertySerializerEntry(Action<object, object> propertySetFn, Func<string, object> propertyParseStringFn, Type propertyType)
             {
                 PropertySetFn = propertySetFn;
                 PropertyParseStringFn = propertyParseStringFn;
+                PropertyType = PropertyType;
             }
 
-            public Action<object, object> PropertySetFn;
-            public Func<string, object> PropertyParseStringFn;
-            public Type PropertyType;
+            public Action<object, object> PropertySetFn { get; private set; }
+            public Func<string, object> PropertyParseStringFn { get; private set; }
+            public Type PropertyType { get; private set; }
         }
 
         private readonly Type type;
@@ -29,7 +30,9 @@ namespace Emby.Server.Implementations.Services
         public Func<string, object> GetParseFn(Type propertyType)
         {
             if (propertyType == typeof(string))
+            {
                 return s => s;
+            }
 
             return _GetParseFn(propertyType);
         }
@@ -48,7 +51,7 @@ namespace Emby.Server.Implementations.Services
                 var propertySetFn = TypeAccessor.GetSetPropertyMethod(type, propertyInfo);
                 var propertyType = propertyInfo.PropertyType;
                 var propertyParseStringFn = GetParseFn(propertyType);
-                var propertySerializer = new PropertySerializerEntry(propertySetFn, propertyParseStringFn) { PropertyType = propertyType };
+                var propertySerializer = new PropertySerializerEntry(propertySetFn, propertyParseStringFn, propertyType);
 
                 propertySetterMap[propertyInfo.Name] = propertySerializer;
             }
@@ -56,34 +59,21 @@ namespace Emby.Server.Implementations.Services
 
         public object PopulateFromMap(object instance, IDictionary<string, string> keyValuePairs)
         {
-            string propertyName = null;
-            string propertyTextValue = null;
             PropertySerializerEntry propertySerializerEntry = null;
 
             if (instance == null)
+            {
                 instance = _CreateInstanceFn(type);
+            }
 
             foreach (var pair in keyValuePairs)
             {
-                propertyName = pair.Key;
-                propertyTextValue = pair.Value;
-
-                if (string.IsNullOrEmpty(propertyTextValue))
-                {
-                    continue;
-                }
+                string propertyName = pair.Key;
+                string propertyTextValue = pair.Value;
 
-                if (!propertySetterMap.TryGetValue(propertyName, out propertySerializerEntry))
-                {
-                    if (propertyName == "v")
-                    {
-                        continue;
-                    }
-
-                    continue;
-                }
-
-                if (propertySerializerEntry.PropertySetFn == null)
+                if (string.IsNullOrEmpty(propertyTextValue)
+                    || !propertySetterMap.TryGetValue(propertyName, out propertySerializerEntry)
+                    || propertySerializerEntry.PropertySetFn == null)
                 {
                     continue;
                 }
@@ -99,6 +89,7 @@ namespace Emby.Server.Implementations.Services
                 {
                     continue;
                 }
+
                 propertySerializerEntry.PropertySetFn(instance, value);
             }
 
@@ -107,7 +98,11 @@ namespace Emby.Server.Implementations.Services
 
         public static string LeftPart(string strVal, char needle)
         {
-            if (strVal == null) return null;
+            if (strVal == null)
+            {
+                return null;
+            }
+
             var pos = strVal.IndexOf(needle);
             return pos == -1
                 ? strVal
@@ -119,7 +114,10 @@ namespace Emby.Server.Implementations.Services
     {
         public static Action<object, object> GetSetPropertyMethod(Type type, PropertyInfo propertyInfo)
         {
-            if (!propertyInfo.CanWrite || propertyInfo.GetIndexParameters().Length > 0) return null;
+            if (!propertyInfo.CanWrite || propertyInfo.GetIndexParameters().Length > 0)
+            {
+                return null;
+            }
 
             var setMethodInfo = propertyInfo.SetMethod;
             return (instance, value) => setMethodInfo.Invoke(instance, new[] { value });