internal void UpdateSortedPropertyCache(ref ReadStackFrame frame) { // Set the sorted property cache. Overwrite any existing cache which can occur in multi-threaded cases. // Todo: on classes with many properties (about 50) we need to switch to a hashtable. if (frame.PropertyRefCache != null) { List <PropertyRef> cache = frame.PropertyRefCache; // Add any missing properties. This creates a consistent cache count which is important for // the loop in GetProperty() when there are multiple threads in a race conditions each generating // a cache for a given POCO type but with different property counts in the json. while (cache.Count < _propertyRefs.Count) { for (int iProperty = 0; iProperty < _propertyRefs.Count; iProperty++) { PropertyRef propertyRef = _propertyRefs[iProperty]; bool found = false; int iCacheProperty = 0; for (; iCacheProperty < cache.Count; iCacheProperty++) { if (IsPropertyRefEqual(ref propertyRef, cache[iCacheProperty])) { // The property is already cached, skip to the next property. found = true; break; } } if (found == false) { cache.Add(propertyRef); break; } } } Debug.Assert(cache.Count == _propertyRefs.Count); _propertyRefsSorted = cache.ToArray(); frame.PropertyRefCache = null; } }
private JsonPropertyInfo GetPropertyThatHasAttribute(Type attributeType) { Debug.Assert(_propertyRefs != null); JsonPropertyInfo property = null; for (int iProperty = 0; iProperty < _propertyRefs.Count; iProperty++) { PropertyRef propertyRef = _propertyRefs[iProperty]; JsonPropertyInfo jsonPropertyInfo = propertyRef.Info; Attribute attribute = jsonPropertyInfo.PropertyInfo.GetCustomAttribute(attributeType); if (attribute != null) { if (property != null) { ThrowHelper.ThrowInvalidOperationException_SerializationDuplicateAttribute(attributeType); } property = jsonPropertyInfo; } } return(property); }
internal JsonPropertyInfo GetProperty(ReadOnlySpan <byte> propertyName, ref ReadStackFrame frame) { ulong key = GetKey(propertyName); JsonPropertyInfo info = null; // First try sorted lookup. int propertyIndex = frame.PropertyIndex; // If we're not trying to build the cache locally, and there is an existing cache, then use it. bool hasPropertyCache = frame.PropertyRefCache == null && _propertyRefsSorted != null; if (hasPropertyCache) { // This .Length is consistent no matter what json data intialized _propertyRefsSorted. int count = _propertyRefsSorted.Length; if (count != 0) { int iForward = propertyIndex; int iBackward = propertyIndex - 1; while (iForward < count || iBackward >= 0) { if (iForward < count) { if (TryIsPropertyRefEqual(ref _propertyRefsSorted[iForward], propertyName, key, ref info)) { return(info); } ++iForward; } if (iBackward >= 0) { if (TryIsPropertyRefEqual(ref _propertyRefsSorted[iBackward], propertyName, key, ref info)) { return(info); } --iBackward; } } } } // Try the main list which has all of the properties in a consistent order. // We could get here even when hasPropertyCache==true if there is a race condition with different json // property ordering and _propertyRefsSorted is re-assigned while in the loop above. for (int i = 0; i < _propertyRefs.Count; i++) { PropertyRef propertyRef = _propertyRefs[i]; if (TryIsPropertyRefEqual(ref propertyRef, propertyName, key, ref info)) { break; } } if (!hasPropertyCache) { if (propertyIndex == 0) { // Create the temporary list on first property access to prevent a partially filled List. Debug.Assert(frame.PropertyRefCache == null); frame.PropertyRefCache = new List <PropertyRef>(); } if (info != null) { Debug.Assert(frame.PropertyRefCache != null); frame.PropertyRefCache.Add(new PropertyRef(key, info)); } } return(info); }
internal JsonPropertyInfo GetProperty(JsonSerializerOptions options, ReadOnlySpan <byte> propertyName, ref ReadStackFrame frame) { // If we should compare with case-insensitive, normalize to an uppercase format since that is what is cached on the propertyInfo. if (options.PropertyNameCaseInsensitive) { string utf16PropertyName = JsonHelpers.Utf8GetString(propertyName); string upper = utf16PropertyName.ToUpperInvariant(); propertyName = Encoding.UTF8.GetBytes(upper); } ulong key = GetKey(propertyName); JsonPropertyInfo info = null; // First try sorted lookup. int propertyIndex = frame.PropertyIndex; // If we're not trying to build the cache locally, and there is an existing cache, then use it. bool hasPropertyCache = frame.PropertyRefCache == null && _propertyRefsSorted != null; if (hasPropertyCache) { // This .Length is consistent no matter what json data intialized _propertyRefsSorted. int count = _propertyRefsSorted.Length; if (count != 0) { int iForward = propertyIndex; int iBackward = propertyIndex - 1; while (iForward < count || iBackward >= 0) { if (iForward < count) { if (TryIsPropertyRefEqual(ref _propertyRefsSorted[iForward], propertyName, key, ref info)) { return(info); } ++iForward; } if (iBackward >= 0) { if (TryIsPropertyRefEqual(ref _propertyRefsSorted[iBackward], propertyName, key, ref info)) { return(info); } --iBackward; } } } } // Try the main list which has all of the properties in a consistent order. // We could get here even when hasPropertyCache==true if there is a race condition with different json // property ordering and _propertyRefsSorted is re-assigned while in the loop above. for (int i = 0; i < _propertyRefs.Count; i++) { PropertyRef propertyRef = _propertyRefs[i]; if (TryIsPropertyRefEqual(ref propertyRef, propertyName, key, ref info)) { break; } } if (!hasPropertyCache) { if (propertyIndex == 0 && frame.PropertyRefCache == null) { // Create the temporary list on first property access to prevent a partially filled List. frame.PropertyRefCache = new List <PropertyRef>(); } if (info != null) { Debug.Assert(frame.PropertyRefCache != null); frame.PropertyRefCache.Add(new PropertyRef(key, info)); } } return(info); }