解剖SQLSERVER 第五篇 OrcaMDF里读取Bits类型数据(译)
时间:2022-03-14 04:42
原文:
我们再添加一些列
CREATE TABLE BitTest ( A bit B bit C bit D int E bit F bit G bit H smallint I bit J bit K bit )
E到G列按道理来说应该存储在D列的后面,但是他们会继续使用第一个 bit byte,直到第一个 bit byte使用完所有的位空间为止
下面的图显示了H列(smallint )直接存储在D列的后面,而在D列后面是存储K列的新bit byte,因为第一个bit byte已经满了
当读取行记录里的位类型时我们需要知道的状态
很明显,我们一次不能只读取一个字段的值,我们读取固定长度数据类型的时候还需要读取定长数据偏移指针
我们需要一些能在读取的时候指示我们当前读取到字节中哪一个位属于哪一个字段的状态,然后我们读取一个新的bit byte
我来介绍一下RecordReadState类
public class RecordReadState { // We start out having consumed all bits as none have been read private int currentBitIndex = 8; private byte bits; public void LoadBitByte(byte bits) { this.bits = bits; currentBitIndex = 0; } public bool AllBitsConsumed { get { return currentBitIndex == 8; } } public bool GetNextBit() { return (bits & (1 << currentBitIndex++)) != 0; } }
RecordReadState 类当前只需要处理bits,但是将来我可能还要创建一个BitReadState 类用来保存读取状态
RecordReadState 类保存了一个字节用来当作指针指出下一个可用的位在字节的哪个地方,如果字节已经用完了存储满了所有的位数据
(currentBixIndex = 8 (0-7 being the available bits)),方法AllBitsConsumed 就会返回true,指示我们需要读取一个新的 bit byte
GetNextBit方法只是简单的从 bit byte中读取当前的bit ,然后将currentBitIndex(bit index)的值加1
demo
using NUnit.Framework; using OrcaMDF.Core.Engine.Records; namespace OrcaMDF.Core.Tests.Engine.Records { [TestFixture] public class RecordReadStateTests { [Test] public void General() { var state = new RecordReadState(); // No bits available Assert.IsTrue(state.AllBitsConsumed); state.LoadBitByte(0xD2); // 11010010 // Bits available Assert.IsFalse(state.AllBitsConsumed); // Reading bit values Assert.IsFalse(state.GetNextBit()); Assert.IsTrue(state.GetNextBit()); Assert.IsFalse(state.GetNextBit()); Assert.IsFalse(state.GetNextBit()); Assert.IsTrue(state.GetNextBit()); Assert.IsFalse(state.GetNextBit()); Assert.IsTrue(state.GetNextBit()); // One bit left Assert.IsFalse(state.AllBitsConsumed); Assert.IsTrue(state.GetNextBit()); // Bits exhausted, ready for next byte Assert.IsTrue(state.AllBitsConsumed); } } }
SqlBit实现
一旦我们实现了状态的读取,我们就可以实现SqlBit 类型
public class SqlBit : ISqlType { private readonly RecordReadState readState; public SqlBit(RecordReadState readState) { this.readState = readState; } public bool IsVariableLength { get { return false; } } public short? FixedLength { get { if (readState.AllBitsConsumed) return 1; return 0; } } public object GetValue(byte[] value) { if(readState.AllBitsConsumed && value.Length != 1) throw new ArgumentException("All bits consumed, invalid value length: " + value.Length); if (value.Length == 1) readState.LoadBitByte(value[0]); return readState.GetNextBit(); } }
SqlBit 在构造函数里传入一个read state,read state指示当前记录读取操作的范围。需要注意的是固定长度需要依据read state里的当前AllBitsConsumed值
如果字节里面所有位都被占用,那么意味着需要读取整个字节,如果if (readState.AllBitsConsumed)返回0表示不需要读取整个字节,但是GetValue方法依然会被调用
GetValue方法会验证一种情况:readState.AllBitsConsumed 返回真,证明 bit byte是有数据存储在里面,但是value.Length返回的长度是0,那证明有问题了
如果我们读到一个值,我们会请求 read state 去装载一个新的bit byte ,之后,我们可以调用GetNextBit 方法返回 read state的当前bit
相关测试
using NUnit.Framework; using OrcaMDF.Core.Engine.Records; using OrcaMDF.Core.Engine.SqlTypes; namespace OrcaMDF.Core.Tests.Engine.SqlTypes { [TestFixture] public class SqlBitTests { [Test] public void GetValue() { var readState = new RecordReadState(); var type = new SqlBit(readState); // No bytes read - length is one Assert.AreEqual(1, type.FixedLength); // Load byte and check length is 0 readState.LoadBitByte(0xD2); Assert.AreEqual(0, type.FixedLength); Assert.IsFalse((bool)type.GetValue(new byte[0])); Assert.IsTrue((bool)type.GetValue(new byte[0])); Assert.IsFalse((bool)type.GetValue(new byte[0])); Assert.IsFalse((bool)type.GetValue(new byte[0])); Assert.IsTrue((bool)type.GetValue(new byte[0])); Assert.IsFalse((bool)type.GetValue(new byte[0])); Assert.IsTrue((bool)type.GetValue(new byte[0])); // One bit left - length should still be 0 Assert.AreEqual(0, type.FixedLength); Assert.IsTrue((bool)type.GetValue(new byte[0])); // All bits consumed - length should be 1 Assert.AreEqual(1, type.FixedLength); } } }
第五篇完