Notes:
- supports 64-bit platforms only
ulong
used where negative values make no sense- library functions are assumed to be correct (whether they actually do what I want or not is a subject for another post)
- the thread-safe implementation will ideally be refactored so that it ends up in another class but this way makes it easier for me to ensure correctness during development
This is my first real adventure in writing a thread-safe class. With the above notes taken into account, did I screw anything up in a way that might violate my thread-safe claim or am I performing any unncessary operations?
Code:
public sealed class BitVector : IEnumerable<bool>
{
private readonly long[] m_bits;
private readonly bool m_isThreadSafe;
private readonly ulong m_length;
private long m_version = 0L;
public bool this[long index] {
get {
return this[checked((ulong)index)];
}
set {
this[checked((ulong)index)] = value;
}
}
[CLSCompliant(false)]
public bool this[ulong index] {
get {
if (m_length > index) {
if (m_isThreadSafe) {
return BTConcurrent.AtomicRead(ref m_bits[(index / 64UL)]).IsBitSet(checked((int)index.Mod64()));
}
else {
return m_bits[(index / 64UL)].IsBitSet(checked((int)index.Mod64()));
}
}
else {
throw new ArgumentOutOfRangeException(paramName: nameof(index));
}
}
set {
if (m_length > index) {
if (value) {
if (m_isThreadSafe) {
unchecked { BTConcurrent.AtomicIncrement(ref m_version); }
BTConcurrent.AtomicSetBit(ref m_bits[unchecked(index / 64UL)], checked((int)index.Mod64()));
}
else {
m_bits[unchecked(index / 64UL)].SetBit(checked((int)index.Mod64()));
}
}
else {
if (m_isThreadSafe) {
unchecked { BTConcurrent.AtomicIncrement(ref m_version); }
BTConcurrent.AtomicClearBit(ref m_bits[unchecked(index / 64UL)], checked((int)index.Mod64()));
}
else {
m_bits[unchecked(index / 64UL)].ClearBit(checked((int)index.Mod64()));
}
}
}
else {
throw new ArgumentOutOfRangeException(paramName: nameof(index));
}
}
}
public long Length {
get {
return checked((long)UnsignedLength);
}
}
[CLSCompliant(false)]
public ulong UnsignedLength {
get {
return m_length;
}
}
[CLSCompliant(false)]
public BitVector(ulong length, long initBitPattern, bool isThreadSafe) {
m_bits = new long[((length / 64UL) + Convert.ToUInt64(length.Mod64().IsPositive()))];
m_isThreadSafe = isThreadSafe;
m_length = length;
if (initBitPattern != 0L) {
SetAll(initBitPattern);
}
}
[CLSCompliant(false)]
public BitVector(ulong length, bool isThreadSafe) : this(length, 0L, isThreadSafe) { }
[CLSCompliant(false)]
public BitVector(ulong length) : this(length, false) { }
[CLSCompliant(false)]
public void And(long bitPattern, ulong count) {
And(this, bitPattern, count);
}
public void AndAll(long bitPattern) {
And(bitPattern, unchecked((ulong)m_bits.LongLength));
}
[CLSCompliant(false)]
public void Clear(ulong count) {
SetAll(0L, count);
}
public void ClearAll() {
SetAll(0L, unchecked((ulong)m_bits.LongLength));
}
public IEnumerator<bool> GetEnumerator() {
if (m_isThreadSafe) {
var versionSnapshot = BTConcurrent.AtomicRead(ref m_version);
for (var i = 0UL; i < UnsignedLength; i++) {
if (versionSnapshot == BTConcurrent.AtomicRead(ref m_version)) {
yield return this[i];
}
else {
throw new InvalidOperationException(message: "data was mutated during enumeration, unable to continue");
}
}
}
else {
for (var i = 0UL; i < UnsignedLength; i++) {
yield return this[i];
}
}
}
[CLSCompliant(false)]
public void Or(long bitPattern, ulong count) {
Or(this, bitPattern, count);
}
public void OrAll(long bitPattern) {
Or(bitPattern, unchecked((ulong)m_bits.LongLength));
}
[CLSCompliant(false)]
public void Not(long bitPattern, ulong count) {
Not(this, bitPattern, count);
}
public void NotAll(long bitPattern) {
Not(bitPattern, unchecked((ulong)m_bits.LongLength));
}
[CLSCompliant(false)]
public void SetAll(long bitPattern, ulong count) {
Set(this, bitPattern, count);
}
public void SetAll(long bitPattern) {
SetAll(bitPattern, ((ulong)m_bits.LongLength));
}
public void SetAll() {
SetAll(-1L);
}
public void SetEven() {
OrAll(6148914691236517205L /* equivalent to 1010....1010 */);
}
public void SetOdd() {
OrAll(-6148914691236517206L /* equivalent to 0101....0101 */);
}
[CLSCompliant(false)]
public void Xor(long bitPattern, ulong count) {
Xor(this, bitPattern, count);
}
public void XorAll(long bitPattern) {
Xor(bitPattern, ((ulong)m_bits.LongLength));
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
return GetEnumerator();
}
private static void And(BitVector bitVector, long bitPattern, ulong count) {
if (count > unchecked((ulong)bitVector.m_bits.Length)) {
throw new ArgumentOutOfRangeException(paramName: nameof(count));
}
if (bitVector.m_isThreadSafe) {
unchecked { BTConcurrent.AtomicIncrement(ref bitVector.m_version); }
for (var i = 0UL; i < count; i++) {
BTConcurrent.AtomicAndAssign(ref bitVector.m_bits[i], bitPattern);
}
}
else {
for (var i = 0UL; i < count; i++) {
bitVector.m_bits[i] &= bitPattern;
}
}
}
private static void Not(BitVector bitVector, long bitPattern, ulong count) {
if (count > unchecked((ulong)bitVector.m_bits.Length)) {
throw new ArgumentOutOfRangeException(paramName: nameof(count));
}
if (bitVector.m_isThreadSafe) {
unchecked { BTConcurrent.AtomicIncrement(ref bitVector.m_version); }
for (var i = 0UL; i < count; i++) {
BTConcurrent.AtomicNotAssign(ref bitVector.m_bits[i]);
}
}
else {
for (var i = 0UL; i < count; i++) {
bitVector.m_bits[i] = (~bitPattern);
}
}
}
private static void Or(BitVector bitVector, long bitPattern, ulong count) {
if (count > unchecked((ulong)bitVector.m_bits.Length)) {
throw new ArgumentOutOfRangeException(paramName: nameof(count));
}
if (bitVector.m_isThreadSafe) {
unchecked { BTConcurrent.AtomicIncrement(ref bitVector.m_version); }
for (var i = 0UL; i < count; i++) {
BTConcurrent.AtomicOrAssign(ref bitVector.m_bits[i], bitPattern);
}
}
else {
for (var i = 0UL; i < count; i++) {
bitVector.m_bits[i] |= bitPattern;
}
}
}
private static void Set(BitVector bitVector, long bitPattern, ulong count) {
if (count > unchecked((ulong)bitVector.m_bits.Length)) {
throw new ArgumentOutOfRangeException(paramName: nameof(count));
}
if (bitVector.m_isThreadSafe) {
unchecked { BTConcurrent.AtomicIncrement(ref bitVector.m_version); }
for (var i = 0UL; i < count; i++) {
BTConcurrent.AtomicWrite(ref bitVector.m_bits[i], bitPattern);
}
}
else {
for (var i = 0UL; i < count; i++) {
bitVector.m_bits[i] = bitPattern;
}
}
}
private static void Xor(BitVector bitVector, long bitPattern, ulong count) {
if (count > unchecked((ulong)bitVector.m_bits.Length)) {
throw new ArgumentOutOfRangeException(paramName: nameof(count));
}
if (bitVector.m_isThreadSafe) {
unchecked { BTConcurrent.AtomicIncrement(ref bitVector.m_version); }
for (var i = 0UL; i < count; i++) {
BTConcurrent.AtomicXorAssign(ref bitVector.m_bits[i], bitPattern);
}
}
else {
for (var i = 0UL; i < count; i++) {
bitVector.m_bits[i] ^= bitPattern;
}
}
}
}
BTConcurrent
class also? \$\endgroup\$