/// <summary> /// <seealso cref="Marshal.ProcessPackedRow"/> /// /// Opcodes supported: /// <seealso cref="Opcode.PackedRow"/> /// </summary> /// <param name="opcode">Type of object to parse</param> /// <returns>The decoded python type</returns> /// <exception cref="InvalidDataException">If any error was found in the data</exception> protected virtual PyDataType ProcessPackedRow(Opcode opcode) { if (opcode != Opcode.PackedRow) { throw new InvalidDataException($"Trying to parse a {opcode} as PackedRow"); } DBRowDescriptor descriptor = this.Process(false); Dictionary <string, PyDataType> data = new Dictionary <string, PyDataType> (); int wholeBytes = 0; int nullBits = 0; int boolBits = 0; List <DBRowDescriptor.Column> booleanColumns = new List <DBRowDescriptor.Column>(); foreach (DBRowDescriptor.Column column in descriptor.Columns) { int bitLength = Utils.GetTypeBits(column.Type); if (column.Type == FieldType.Bool) { booleanColumns.Add(column); boolBits++; } nullBits++; if (bitLength >= 8) { wholeBytes += bitLength >> 3; } } // sort columns by the bit size and calculate other statistics for the PackedRow IOrderedEnumerable <DBRowDescriptor.Column> enumerator = descriptor.Columns.OrderByDescending(c => Utils.GetTypeBits(c.Type)); MemoryStream decompressedStream = ZeroCompressionUtils.LoadZeroCompressed(this.mReader, wholeBytes + ((nullBits + boolBits) >> 3) + 1); BinaryReader decompressedReader = new BinaryReader(decompressedStream); byte[] fullBuffer = decompressedStream.GetBuffer(); foreach (DBRowDescriptor.Column column in enumerator) { int bit = (wholeBytes << 3) + descriptor.Columns.IndexOf(column) + boolBits; bool isNull = (fullBuffer[bit >> 3] & (1 << (bit & 0x7))) == (1 << (bit & 0x7)); switch (column.Type) { case FieldType.I8: case FieldType.UI8: case FieldType.CY: case FieldType.FileTime: data[column.Name] = new PyInteger(decompressedReader.ReadInt64()); break; case FieldType.I4: case FieldType.UI4: data[column.Name] = new PyInteger(decompressedReader.ReadInt32()); break; case FieldType.I2: case FieldType.UI2: data[column.Name] = new PyInteger(decompressedReader.ReadInt16()); break; case FieldType.I1: case FieldType.UI1: data[column.Name] = new PyInteger(decompressedReader.ReadByte()); break; case FieldType.R8: data[column.Name] = new PyDecimal(decompressedReader.ReadDouble()); break; case FieldType.R4: data[column.Name] = new PyDecimal(decompressedReader.ReadSingle()); break; case FieldType.Bool: { int boolBit = (wholeBytes << 3) + booleanColumns.IndexOf(column); bool isTrue = (fullBuffer[boolBit >> 3] & (1 << (boolBit & 0x7))) == (1 << (boolBit & 0x7)); data[column.Name] = new PyBool(isTrue); } break; case FieldType.Bytes: case FieldType.WStr: case FieldType.Str: data[column.Name] = this.Process(false); break; default: throw new InvalidDataException($"Unknown column type {column.Type}"); } if (isNull == true) { data[column.Name] = null; } } return(new PyPackedRow(descriptor, data)); }
/// <summary> /// <seealso cref="Marshal.ProcessPackedRow"/> /// /// Opcodes supported: /// <seealso cref="Opcode.PackedRow"/> /// </summary> /// <param name="opcode">Type of object to parse</param> /// <returns>The decoded python type</returns> /// <exception cref="InvalidDataException">If any error was found in the data</exception> protected virtual PyDataType ProcessPackedRow(Opcode opcode) { if (opcode != Opcode.PackedRow) { throw new InvalidDataException($"Trying to parse a {opcode} as PackedRow"); } DBRowDescriptor descriptor = this.Process(false); Dictionary <string, PyDataType> data = new Dictionary <string, PyDataType> (); MemoryStream decompressedStream = ZeroCompressionUtils.LoadZeroCompressed(this.mReader); BinaryReader decompressedReader = new BinaryReader(decompressedStream); // sort columns by the bit size IEnumerable <DBRowDescriptor.Column> enumerator = descriptor.Columns.OrderByDescending(c => Utils.GetTypeBits(c.Type)); int bitOffset = 8; byte buffer = 0; foreach (DBRowDescriptor.Column column in enumerator) { switch (column.Type) { case FieldType.I8: case FieldType.UI8: case FieldType.CY: case FieldType.FileTime: data[column.Name] = new PyInteger(decompressedReader.ReadInt64()); break; case FieldType.I4: case FieldType.UI4: data[column.Name] = new PyInteger(decompressedReader.ReadInt32()); break; case FieldType.I2: case FieldType.UI2: data[column.Name] = new PyInteger(decompressedReader.ReadInt16()); break; case FieldType.I1: case FieldType.UI1: data[column.Name] = new PyInteger(decompressedReader.ReadByte()); break; case FieldType.R8: data[column.Name] = new PyDecimal(decompressedReader.ReadDouble()); break; case FieldType.R4: data[column.Name] = new PyDecimal(decompressedReader.ReadSingle()); break; case FieldType.Bool: // read a byte from the buffer if needed if (bitOffset == 8) { buffer = decompressedReader.ReadByte(); bitOffset = 0; } data[column.Name] = new PyBool(((buffer >> bitOffset++) & 0x01) == 0x01); break; case FieldType.Bytes: case FieldType.WStr: case FieldType.Str: data[column.Name] = this.Process(false); break; default: throw new InvalidDataException($"Unknown column type {column.Type}"); } } return(new PyPackedRow(descriptor, data)); }