提交 92c4fc74 编写于 作者: H Heejae Chang 提交者: GitHub

Merge pull request #12890 from heejaechang/objectreader

addressed PR feedbacks on object writer/reader
......@@ -15,6 +15,7 @@ public class BitArrayTests
private const int seed = 128129347;
private const int rounds = 200;
private const int maxBits = 132;
[Fact]
public void UpperBitsUnset()
{
......@@ -149,6 +150,65 @@ public void CheckTrueBits()
}
}
[Fact]
public void CheckWords()
{
for (int capacity = 0; capacity < maxBits; capacity++)
{
BitVector b = BitVector.Create(capacity);
for (int i = 0; i < capacity; i++)
{
b[i] = false;
}
var required = BitVector.WordsRequired(capacity);
var count = BitVector.AllSet(capacity).Words().Count();
}
}
[Fact]
public void CheckIsTrue()
{
var r1 = new Random(seed);
for (int capacity = 0; capacity < maxBits; capacity++)
{
BitVector b = BitVector.Create(capacity);
for (int i = 0; i < capacity; i++)
{
b[i] = r1.NextBool();
}
var index = 0;
foreach (var word in b.Words())
{
for (var i = 0; i < BitVector.BitsPerWord; i++)
{
if (index >= capacity)
{
break;
}
Assert.Equal(b[index], BitVector.IsTrue(word, index));
index++;
}
}
}
}
[Fact]
public void CheckWordsRequired()
{
for (int capacity = 0; capacity < maxBits; capacity++)
{
var required = BitVector.WordsRequired(capacity);
var count = BitVector.AllSet(capacity).Words().Count();
Assert.Equal(count, required);
}
}
private void CheckTrueBitsCore(int capacity, Random r1, Random r2)
{
BitVector b = BitVector.Create(capacity);
......
......@@ -71,6 +71,52 @@ public void TestRoundTripPrimitiveArray()
reader.Dispose();
}
[Fact]
public void TestRoundTripBooleanArray()
{
for (var i = 0; i < 1000; i++)
{
var inputBool = new bool[i];
for (var j = 0; j < i; j++)
{
inputBool[j] = j % 2 == 0;
}
var stream = new MemoryStream();
var writer = new ObjectWriter(stream);
writer.WriteValue(inputBool);
writer.Dispose();
stream.Position = 0;
var reader = new ObjectReader(stream);
Assert.True(Enumerable.SequenceEqual(inputBool, (bool[])reader.ReadValue()));
reader.Dispose();
}
}
[Fact]
public void TestRoundTripFalseBooleanArray()
{
var inputBool = Enumerable.Repeat<bool>(false, 1000).ToArray();
var stream = new MemoryStream();
var writer = new ObjectWriter(stream);
writer.WriteValue(inputBool);
writer.Dispose();
stream.Position = 0;
var reader = new ObjectReader(stream);
Assert.True(Enumerable.SequenceEqual(inputBool, (bool[])reader.ReadValue()));
reader.Dispose();
}
[Fact]
public void TestRoundTripPrimitives()
{
......
......@@ -10,16 +10,17 @@ namespace Microsoft.CodeAnalysis
{
internal struct BitVector : IEquatable<BitVector>
{
private const Word ZeroWord = 0;
private const int Log2BitsPerWord = 5;
public const int BitsPerWord = 1 << Log2BitsPerWord;
// Cannot expose the following two field publicly because this structure is mutable
// and might become not null/empty, unless we restrict access to it.
private static readonly Word[] s_emptyArray = SpecializedCollections.EmptyArray<Word>();
private static readonly BitVector s_nullValue = new BitVector(0, null, 0);
private static readonly BitVector s_emptyValue = new BitVector(0, s_emptyArray, 0);
private const int Log2BitsPerWord = 5;
internal const int BitsPerWord = 1 << Log2BitsPerWord;
private const Word ZeroWord = 0;
private Word _bits0;
private Word[] _bits;
private int _capacity;
......@@ -95,14 +96,14 @@ public void EnsureCapacity(int newCapacity)
Check();
}
internal IEnumerable<Word> Words()
public IEnumerable<Word> Words()
{
if (_bits0 != 0)
if (_capacity > 0)
{
yield return _bits0;
}
for (int i = 0; i < _bits.Length; i++)
for (int i = 0; i < _bits?.Length; i++)
{
yield return _bits[i];
}
......@@ -159,6 +160,11 @@ public static BitVector Create(int capacity)
/// <returns></returns>
public static BitVector AllSet(int capacity)
{
if (capacity == 0)
{
return Empty;
}
int requiredWords = WordsForCapacity(capacity);
Word[] bits = (requiredWords == 0) ? s_emptyArray : new Word[requiredWords];
int lastWord = requiredWords - 1;
......@@ -298,10 +304,9 @@ public void UnionWith(BitVector other)
if (index >= _capacity)
return false;
int i = (index >> Log2BitsPerWord) - 1;
int b = index & (BitsPerWord - 1);
Word mask = ((Word)1) << b;
var word = (i < 0) ? _bits0 : _bits[i];
return (word & mask) != 0;
return IsTrue(word, index);
}
set
......@@ -333,5 +338,18 @@ public void Clear()
_bits0 = 0;
if (_bits != null) Array.Clear(_bits, 0, _bits.Length);
}
public static bool IsTrue(Word word, int index)
{
int b = index & (BitsPerWord - 1);
Word mask = ((Word)1) << b;
return (word & mask) != 0;
}
public static int WordsRequired(int capacity)
{
if (capacity <= 0) return 0;
return WordsForCapacity(capacity) + 1;
}
}
}
......@@ -371,70 +371,93 @@ private Array ReadPrimitiveTypeArrayElements(Type type, DataKind kind, int lengt
return _reader.ReadChars(length);
}
var array = Array.CreateInstance(type, length);
// optimizations for string where object reader/writer has its own mechanism to
// reduce duplicated strings
if (type == typeof(string))
{
for (int i = 0; i < length; i++)
{
var value = this.ReadString();
array.SetValue(value, i);
}
return ReadPrimitiveTypeArrayElements(length, ReadString);
}
return array;
if (type == typeof(bool))
{
return ReadBooleanArray(length);
}
// otherwise, read elements directly from underlying binary writer
for (int i = 0; i < length; i++)
switch (kind)
{
case DataKind.Int8:
return ReadPrimitiveTypeArrayElements(length, _reader.ReadSByte);
case DataKind.Int16:
return ReadPrimitiveTypeArrayElements(length, _reader.ReadInt16);
case DataKind.Int32:
return ReadPrimitiveTypeArrayElements(length, _reader.ReadInt32);
case DataKind.Int64:
return ReadPrimitiveTypeArrayElements(length, _reader.ReadInt64);
case DataKind.UInt16:
return ReadPrimitiveTypeArrayElements(length, _reader.ReadUInt16);
case DataKind.UInt32:
return ReadPrimitiveTypeArrayElements(length, _reader.ReadUInt32);
case DataKind.UInt64:
return ReadPrimitiveTypeArrayElements(length, _reader.ReadUInt64);
case DataKind.Float4:
return ReadPrimitiveTypeArrayElements(length, _reader.ReadSingle);
case DataKind.Float8:
return ReadPrimitiveTypeArrayElements(length, _reader.ReadDouble);
case DataKind.Decimal:
return ReadPrimitiveTypeArrayElements(length, _reader.ReadDecimal);
default:
throw ExceptionUtilities.UnexpectedValue(kind);
}
}
private Array ReadBooleanArray(int length)
{
if (length == 0)
{
switch (kind)
// simple check
return SpecializedCollections.EmptyArray<bool>();
}
var array = new bool[length];
var wordLength = BitVector.WordsRequired(length);
var count = 0;
for (var i = 0; i < wordLength; i++)
{
var word = _reader.ReadUInt32();
for (var p = 0; p < BitVector.BitsPerWord; p++)
{
case DataKind.Int8:
array.SetValue(_reader.ReadSByte(), i);
continue;
case DataKind.Int16:
array.SetValue(_reader.ReadInt16(), i);
continue;
case DataKind.Int32:
array.SetValue(_reader.ReadInt32(), i);
continue;
case DataKind.Int64:
array.SetValue(_reader.ReadInt64(), i);
continue;
case DataKind.UInt8:
array.SetValue(_reader.ReadByte(), i);
continue;
case DataKind.UInt16:
array.SetValue(_reader.ReadUInt16(), i);
continue;
case DataKind.UInt32:
array.SetValue(_reader.ReadUInt32(), i);
continue;
case DataKind.UInt64:
array.SetValue(_reader.ReadUInt64(), i);
continue;
case DataKind.Float4:
array.SetValue(_reader.ReadSingle(), i);
continue;
case DataKind.Float8:
array.SetValue(_reader.ReadDouble(), i);
continue;
case DataKind.Decimal:
array.SetValue(_reader.ReadDecimal(), i);
continue;
case DataKind.BooleanType:
array.SetValue(_reader.ReadBoolean(), i);
continue;
default:
throw ExceptionUtilities.UnexpectedValue(kind);
if (count >= length)
{
return array;
}
array[count++] = BitVector.IsTrue(word, p);
}
}
return array;
}
private T[] ReadPrimitiveTypeArrayElements<T>(int length, Func<T> read)
{
if (length == 0)
{
// quick check
return SpecializedCollections.EmptyArray<T>();
}
var array = new T[length];
for (var i = 0; i < array.Length; i++)
{
array[i] = read();
}
return array;
}
private Type ReadType()
{
var kind = (DataKind)_reader.ReadByte();
......
......@@ -514,55 +514,76 @@ private void WritePrimitiveTypeArrayElements(Type type, DataKind kind, Array ins
// its own optimization to reduce repeated string
if (type == typeof(string))
{
foreach (var value in instance)
{
this.WriteString((string)value);
}
WritePrimitiveTypeArrayElements((string[])instance, WriteString);
return;
}
// optimization for bool array
if (type == typeof(bool))
{
WriteBooleanArray((bool[])instance);
return;
}
// otherwise, write elements directly to underlying binary writer
foreach (var value in instance)
switch (kind)
{
switch (kind)
{
case DataKind.Int8:
_writer.Write((sbyte)value);
break;
case DataKind.Int16:
_writer.Write((short)value);
break;
case DataKind.Int32:
_writer.Write((int)value);
break;
case DataKind.Int64:
_writer.Write((long)value);
break;
case DataKind.UInt16:
_writer.Write((ushort)value);
break;
case DataKind.UInt32:
_writer.Write((uint)value);
break;
case DataKind.UInt64:
_writer.Write((ulong)value);
break;
case DataKind.Float4:
_writer.Write((float)value);
break;
case DataKind.Float8:
_writer.Write((double)value);
break;
case DataKind.Decimal:
_writer.Write((decimal)value);
break;
case DataKind.BooleanType:
_writer.Write((bool)value);
break;
default:
throw ExceptionUtilities.UnexpectedValue(kind);
}
case DataKind.Int8:
WritePrimitiveTypeArrayElements((sbyte[])instance, _writer.Write);
return;
case DataKind.Int16:
WritePrimitiveTypeArrayElements((short[])instance, _writer.Write);
return;
case DataKind.Int32:
WritePrimitiveTypeArrayElements((int[])instance, _writer.Write);
return;
case DataKind.Int64:
WritePrimitiveTypeArrayElements((long[])instance, _writer.Write);
return;
case DataKind.UInt16:
WritePrimitiveTypeArrayElements((ushort[])instance, _writer.Write);
return;
case DataKind.UInt32:
WritePrimitiveTypeArrayElements((uint[])instance, _writer.Write);
return;
case DataKind.UInt64:
WritePrimitiveTypeArrayElements((ulong[])instance, _writer.Write);
return;
case DataKind.Float4:
WritePrimitiveTypeArrayElements((float[])instance, _writer.Write);
return;
case DataKind.Float8:
WritePrimitiveTypeArrayElements((double[])instance, _writer.Write);
return;
case DataKind.Decimal:
WritePrimitiveTypeArrayElements((decimal[])instance, _writer.Write);
return;
default:
throw ExceptionUtilities.UnexpectedValue(kind);
}
}
private void WriteBooleanArray(bool[] array)
{
// convert bool array to bit array
var bits = BitVector.Create(array.Length);
for (var i = 0; i < array.Length; i++)
{
bits[i] = array[i];
}
// send over bit array
foreach (var word in bits.Words())
{
_writer.Write(word);
}
}
private void WritePrimitiveTypeArrayElements<T>(T[] array, Action<T> write)
{
for (var i = 0; i < array.Length; i++)
{
write(array[i]);
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册