mirror of
https://github.com/Sonarr/Sonarr.git
synced 2024-12-16 11:37:58 +02:00
421 lines
12 KiB
C#
421 lines
12 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
|
|
namespace MonoTorrent
|
|
{
|
|
/// <summary>
|
|
/// This class is for represting the Peer's bitfield
|
|
/// </summary>
|
|
public class BitField : ICloneable, IEnumerable<bool>
|
|
{
|
|
#region Member Variables
|
|
|
|
private int[] array;
|
|
private int length;
|
|
private int trueCount;
|
|
|
|
internal bool AllFalse
|
|
{
|
|
get { return this.trueCount == 0; }
|
|
}
|
|
|
|
internal bool AllTrue
|
|
{
|
|
get { return this.trueCount == this.length; }
|
|
}
|
|
|
|
public int Length
|
|
{
|
|
get { return this.length; }
|
|
}
|
|
|
|
public double PercentComplete
|
|
{
|
|
get { return (double)this.trueCount / this.length * 100.0; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
|
|
#region Constructors
|
|
public BitField(byte[] array, int length)
|
|
: this(length)
|
|
{
|
|
this.FromArray(array, 0, array.Length);
|
|
}
|
|
|
|
public BitField(int length)
|
|
{
|
|
if (length < 0)
|
|
throw new ArgumentOutOfRangeException("length");
|
|
|
|
this.length = length;
|
|
this.array = new int[(length + 31) / 32];
|
|
}
|
|
|
|
public BitField(bool[] array)
|
|
{
|
|
this.length = array.Length;
|
|
this.array = new int[(array.Length + 31) / 32];
|
|
for (int i = 0; i < array.Length; i++)
|
|
this.Set(i, array[i]);
|
|
}
|
|
|
|
#endregion
|
|
|
|
|
|
#region Methods BitArray
|
|
|
|
public bool this[int index]
|
|
{
|
|
get { return this.Get(index); }
|
|
internal set { this.Set(index, value); }
|
|
}
|
|
|
|
object ICloneable.Clone()
|
|
{
|
|
return this.Clone();
|
|
}
|
|
|
|
public BitField Clone()
|
|
{
|
|
BitField b = new BitField(this.length);
|
|
Buffer.BlockCopy(this.array, 0, b.array, 0, this.array.Length * 4);
|
|
b.trueCount = this.trueCount;
|
|
return b;
|
|
}
|
|
|
|
public BitField From(BitField value)
|
|
{
|
|
this.Check(value);
|
|
Buffer.BlockCopy(value.array, 0, this.array, 0, this.array.Length * 4);
|
|
this.trueCount = value.trueCount;
|
|
return this;
|
|
}
|
|
|
|
public BitField Not()
|
|
{
|
|
for (int i = 0; i < this.array.Length; i++)
|
|
this.array[i] = ~this.array[i];
|
|
|
|
this.trueCount = this.length - this.trueCount;
|
|
return this;
|
|
}
|
|
|
|
public BitField And(BitField value)
|
|
{
|
|
this.Check(value);
|
|
|
|
for (int i = 0; i < this.array.Length; i++)
|
|
this.array[i] &= value.array[i];
|
|
|
|
this.Validate();
|
|
return this;
|
|
}
|
|
|
|
internal BitField NAnd(BitField value)
|
|
{
|
|
this.Check(value);
|
|
|
|
for (int i = 0; i < this.array.Length; i++)
|
|
this.array[i] &= ~value.array[i];
|
|
|
|
this.Validate();
|
|
return this;
|
|
}
|
|
|
|
public BitField Or(BitField value)
|
|
{
|
|
this.Check(value);
|
|
|
|
for (int i = 0; i < this.array.Length; i++)
|
|
this.array[i] |= value.array[i];
|
|
|
|
this.Validate();
|
|
return this;
|
|
}
|
|
|
|
public BitField Xor(BitField value)
|
|
{
|
|
this.Check(value);
|
|
|
|
for (int i = 0; i < this.array.Length; i++)
|
|
this.array[i] ^= value.array[i];
|
|
|
|
this.Validate();
|
|
return this;
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
BitField bf = obj as BitField;
|
|
|
|
if (bf == null || this.array.Length != bf.array.Length || this.TrueCount != bf.TrueCount)
|
|
return false;
|
|
|
|
for (int i = 0; i < this.array.Length; i++)
|
|
if (this.array[i] != bf.array[i])
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
public int FirstTrue()
|
|
{
|
|
return this.FirstTrue(0, this.length);
|
|
}
|
|
|
|
public int FirstTrue(int startIndex, int endIndex)
|
|
{
|
|
int start;
|
|
int end;
|
|
|
|
// If the number of pieces is an exact multiple of 32, we need to decrement by 1 so we don't overrun the array
|
|
// For the case when endIndex == 0, we need to ensure we don't go negative
|
|
int loopEnd = Math.Min((endIndex / 32), this.array.Length - 1);
|
|
for (int i = (startIndex / 32); i <= loopEnd; i++)
|
|
{
|
|
if (this.array[i] == 0) // This one has no true values
|
|
continue;
|
|
|
|
start = i * 32;
|
|
end = start + 32;
|
|
start = (start < startIndex) ? startIndex : start;
|
|
end = (end > this.length) ? this.length : end;
|
|
end = (end > endIndex) ? endIndex : end;
|
|
if (end == this.Length && end > 0)
|
|
end--;
|
|
|
|
for (int j = start; j <= end; j++)
|
|
if (this.Get(j)) // This piece is true
|
|
return j;
|
|
}
|
|
|
|
return -1; // Nothing is true
|
|
}
|
|
|
|
public int FirstFalse()
|
|
{
|
|
return this.FirstFalse(0, this.Length);
|
|
}
|
|
|
|
public int FirstFalse(int startIndex, int endIndex)
|
|
{
|
|
int start;
|
|
int end;
|
|
|
|
// If the number of pieces is an exact multiple of 32, we need to decrement by 1 so we don't overrun the array
|
|
// For the case when endIndex == 0, we need to ensure we don't go negative
|
|
int loopEnd = Math.Min((endIndex / 32), this.array.Length - 1);
|
|
for (int i = (startIndex / 32); i <= loopEnd; i++)
|
|
{
|
|
if (this.array[i] == ~0) // This one has no false values
|
|
continue;
|
|
|
|
start = i * 32;
|
|
end = start + 32;
|
|
start = (start < startIndex) ? startIndex : start;
|
|
end = (end > this.length) ? this.length : end;
|
|
end = (end > endIndex) ? endIndex : end;
|
|
if (end == this.Length && end > 0)
|
|
end--;
|
|
|
|
for (int j = start; j <= end; j++)
|
|
if (!this.Get(j)) // This piece is true
|
|
return j;
|
|
}
|
|
|
|
return -1; // Nothing is true
|
|
}
|
|
internal void FromArray(byte[] buffer, int offset, int length)
|
|
{
|
|
int end = this.Length / 32;
|
|
for (int i = 0; i < end; i++)
|
|
this.array[i] = (buffer[offset++] << 24) |
|
|
(buffer[offset++] << 16) |
|
|
(buffer[offset++] << 8) |
|
|
(buffer[offset++] << 0);
|
|
|
|
int shift = 24;
|
|
for (int i = end * 32; i < this.Length; i += 8)
|
|
{
|
|
this.array[this.array.Length - 1] |= buffer[offset++] << shift;
|
|
shift -= 8;
|
|
}
|
|
this.Validate();
|
|
}
|
|
|
|
bool Get(int index)
|
|
{
|
|
if (index < 0 || index >= this.length)
|
|
throw new ArgumentOutOfRangeException("index");
|
|
|
|
return (this.array[index >> 5] & (1 << (31 - (index & 31)))) != 0;
|
|
}
|
|
|
|
public IEnumerator<bool> GetEnumerator()
|
|
{
|
|
for (int i = 0; i < this.length; i++)
|
|
yield return this.Get(i);
|
|
}
|
|
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
{
|
|
return this.GetEnumerator();
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
int count = 0;
|
|
for (int i = 0; i < this.array.Length; i++)
|
|
count += this.array[i];
|
|
|
|
return count;
|
|
}
|
|
|
|
public int LengthInBytes
|
|
{
|
|
get { return (this.length + 7) / 8; } //8 bits in a byte.
|
|
}
|
|
|
|
public BitField Set(int index, bool value)
|
|
{
|
|
if (index < 0 || index >= this.length)
|
|
throw new ArgumentOutOfRangeException("index");
|
|
|
|
if (value)
|
|
{
|
|
if ((this.array[index >> 5] & (1 << (31 - (index & 31)))) == 0)// If it's not already true
|
|
this.trueCount++; // Increase true count
|
|
this.array[index >> 5] |= (1 << (31 - index & 31));
|
|
}
|
|
else
|
|
{
|
|
if ((this.array[index >> 5] & (1 << (31 - (index & 31)))) != 0)// If it's not already false
|
|
this.trueCount--; // Decrease true count
|
|
this.array[index >> 5] &= ~(1 << (31 - (index & 31)));
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
internal BitField SetTrue(params int[] indices)
|
|
{
|
|
foreach (int index in indices)
|
|
this.Set(index, true);
|
|
return this;
|
|
}
|
|
|
|
internal BitField SetFalse(params int[] indices)
|
|
{
|
|
foreach (int index in indices)
|
|
this.Set(index, false);
|
|
return this;
|
|
}
|
|
|
|
internal BitField SetAll(bool value)
|
|
{
|
|
if (value)
|
|
{
|
|
for (int i = 0; i < this.array.Length; i++)
|
|
this.array[i] = ~0;
|
|
this.Validate();
|
|
}
|
|
|
|
else
|
|
{
|
|
for (int i = 0; i < this.array.Length; i++)
|
|
this.array[i] = 0;
|
|
this.trueCount = 0;
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
internal byte[] ToByteArray()
|
|
{
|
|
byte[] data = new byte[this.LengthInBytes];
|
|
this.ToByteArray(data, 0);
|
|
return data;
|
|
}
|
|
|
|
internal void ToByteArray(byte[] buffer, int offset)
|
|
{
|
|
if (buffer == null)
|
|
throw new ArgumentNullException("buffer");
|
|
|
|
this.ZeroUnusedBits();
|
|
int end = this.Length / 32;
|
|
for (int i = 0; i < end; i++)
|
|
{
|
|
buffer[offset++] = (byte)(this.array[i] >> 24);
|
|
buffer[offset++] = (byte)(this.array[i] >> 16);
|
|
buffer[offset++] = (byte)(this.array[i] >> 8);
|
|
buffer[offset++] = (byte)(this.array[i] >> 0);
|
|
}
|
|
|
|
int shift = 24;
|
|
for (int i = end * 32; i < this.Length; i += 8)
|
|
{
|
|
buffer[offset++] = (byte)(this.array[this.array.Length - 1] >> shift);
|
|
shift -= 8;
|
|
}
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
StringBuilder sb = new StringBuilder(this.array.Length * 16);
|
|
for (int i = 0; i < this.Length; i++)
|
|
{
|
|
sb.Append(this.Get(i) ? 'T' : 'F');
|
|
sb.Append(' ');
|
|
}
|
|
|
|
return sb.ToString(0, sb.Length - 1);
|
|
}
|
|
|
|
public int TrueCount
|
|
{
|
|
get { return this.trueCount; }
|
|
}
|
|
|
|
void Validate()
|
|
{
|
|
this.ZeroUnusedBits();
|
|
|
|
// Update the population count
|
|
uint count = 0;
|
|
for (int i = 0; i < this.array.Length; i++)
|
|
{
|
|
uint v = (uint)this.array[i];
|
|
v = v - ((v >> 1) & 0x55555555);
|
|
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
|
|
count += (((v + (v >> 4) & 0xF0F0F0F) * 0x1010101)) >> 24;
|
|
}
|
|
this.trueCount = (int)count ;
|
|
}
|
|
|
|
void ZeroUnusedBits()
|
|
{
|
|
if (this.array.Length == 0)
|
|
return;
|
|
|
|
// Zero the unused bits
|
|
int shift = 32 - this.length % 32;
|
|
if (shift != 0)
|
|
this.array[this.array.Length - 1] &= (-1 << shift);
|
|
}
|
|
|
|
void Check(BitField value)
|
|
{
|
|
MonoTorrent.Check.Value(value);
|
|
if (this.length != value.length)
|
|
throw new ArgumentException("BitFields are of different lengths", "value");
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|