-
Notifications
You must be signed in to change notification settings - Fork 16
Expand file tree
/
Copy pathBuckets.cs
More file actions
163 lines (148 loc) · 5.81 KB
/
Buckets.cs
File metadata and controls
163 lines (148 loc) · 5.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
namespace ProbabilisticDataStructures
{
/// <summary>
/// Buckets is a fast, space-efficient array of buckets where each bucket can store
/// up to a configured maximum value.
/// </summary>
public class Buckets
{
private byte[] Data { get; set; }
private byte bucketSize { get; set; }
private byte _max;
private int Max
{
get
{
return _max;
}
set
{
// TODO: Figure out this truncation thing.
// I'm not sure if MaxValue is always supposed to be capped at 255 via
// a byte conversion or not...
if (value > byte.MaxValue)
_max = byte.MaxValue;
else
_max = (byte)value;
}
}
internal uint count { get; set; }
/// <summary>
/// Creates a new Buckets with the provided number of buckets where each bucket
/// is the specified number of bits.
/// </summary>
/// <param name="count">Number of buckets.</param>
/// <param name="bucketSize">Number of bits per bucket.</param>
internal Buckets(uint count, byte bucketSize)
{
this.count = count;
this.Data = new byte[(count * bucketSize + 7) / 8];
this.bucketSize = bucketSize;
this.Max = (1 << bucketSize) - 1;
}
/// <summary>
/// Returns the maximum value that can be stored in a bucket.
/// </summary>
/// <returns>The bucket max value.</returns>
internal byte MaxBucketValue()
{
return this._max;
}
/// <summary>
/// Increment the value in the specified bucket by the provided delta. A bucket
/// can be decremented by providing a negative delta.
/// <para>
/// The value is clamped to zero and the maximum bucket value. Returns itself
/// to allow for chaining.
/// </para>
/// </summary>
/// <param name="bucket">The bucket to increment.</param>
/// <param name="delta">The amount to increment the bucket by.</param>
/// <returns>The modified bucket.</returns>
internal Buckets Increment(uint bucket, int delta)
{
int val = (int)(GetBits(bucket * this.bucketSize, this.bucketSize) + delta);
if (val > this.Max)
val = this.Max;
else if (val < 0)
val = 0;
SetBits((uint)bucket * (uint)this.bucketSize, this.bucketSize, (uint)val);
return this;
}
/// <summary>
/// Set the bucket value. The value is clamped to zero and the maximum bucket
/// value. Returns itself to allow for chaining.
/// </summary>
/// <param name="bucket">The bucket to change the value of.</param>
/// <param name="value">The value to set.</param>
/// <returns>The modified bucket.</returns>
internal Buckets Set(uint bucket, byte value)
{
if (value > this._max)
value = this._max;
SetBits(bucket * this.bucketSize, this.bucketSize, value);
return this;
}
/// <summary>
/// Returns the value in the specified bucket.
/// </summary>
/// <param name="bucket">The bucket to get.</param>
/// <returns>The specified bucket.</returns>
internal uint Get(uint bucket)
{
return GetBits(bucket * this.bucketSize, this.bucketSize);
}
/// <summary>
/// Restores the Buckets to the original state. Returns itself to allow for
/// chaining.
/// </summary>
/// <returns>The Buckets object the reset operation was performed on.</returns>
internal Buckets Reset()
{
this.Data = new byte[(this.count * this.bucketSize + 7) / 8];
return this;
}
/// <summary>
/// Returns the bits at the specified offset and length.
/// </summary>
/// <param name="offset">The position to start reading at.</param>
/// <param name="length">The distance to read from the offset.</param>
/// <returns>The bits at the specified offset and length.</returns>
internal uint GetBits(uint offset, int length)
{
uint byteIndex = offset / 8;
int byteOffset = (int)(offset % 8);
if ((byteOffset + length) > 8)
{
int rem = 8 - byteOffset;
return GetBits(offset, rem)
| (GetBits((uint)(offset + rem), length - rem) << rem);
}
int bitMask = (1 << length) - 1;
return (uint)((this.Data[byteIndex] & (bitMask << byteOffset)) >> byteOffset);
}
/// <summary>
/// Sets bits at the specified offset and length.
/// </summary>
/// <param name="offset">The position to start writing at.</param>
/// <param name="length">The distance to write from the offset.</param>
/// <param name="bits">The bits to write.</param>
internal void SetBits(uint offset, int length, uint bits)
{
uint byteIndex = offset / 8;
int byteOffset = (int)(offset % 8);
if ((byteOffset + length) > 8)
{
int rem = 8 - byteOffset;
SetBits(offset, (byte)rem, bits);
SetBits((uint)(offset + rem), length - rem, bits >> rem);
return;
}
int bitMask = (1 << length) - 1;
this.Data[byteIndex] =
(byte)((this.Data[byteIndex]) & ~(bitMask << byteOffset));
this.Data[byteIndex] =
(byte)((this.Data[byteIndex]) | ((bits & bitMask) << byteOffset));
}
}
}