src/Proton.TestPeer/Codec/Impl/ArrayElement.cs (459 lines of code) (raw):

/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.IO; using Apache.Qpid.Proton.Test.Driver.Codec.Primitives; namespace Apache.Qpid.Proton.Test.Driver.Codec.Impl { public class ArrayElement : AbstractElement { private IElement first; private DataType arrayType; private bool described; private ConstructorType constructorType; internal ArrayElement(IElement parent, IElement prev, bool described, DataType type) : base(parent, prev) { if (type == DataType.Described) { throw new ArgumentException("Array type cannot be DESCRIBED"); } IsDescribed = described; ArrayType = type; ConstructorType = ArrayType switch { DataType.UInt or DataType.ULong or DataType.List => ConstructorType.Tiny, _ => ConstructorType.Small, }; } internal ConstructorType ConstructorType { get => constructorType; set => constructorType = value; } public bool IsDescribed { get => described; init => described = value; } public override IElement Child { get => first; set => first = value; } public override bool CanEnter => true; public override uint GetSize() { return ComputeSize(); } public override object Value => ExtrapolateValue(); public Array ArrayValue => ExtrapolateValue(); public override DataType DataType => DataType.Array; public DataType ArrayType { get => arrayType; init => arrayType = value; } public override IElement AddChild(IElement element) { if (IsDescribed || element.DataType == ArrayType) { first = element; return element; } else { IElement replacement = Coerce(element); if (replacement != null) { first = replacement; return replacement; } throw new ArgumentException("Attempting to add instance of " + element.DataType + " to array of " + ArrayType); } } public override IElement CheckChild(IElement element) { if (element.DataType != ArrayType) { IElement replacement = Coerce(element); if (replacement != null) { return replacement; } throw new ArgumentException("Attempting to add instance of " + element.DataType + " to array of " + ArrayType); } return element; } private IElement Coerce(IElement element) { switch (arrayType) { case DataType.Int: int i; switch (element.DataType) { case DataType.Byte: i = ((ByteElement)element).SByteValue; break; case DataType.Short: i = ((ShortElement)element).ShortValue; break; case DataType.Long: i = (int)((LongElement)element).LongValue; break; default: return null; } return new IntegerElement(element.Parent, element.Prev, i); case DataType.Long: long l; switch (element.DataType) { case DataType.Byte: l = ((ByteElement)element).SByteValue; break; case DataType.Short: l = ((ShortElement)element).ShortValue; break; case DataType.Int: l = ((IntegerElement)element).IntegerValue; break; default: return null; } return new LongElement(element.Parent, element.Prev, l); case DataType.Array: break; case DataType.Binary: break; case DataType.Bool: break; case DataType.Byte: break; case DataType.Char: break; case DataType.Decimal32: break; case DataType.Decimal64: break; case DataType.Decimal128: break; case DataType.Described: break; case DataType.Double: break; case DataType.Float: break; case DataType.List: break; case DataType.Map: break; case DataType.Null: break; case DataType.Short: break; case DataType.String: break; case DataType.Symbol: break; case DataType.Timestamp: break; case DataType.UByte: break; case DataType.UInt: break; case DataType.ULong: break; case DataType.UShort: break; case DataType.Uuid: break; default: break; } return null; } public override uint Encode(Stream stream) { uint size = GetSize(); uint count = Count; if (stream.IsWritable()) { if (!IsElementOfArray()) { if (size > 257 || count > 255) { stream.WriteByte(0xf0); stream.WriteUnsignedInt(size - 5); stream.WriteUnsignedInt(count); } else { stream.WriteByte(0xe0); stream.WriteByte((byte)(size - 2)); stream.WriteByte((byte)count); } } else { ArrayElement parent = (ArrayElement)Parent; if (parent.ConstructorType == ConstructorType.Tiny) { stream.WriteByte((byte)(size - 1)); stream.WriteByte((byte)count); } else { stream.WriteUnsignedInt(size - 4); stream.WriteUnsignedInt(count); } } IElement element = first; if (IsDescribed) { stream.WriteByte(0); if (element == null) { stream.WriteByte(0x40); } else { element.Encode(stream); element = element.Next; } } switch (arrayType) { case DataType.Null: stream.WriteByte(0x40); break; case DataType.Bool: stream.WriteByte(0x56); break; case DataType.UByte: stream.WriteByte(0x50); break; case DataType.Byte: stream.WriteByte(0x51); break; case DataType.UShort: stream.WriteByte(0x60); break; case DataType.Short: stream.WriteByte(0x61); break; case DataType.UInt: switch (ConstructorType) { case ConstructorType.Tiny: stream.WriteByte(0x43); break; case ConstructorType.Small: stream.WriteByte(0x52); break; case ConstructorType.Large: stream.WriteByte(0x70); break; } break; case DataType.Int: stream.WriteByte(ConstructorType == ConstructorType.Small ? (byte)0x54 : (byte)0x71); break; case DataType.Char: stream.WriteByte(0x73); break; case DataType.ULong: switch (ConstructorType) { case ConstructorType.Tiny: stream.WriteByte(0x44); break; case ConstructorType.Small: stream.WriteByte(0x53); break; case ConstructorType.Large: stream.WriteByte(0x80); break; } break; case DataType.Long: stream.WriteByte(ConstructorType == ConstructorType.Small ? (byte)0x55 : (byte)0x81); break; case DataType.Timestamp: stream.WriteByte(0x83); break; case DataType.Float: stream.WriteByte(0x72); break; case DataType.Double: stream.WriteByte(0x82); break; case DataType.Decimal32: stream.WriteByte(0x74); break; case DataType.Decimal64: stream.WriteByte(0x84); break; case DataType.Decimal128: stream.WriteByte(0x94); break; case DataType.Uuid: stream.WriteByte(0x98); break; case DataType.Binary: stream.WriteByte(ConstructorType == ConstructorType.Small ? (byte)0xa0 : (byte)0xb0); break; case DataType.String: stream.WriteByte(ConstructorType == ConstructorType.Small ? (byte)0xa1 : (byte)0xb1); break; case DataType.Symbol: stream.WriteByte(ConstructorType == ConstructorType.Small ? (byte)0xa3 : (byte)0xb3); break; case DataType.Array: stream.WriteByte(ConstructorType == ConstructorType.Small ? (byte)0xe0 : (byte)0xf0); break; case DataType.List: stream.WriteByte(ConstructorType == ConstructorType.Tiny ? (byte)0x45 : ConstructorType == ConstructorType.Small ? (byte)0xc0 : (byte)0xd0); break; case DataType.Map: stream.WriteByte(ConstructorType == ConstructorType.Small ? (byte)0xc1 : (byte)0xd1); break; case DataType.Described: break; default: break; } while (element != null) { element.Encode(stream); element = element.Next; } return size; } else { return 0; } } internal override string StartSymbol() { return string.Format("{0}{1}[", IsDescribed ? "D" : "", ArrayType); } internal override string StopSymbol() { return "]"; } public uint Count { get { uint count = 0; IElement elt = first; while (elt != null) { count++; elt = elt.Next; } if (IsDescribed && count != 0) { count--; } return count; } } private uint ComputeSize() { ConstructorType oldConstructorType; uint bodySize; uint count = 0; do { bodySize = 1; // data type constructor oldConstructorType = ConstructorType; IElement element = first; while (element != null) { count++; bodySize += element.GetSize(); element = element.Next; } } while (oldConstructorType != ConstructorType); if (IsDescribed) { bodySize++; // 00 instruction if (count != 0) { count--; } } if (IsElementOfArray()) { ArrayElement parent = (ArrayElement)Parent; if (parent.ConstructorType == ConstructorType.Small) { if (count <= 255 && bodySize <= 254) { bodySize += 2; } else { parent.ConstructorType = ConstructorType.Large; bodySize += 8; } } else { bodySize += 8; } } else { if (count <= 255 && bodySize <= 254) { bodySize += 3; } else { bodySize += 9; } } return bodySize; } private object[] ExtrapolateValue() { if (IsDescribed) { IDescribedType[] rVal = new IDescribedType[Count]; object descriptor = first?.Value; IElement element = first?.Next; int i = 0; while (element != null) { rVal[i++] = new DescribedType(descriptor, element.Value); element = element.Next; } return rVal; } else if (ArrayType == DataType.Symbol) { Symbol[] rVal = new Symbol[Count]; SymbolElement element = (SymbolElement)first; int i = 0; while (element != null) { rVal[i++] = element.SymbolValue; element = (SymbolElement)element.Next; } return rVal; } else { object[] rVal = new object[Count]; IElement element = first; int i = 0; while (element != null) { rVal[i++] = element.Value; element = element.Next; } return rVal; } } } }