private static void ValidateRange(Type serializeType, IEnumerable <PropertyInfo> properties) { var bitsUsed = new HashSet <int>(); int maxBit = 0; int minBit = int.MaxValue; foreach (PropertyInfo prop in properties) { ValidateProperty(serializeType, prop); PackedBitRangeAttribute bitRangeAttr = prop.GetCustomAttribute <PackedBitRangeAttribute>(); // Validation if (bitRangeAttr.HasHighBit) { if (bitRangeAttr.LowBit <= 0 || bitRangeAttr.HighBit <= 0) { throw new ArgumentException($"Bits must be expressed as 1 or above ({prop.Name})", nameof(serializeType)); } if (bitRangeAttr.HighBit < bitRangeAttr.LowBit) { throw new ArgumentException($"HighBit must be higher than LowBit ({prop.Name})", nameof(serializeType)); } for (int i = bitRangeAttr.LowBit; i <= bitRangeAttr.HighBit; i++) { if (bitsUsed.Contains(i)) { throw new ArgumentException($"Overlapping bit value in range ({prop.Name}): {i}", nameof(serializeType)); } bitsUsed.Add(i); } maxBit = Math.Max(maxBit, bitRangeAttr.HighBit); minBit = Math.Min(minBit, bitRangeAttr.LowBit); } else { int bitValue = bitRangeAttr.LowBit; if (bitValue <= 0) { throw new ArgumentException($"Bits must be expressed as 1 or above ({prop.Name})", nameof(serializeType)); } if (bitsUsed.Contains(bitValue)) { throw new ArgumentException($"Overlapping bit value in range ({prop.Name}): {bitValue}", nameof(serializeType)); } bitsUsed.Add(bitValue); maxBit = Math.Max(maxBit, bitValue); minBit = Math.Min(minBit, bitValue); } } ValidateFullRange(serializeType, bitsUsed, minBit, maxBit); }
/// <summary> /// Serializes an object into a PackedBitWriter stream. /// </summary> /// <param name="writer">The stream to serialize to.</param> /// <param name="value">The object to serialize.</param> /// <exception cref="ArgumentNullException"><paramref name="writer" /> is null.</exception> /// <exception cref="ArgumentNullException"><paramref name="value" /> is null.</exception> public void Serialize(PackedBitWriter writer, object value) { if (object.ReferenceEquals(writer, null)) { throw new ArgumentNullException(nameof(writer)); } if (object.ReferenceEquals(value, null)) { throw new ArgumentNullException(nameof(value)); } if (ImplementsInterface) { ((IPackedBitSerializable)value).Serialize(writer); } else { var properties = GetProperties(SerializeType); if (HasOrder) { properties = properties.OrderBy(p => (p.GetCustomAttribute <PackedBitOrderAttribute>() ?? new PackedBitOrderAttribute(0)).LowBit); WriteImperativeBits(writer, value, properties, (prop) => { PackedBitOrderAttribute bitOrderAttr = prop.GetCustomAttribute <PackedBitOrderAttribute>(); return(new BitPair() { Count = bitOrderAttr.BitCount, Signed = bitOrderAttr.Signed, }); }); } else if (HasRange) { properties = properties.OrderBy(p => (p.GetCustomAttribute <PackedBitRangeAttribute>() ?? new PackedBitRangeAttribute(0)).LowBit); WriteImperativeBits(writer, value, properties, (prop) => { PackedBitRangeAttribute bitRangeAttr = prop.GetCustomAttribute <PackedBitRangeAttribute>(); return(new BitPair() { Count = bitRangeAttr.HasHighBit ? bitRangeAttr.HighBit - (bitRangeAttr.LowBit - 1) : 1, Signed = bitRangeAttr.Signed, }); }); } else if (HasSize) { WriteImperativeBits(writer, value, properties, (prop) => { PackedBitSizeAttribute bitSizeAttr = prop.GetCustomAttribute <PackedBitSizeAttribute>(); return(new BitPair() { Count = bitSizeAttr.BitCount, Signed = bitSizeAttr.Signed, }); }); } else { throw CodePath.Unreachable; } } }
/// <summary> /// Deserializes a PackedBitReader stream to an object. /// </summary> /// <param name="reader">The stream to deserialize.</param> /// <returns>An object populated by the stream.</returns> /// <exception cref="ArgumentNullException"><paramref name="reader" /> is null.</exception> public object Deserialize(PackedBitReader reader) { if (object.ReferenceEquals(reader, null)) { throw new ArgumentNullException(nameof(reader)); } object ret = Activator.CreateInstance(SerializeType); if (ImplementsInterface) { ((IPackedBitSerializable)ret).Deserialize(reader); } else { var properties = GetProperties(SerializeType); if (HasOrder) { properties = properties.OrderBy(p => p.GetCustomAttribute <PackedBitOrderAttribute>().LowBit); ReadImperativeBits(reader, ret, properties, (prop) => { PackedBitOrderAttribute bitOrderAttr = prop.GetCustomAttribute <PackedBitOrderAttribute>(); return(new BitPair() { Count = bitOrderAttr.BitCount, Signed = bitOrderAttr.Signed, }); }); } else if (HasRange) { properties = properties.OrderBy(p => p.GetCustomAttribute <PackedBitRangeAttribute>().LowBit); ReadImperativeBits(reader, ret, properties, (prop) => { PackedBitRangeAttribute bitRangeAttr = prop.GetCustomAttribute <PackedBitRangeAttribute>(); return(new BitPair() { Count = bitRangeAttr.HasHighBit ? bitRangeAttr.HighBit - (bitRangeAttr.LowBit - 1) : 1, Signed = bitRangeAttr.Signed, }); }); } else if (HasSize) { ReadImperativeBits(reader, ret, properties, (prop) => { PackedBitSizeAttribute bitSizeAttr = prop.GetCustomAttribute <PackedBitSizeAttribute>(); return(new BitPair() { Count = bitSizeAttr.BitCount, Signed = bitSizeAttr.Signed, }); }); } else { throw CodePath.Unreachable; } } return(ret); }