Exemple #1
0
        /// <summary>
        ///   Creates the default range when no additional range data is available for the <paramref name="obj" />'s
        ///   <paramref name="field" />.
        /// </summary>
        internal static RangeMetadata CreateDefaultRange(object obj, FieldInfo field)
        {
            // Check if the serializer knows a range
            RangeAttribute range;

            if (SerializationRegistry.Default.GetSerializer(obj).TryGetRange(obj, field, out range))
            {
                return(RangeMetadata.Create(obj, field, range));
            }

            // Otherwise, maybe we can deduce a range for the type
            return(RangeMetadata.Create(obj, field, CreateDefaultRange(field.FieldType)));
        }
Exemple #2
0
		/// <summary>
		///   Compacts the state vector.
		/// </summary>
		/// <param name="model">The model whose state vector is compacted.</param>
		/// <param name="mode">The serialization mode the state vector is compacted for.</param>
		internal void Compact(ModelBase model, SerializationMode mode)
		{
			if (mode == SerializationMode.Optimized)
			{
				// Check if we can use the range to compress the data even further
				foreach (var slot in _slots)
				{
					if (slot.ContainedInStruct)
						slot.Range = Range.GetMetadata(slot.FieldChain.Last());
					else if (slot.Field != null)
						slot.Range = Range.GetMetadata(model, slot.Object, slot.Field);
					else
						slot.Range = RangeMetadata.Create(slot.Object, slot.DataType, Range.CreateDefaultRange(slot.DataType));

					if (slot.Range != null)
						slot.CompressedDataType = slot.Range.GetRangeType();
				}
			}

			// Organize all slots with the same element size into groups
			Groups = _slots
				.GroupBy(slot => slot.ElementSizeInBits)
				.OrderBy(group => group.Key)
				.Select(group => new CompactedStateGroup
				{
					Slots = group
						.OrderBy(slot => slot.ObjectIdentifier)
						.ThenBy(slot => slot.Field?.Name ?? "array")
						.ThenBy(slot => slot.Field?.DeclaringType?.FullName ?? "array")
						.ToArray()
				})
				.ToArray();

			// Compute the total state vector size and ensure alignment of the individual groups
			// Ensure that the state vector size is a multiple of 4 for correct alignment in state vector arrays
			for (var i = 0; i < Groups.Length; ++i)
			{
				Groups[i].OffsetInBytes = SizeInBytes;
				SizeInBytes += Groups[i].GroupSizeInBytes;

				var alignment = i + 1 >= Groups.Length ? 4 : Math.Min(4, Math.Max(1, Groups[i + 1].ElementSizeInBits / 8));
				var remainder = SizeInBytes % alignment;

				Groups[i].PaddingBytes = remainder == 0 ? 0 : alignment - remainder;
				SizeInBytes += Groups[i].PaddingBytes;
			}

			// We never generate 0-sized state vectors to avoid special casing
			if (SizeInBytes <= 0)
				SizeInBytes = 4;
		}
Exemple #3
0
        /// <summary>
        ///   Gets the range metadata for the <paramref name="obj" />'s <paramref name="field" />. Returns <c>null</c> when the range is
        ///   unrestricted.
        /// </summary>
        /// <param name="model">The model that stores the <paramref name="obj" />'s range metadata.</param>
        /// <param name="obj">The object the range metadata should be returned for.</param>
        /// <param name="field">The field the range metadata should be returned for.</param>
        internal static RangeMetadata GetMetadata(ModelBase model, object obj, FieldInfo field)
        {
            // For backing fields of auto-implemented properties, check the property instead
            // TODO: Remove this workaround once C# supports [field:Attribute] on properties
            var range = field.GetCustomAttribute <RangeAttribute>() ?? field?.GetAutoProperty()?.GetCustomAttribute <RangeAttribute>();

            if (range != null)
            {
                return(RangeMetadata.Create(obj, field, range));
            }

            var metadata = model.RangeMetadata.FirstOrDefault(m => m.DescribesField(obj, field));

            return(metadata ?? CreateDefaultRange(obj, field));
        }
Exemple #4
0
        /// <summary>
        ///   Restricts values that can be stored in the field referenced by the <paramref name="fieldExpression" /> to the range of
        ///   <paramref name="lowerBound" /> and <paramref name="upperBound" />, both inclusive, using the
        ///   <paramref name="overflowBehavior" /> to handle range overflows.
        /// </summary>
        /// <typeparam name="T">The type of the field that is restricted.</typeparam>
        /// <param name="fieldExpression">The expression referencing the field whose range should be restricted.</param>
        /// <param name="lowerBound">The inclusive lower bound.</param>
        /// <param name="upperBound">The inclusive upper bound.</param>
        /// <param name="overflowBehavior">The overflow behavior.</param>
        public static void Restrict <T>(Expression <Func <T> > fieldExpression, object lowerBound, object upperBound,
                                        OverflowBehavior overflowBehavior)
            where T : struct, IComparable
        {
            Requires.NotNull(fieldExpression, nameof(fieldExpression));
            Requires.InRange(overflowBehavior, nameof(overflowBehavior));
            Requires.That(typeof(T).IsNumericType(), nameof(fieldExpression), "Expected a field of numeric type.");
            Requires.OfType <MemberExpression>(fieldExpression.Body, nameof(fieldExpression), "Expected a non-nested reference to a field.");

            var range            = new RangeAttribute(lowerBound, upperBound, overflowBehavior);
            var memberExpression = (MemberExpression)fieldExpression.Body;
            var propertyInfo     = memberExpression.Member as PropertyInfo;
            var fieldInfo        = propertyInfo?.GetBackingField() ?? memberExpression.Member as FieldInfo;
            var objectExpression = memberExpression.Expression as ConstantExpression;

            Requires.That(fieldInfo != null, nameof(fieldExpression), "Expected a non-nested reference to a field or an auto-property.");
            Requires.That(objectExpression != null, nameof(fieldExpression), "Expected a non-nested reference to non-static field of primitive type.");
            Requires.That(((IComparable)range.LowerBound).CompareTo(range.UpperBound) <= 0, nameof(lowerBound),
                          $"lower bound '{range.LowerBound}' is not smaller than upper bound '{range.UpperBound}'.");

            List <RangeMetadata> fields;

            if (_ranges.TryGetValue(objectExpression.Value, out fields))
            {
                var metadata = fields.FirstOrDefault(m => m.DescribesField(objectExpression.Value, fieldInfo));
                if (metadata != null)
                {
                    fields.Remove(metadata);
                }

                fields.Add(RangeMetadata.Create(objectExpression.Value, fieldInfo, range));
            }
            else
            {
                _ranges.Add(objectExpression.Value, new List <RangeMetadata> {
                    RangeMetadata.Create(objectExpression.Value, fieldInfo, range)
                });
            }
        }
Exemple #5
0
        /// <summary>
        ///   Gets the range metadata for the <paramref name="field" />. Returns <c>null</c> when the range is unrestricted.
        /// </summary>
        /// <param name="field">The field the range metadata should be returned for.</param>
        internal static RangeMetadata GetMetadata(FieldInfo field)
        {
            var range = field.GetCustomAttribute <RangeAttribute>();

            return(range == null ? null : RangeMetadata.Create(null, field, range));
        }