Skip to content

Commit 4900613

Browse files
committed
CRC implementation, and Xorshift and BBS rework.
Along with that: unit tests and some tidying.
1 parent d98773d commit 4900613

16 files changed

+1156
-134
lines changed
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
template<typename IntegralType>
2+
CRC<IntegralType>::CRC (Type polynomial, Type initialValue, Type xorOutValue,
3+
bool shouldReflectIn, bool shouldReflectOut) noexcept :
4+
poly (polynomial),
5+
xorIn (initialValue),
6+
xorOut (xorOutValue),
7+
reflectIn (shouldReflectIn),
8+
reflectOut (shouldReflectOut),
9+
crc (initialValue)
10+
{
11+
populateLookupTable();
12+
}
13+
14+
template<typename IntegralType>
15+
CRC<IntegralType>::Type CRC<IntegralType>::getCheckValue() const
16+
{
17+
CRC<Type> test (poly, xorIn, xorOut, reflectIn, reflectOut);
18+
return test.processString (getCheckString()).finalise().get();
19+
}
20+
21+
template<typename IntegralType>
22+
CRC<IntegralType>& CRC<IntegralType>::reset() noexcept { crc = xorIn; return *this; }
23+
24+
template<typename IntegralType>
25+
[[nodiscard]] String CRC<IntegralType>::toHexString() const { return "0x" + String::toHexString (crc); }
26+
27+
template<typename IntegralType>
28+
[[nodiscard]] String CRC<IntegralType>::toBinString() const { return "0b" + String (toBitSet().to_string()); }
29+
30+
template<typename IntegralType>
31+
[[nodiscard]] String CRC<IntegralType>::toOctString() const
32+
{
33+
std::ostringstream str;
34+
str << std::oct << crc;
35+
return "0o" + String (str.str());
36+
}
37+
38+
template<typename IntegralType>
39+
CRC<IntegralType>& CRC<IntegralType>::processByte (uint8 data) noexcept
40+
{
41+
const auto dataConv = reflectIn
42+
? static_cast<Type> (reflect (data))
43+
: static_cast<Type> (data);
44+
45+
if constexpr (numBits == 8)
46+
{
47+
const auto pos = dataConv ^ crc;
48+
crc = table[(size_t) pos];
49+
}
50+
else if constexpr (numBits == 16)
51+
{
52+
const auto pos = (crc >> 8) ^ dataConv;
53+
crc = (Type) (crc << 8) ^ table[(size_t) pos];
54+
}
55+
else
56+
{
57+
const auto pos = (crc ^ (dataConv << numBitsToShift)) >> numBitsToShift;
58+
crc = (Type) (crc << 8) ^ table[(size_t) pos];
59+
}
60+
61+
return *this;
62+
}
63+
64+
template<typename IntegralType>
65+
CRC<IntegralType>& CRC<IntegralType>::process (const uint8* data, size_t numBytes) noexcept
66+
{
67+
jassert (data != nullptr && numBytes > 0);
68+
69+
if (data != nullptr)
70+
for (size_t i = 0; i < numBytes; ++i)
71+
processByte (data[i]);
72+
73+
return *this;
74+
}
75+
76+
template<typename IntegralType>
77+
CRC<IntegralType>& CRC<IntegralType>::process (const MemoryBlock& data) noexcept
78+
{
79+
return process (static_cast<const uint8*> (data.getData()), data.getSize());
80+
}
81+
82+
template<typename IntegralType>
83+
CRC<IntegralType>& CRC<IntegralType>::process (const File& file)
84+
{
85+
if (FileInputStream fis (file); fis.openedOk())
86+
{
87+
MemoryBlock data;
88+
fis.readIntoMemoryBlock (data);
89+
process (data);
90+
}
91+
92+
return *this;
93+
}
94+
95+
template<typename IntegralType>
96+
CRC<IntegralType>& CRC<IntegralType>::processString (const String& data)
97+
{
98+
return process (reinterpret_cast<const uint8*> (data.toRawUTF8()),
99+
data.getNumBytesAsUTF8());
100+
}
101+
102+
template<typename IntegralType>
103+
CRC<IntegralType>& CRC<IntegralType>::finalise() noexcept
104+
{
105+
if (reflectOut)
106+
crc = reflect (crc);
107+
108+
constexpr auto zero = static_cast<Type> (0);
109+
if (xorOut != zero)
110+
crc ^= xorOut;
111+
112+
return *this;
113+
}
114+
115+
template<typename IntegralType>
116+
void CRC<IntegralType>::populateLookupTable()
117+
{
118+
constexpr auto one = static_cast<Type> (1);
119+
constexpr auto bitMask = static_cast<Type> (one << (numBits - one));
120+
121+
// iterate over all possible input byte values 0 - 255
122+
for (size_t dividend = 0; dividend < 256; ++dividend)
123+
{
124+
// move dividend byte into MSB of CRC
125+
auto curByte = (Type) dividend << numBitsToShift;
126+
for (uint8 bit = 0; bit < 8; ++bit)
127+
{
128+
const bool test = (curByte & bitMask) != 0;
129+
curByte <<= 1;
130+
if (test)
131+
curByte ^= poly;
132+
}
133+
134+
table[dividend] = (Type) curByte;
135+
}
136+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/**
2+
3+
*/
4+
template<typename IntegralType>
5+
struct CRC final
6+
{
7+
//==============================================================================
8+
/** The underlying unsigned integral type. */
9+
using Type = std::make_unsigned_t<std::remove_cvref_t<std::remove_pointer_t<IntegralType>>>;
10+
/** The total number of bits needed for this CRC. */
11+
static inline constexpr auto numBits = static_cast<Type> (std::numeric_limits<Type>::digits);
12+
/** An std::bitset that houses the proper amount of bits to support the CRC value. */
13+
using BitSet = std::bitset<numBits>;
14+
15+
//==============================================================================
16+
/** Constructor.
17+
18+
@param polynomial Generator polynomial value to use.
19+
@param initialValue The value used to initialise the CRC value.
20+
If you call reset(), the CRC will be set to this value.
21+
@param xorOutValue The final XOR value is XORed to the final CRC value.
22+
This is done after the 'Result reflected' step.
23+
@param shouldReflectIn If this value is true, each input byte is reflected before being used in the CRC calculation.
24+
Reflected means that the bits of the input byte are used in reverse order.
25+
@param shouldReflectOut If this value is true, the final CRC value is reflected before being returned.
26+
The reflection is done over the entirety of CRC value's bits.
27+
*/
28+
CRC (Type polynomial, Type initialValue, Type xorOutValue,
29+
bool shouldReflectIn, bool shouldReflectOut) noexcept;
30+
31+
//==============================================================================
32+
/** Resets the CRC to the initial, aka XOR-In, value. */
33+
CRC& reset() noexcept;
34+
35+
/** @returns the CRC calculated thus far.
36+
37+
If you're done streaming in data, you should call finalise().
38+
This will give you the concluded CRC value.
39+
*/
40+
[[nodiscard]] constexpr Type get() const noexcept { return crc; }
41+
42+
/** @returns an appropriately sized std::bitset containing
43+
the currently calculated CRC.
44+
*/
45+
[[nodiscard]] BitSet toBitSet() const noexcept { return crc; }
46+
47+
//==============================================================================
48+
/** Processes a single byte into the CRC.
49+
This is where the bulk of the work happens.
50+
*/
51+
CRC& processByte (uint8 data) noexcept;
52+
53+
/** Processes an arbitrary pointer to data. */
54+
CRC& process (const uint8* data, size_t numBytes) noexcept;
55+
56+
/** Processes a MemoryBlock. */
57+
CRC& process (const MemoryBlock& data) noexcept;
58+
59+
/** Processes an entire File. */
60+
CRC& process (const File& file);
61+
62+
/** Processes a String. */
63+
CRC& processString (const String& data);
64+
65+
/** You must call this before getting the actual CRC value.
66+
67+
This is a separate step deliberately because of the nature of calculating CRC values:
68+
it's not possible to know the final CRC value until all the data has been processed.
69+
*/
70+
CRC& finalise() noexcept;
71+
72+
//==============================================================================
73+
/** @returns an hexadecimal representation of the CRC value.
74+
This will look something like "0x1234abcd".
75+
*/
76+
[[nodiscard]] String toHexString() const;
77+
78+
/** @returns a binary representation of the CRC value.
79+
This will look something like "0b00000001".
80+
*/
81+
[[nodiscard]] String toBinString() const;
82+
83+
/** @returns an octal representation of the CRC value.
84+
This will look something like "0o00000001".
85+
*/
86+
[[nodiscard]] String toOctString() const;
87+
88+
//==============================================================================
89+
/** @returns the standard check, or test, string.
90+
This is used to help quickly assert that things are in order.
91+
*/
92+
static String getCheckString() { return "123456789"; }
93+
94+
/** @returns the CRC value as expected by the check string. */
95+
Type getCheckValue() const;
96+
97+
private:
98+
//==============================================================================
99+
const Type poly, xorIn, xorOut;
100+
const bool reflectIn, reflectOut;
101+
std::array<Type, 256> table;
102+
Type crc;
103+
104+
static inline constexpr auto numBitsToShift = static_cast<Type> (numBits - 8);
105+
106+
//==============================================================================
107+
void populateLookupTable();
108+
109+
//==============================================================================
110+
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CRC)
111+
};
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
namespace bbs
2+
{
3+
constexpr std::optional<int> getMSB (const BlumBlumShub::BigM& set)
4+
{
5+
for (int i = (int) set.size(); --i >= 0;)
6+
if (set.test ((size_t) i))
7+
return { i };
8+
9+
return std::nullopt;
10+
}
11+
12+
inline BlumBlumShub::BigM toBitset (uint64 value) noexcept
13+
{
14+
return BlumBlumShub::BigM (static_cast<unsigned long long> (value));
15+
}
16+
17+
inline uint64 fromBitset (const BlumBlumShub::BigM& biggy) noexcept
18+
{
19+
return static_cast<uint64> (biggy.to_ullong());
20+
}
21+
22+
constexpr uint64 gcd (uint64 a, uint64 b)
23+
{
24+
while (b != 0)
25+
{
26+
const auto t = b;
27+
b = a % b;
28+
a = t;
29+
}
30+
31+
return a;
32+
}
33+
34+
constexpr bool areCoprimes (uint64 a, uint64 b)
35+
{
36+
if (isEven (a) && isEven (b))
37+
return false;
38+
39+
return gcd (a, b) == 1;
40+
}
41+
42+
inline uint64 createProbablePrime()
43+
{
44+
const auto biggy = Primes::createProbablePrime (32, 30);
45+
const auto hsb = biggy.getHighestBit();
46+
BlumBlumShub::BigM bits;
47+
48+
for (int i = 0; i < hsb; ++i)
49+
bits.set ((size_t) i, biggy[i]);
50+
51+
return fromBitset (bits);
52+
}
53+
}
54+
55+
//==============================================================================
56+
BlumBlumShub::BlumBlumShub() { reseed(); }
57+
BlumBlumShub::BlumBlumShub (uint64 customP, uint64 customQ) { reseed (customP, customQ); }
58+
59+
uint64 BlumBlumShub::generate() noexcept
60+
{
61+
x = square (x) % bbs::fromBitset (bigM);
62+
while (x == 0)
63+
reseed();
64+
65+
return x;
66+
}
67+
68+
double BlumBlumShub::generateNormalised() noexcept
69+
{
70+
const auto nextValue = generate();
71+
return lerp ((double) 0.0, (double) (ULLONG_MAX - 1ULL), (double) nextValue);
72+
}
73+
74+
void BlumBlumShub::reseed()
75+
{
76+
reseed (bbs::createProbablePrime(), bbs::createProbablePrime());
77+
}
78+
79+
void BlumBlumShub::reseed (uint64 customP, uint64 customQ)
80+
{
81+
// preseed 'x' to something non-static:
82+
x = []() -> uint64
83+
{
84+
std::random_device device;
85+
std::mt19937_64 engine (device());
86+
std::uniform_int_distribution<uint64> dist (1, ULLONG_MAX - 1ULL);
87+
return static_cast<uint64> (dist (engine));
88+
}();
89+
90+
p = customP;
91+
q = customQ;
92+
m = p * q;
93+
bigM = bbs::toBitset (m);
94+
95+
while (! bbs::areCoprimes (x, m))
96+
++x;
97+
}

0 commit comments

Comments
 (0)