Files
chia-blockchain/lib/chiapos/hellman_example/bits.hpp
T
2019-11-20 00:03:34 +02:00

621 lines
22 KiB
C++

// Copyright 2018 Chia Network Inc
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_CPP_BITS_HPP_
#define SRC_CPP_BITS_HPP_
#include <vector>
#include <algorithm>
#include <limits>
#include <string>
#include <utility>
#include "./util.hpp"
#include "./stack_allocator.h"
#define kBufSize 5
#define kMaxSizeBits 65535
// A stack vector of length 5, having the functions of std::vector needed for Bits.
struct SmallVector {
SmallVector() {
count_ = 0;
}
uint128_t& operator[] (const uint16_t index) {
return v_[index];
}
uint128_t operator[] (const uint16_t index) const {
return v_[index];
}
void push_back(uint128_t value) {
v_[count_++] = value;
}
SmallVector& operator = (const SmallVector& other) {
count_ = other.count_;
for (uint16_t i = 0; i < other.count_; i++)
v_[i] = other.v_[i];
return (*this);
}
uint16_t size() const {
return count_;
}
private:
uint128_t v_[5];
uint16_t count_;
};
// A stack vector of length 1024, having the functions of std::vector needed for Bits.
struct ParkVector {
ParkVector() {
count_ = 0;
}
uint128_t& operator[] (const uint16_t index) {
return v_[index];
}
uint128_t operator[] (const uint16_t index) const {
return v_[index];
}
void push_back(uint128_t value) {
v_[count_++] = value;
}
ParkVector& operator = (const ParkVector& other) {
count_ = other.count_;
for (uint16_t i = 0; i < other.count_; i++)
v_[i] = other.v_[i];
return (*this);
}
uint16_t size() const {
return count_;
}
private:
uint128_t v_[1024];
uint16_t count_;
};
/*
* This class represents an array of bits. These are stored in an
* array of integers, allowing for efficient bit manipulations. The Bits class provides
* utilities to easily work with Bits, adding and slicing them, etc.
* The class is a generic one, allowing any type of an array, as long as providing std::vector methods.
* We currently use SmallVector (stack-array of length 5), ParkVector (stack-array of length 128) and
* std::vector.
* Conversion between two BitsGeneric<T> classes of different types can be done by using += operator, or converting
* to bytes the first class, then using the bytes constructor of the second class (should be slower).
* NOTE: CalculateBucket only accepts a BitsGeneric<SmallVector>, so in order to use that, you have to firstly convert
* your BitsGeneric<T> object into a BitsGeneric<SmallVector>.
*/
template <class T> class BitsGeneric {
public:
template <class> friend class BitsGeneric;
BitsGeneric<T>() {
this->last_size_ = 0;
}
// Converts from unit128_t to Bits. If the number of bits of value is smaller than size, adds 0 bits at the beginning.
// i.e. Bits(5, 10) = 0000000101
BitsGeneric<T>(uint128_t value, uint16_t size) {
// TODO(mariano) remove
if (size < 128 && value > ((uint128_t)1 << size)) {
std::cout << "TOO BIG FOR BITS" << std::endl;
// abort();
}
this->last_size_ = 0;
if (size > 128) {
// Get number of extra 0s added at the beginning.
uint16_t zeros = size - Util::GetSizeBits(value);
// Add a full group of 0s (length 128)
while (zeros > 128) {
AppendValue(0, 128);
zeros -= 128;
}
// Add the incomplete group of 0s and then the value.
AppendValue(0, zeros);
AppendValue(value, Util::GetSizeBits(value));
} else {
values_.push_back(value);
this->last_size_ = size;
}
}
// Copy the content of another Bits object. If the size of the other Bits object is smaller
// than 'size', adds 0 bits at the beginning.
BitsGeneric<T>(const BitsGeneric<T>& other, uint16_t size) {
uint16_t total_size = other.GetSize();
this->last_size_ = 0;
assert(size >= total_size);
// Add the extra 0 bits at the beginning.
uint16_t extra_space = size - total_size;
while (extra_space >= 128) {
AppendValue(0, 128);
extra_space -= 128;
}
if (extra_space > 0)
AppendValue(0, extra_space);
// Copy the Bits object element by element, and append it to the current Bits object.
if (other.values_.size() > 0) {
for (uint8_t i = 0; i < other.values_.size() - 1; i++)
AppendValue(other.values_[i], 128);
AppendValue(other.values_[other.values_.size() - 1], other.last_size_);
}
}
// Converts bytes to bits.
BitsGeneric<T>(uint8_t* big_endian_bytes, uint32_t num_bytes, uint16_t size_bits) {
this->last_size_ = 0;
uint32_t extra_space = size_bits - num_bytes * 8;
// Add the extra 0 bits at the beginning.
while (extra_space >= 128) {
AppendValue(0, 128);
extra_space -= 128;
}
if (extra_space > 0) {
AppendValue(0, extra_space);
}
for (uint32_t i = 0; i < num_bytes; i += 16) {
uint128_t val = 0;
uint8_t bucket_size = 0;
// Compress bytes together into uint128_t, either until we have 128 bits, or until we run out of bytes
// in big_endian_bytes.
for (uint32_t j = i; j < i + 16 && j < num_bytes; j++) {
val = (val << 8) + big_endian_bytes[j];
bucket_size += 8;
}
AppendValue(val, bucket_size);
}
}
BitsGeneric<T>(const BitsGeneric<T>& other) {
values_ = other.values_;
last_size_ = other.last_size_;
}
BitsGeneric<T>& operator = (const BitsGeneric<T>& other) {
values_ = other.values_;
last_size_ = other.last_size_;
return *this;
}
// Concatenates two Bits objects together.
BitsGeneric<T> operator+(const BitsGeneric<T>& b) const {
if (GetSize() + b.GetSize() > kMaxSizeBits) {
throw std::string("The number of bits exceeds the limit.");
}
BitsGeneric<T> result;
if (values_.size() > 0) {
for (uint8_t i = 0; i < values_.size() - 1; i++)
result.AppendValue(values_[i], 128);
result.AppendValue(values_[values_.size() - 1], last_size_);
}
if (b.values_.size() > 0) {
for (uint8_t i = 0; i < b.values_.size() - 1; i++)
result.AppendValue(b.values_[i], 128);
result.AppendValue(b.values_[b.values_.size() - 1], b.last_size_);
}
return result;
}
// Appends one Bits object at the end of the first one.
template <class T2>
BitsGeneric<T>& operator += (const BitsGeneric<T2>& b) {
if (b.values_.size() > 0) {
for (uint8_t i = 0; i < b.values_.size() - 1; i++)
this->AppendValue(b.values_[i], 128);
this->AppendValue(b.values_[b.values_.size() - 1], b.last_size_);
}
return *this;
}
BitsGeneric<T>& operator++() {
uint128_t limit = ((uint128_t)std::numeric_limits<uint64_t> :: max() << 64) +
(uint128_t)std::numeric_limits<uint64_t> :: max();
uint128_t last_bucket_mask = (last_size_ == 128) ? limit : ((static_cast<uint128_t>(1) << last_size_) - 1);
// If the last bucket isn't full of 1 bits, we can increment that by one.
if (values_[values_.size() - 1] != last_bucket_mask) {
values_[values_.size() - 1]++;
} else {
bool all_one = true;
if (values_.size() > 1) {
// Otherwise, search for the first bucket that isn't full of 1 bits.
for (int16_t i = values_.size() - 2; i >= 0; i--)
if (values_[i] != limit) {
all_one = false;
// Increment it.
values_[i]++;
// Buckets that were full of 1 bits turn all to 0 bits.
// (i.e. 10011111 + 1 = 10100000)
for (uint16_t j = i + 1; j < values_.size(); j++)
values_[j] = 0;
break;
}
}
// This isn't allowed, as the Bits size must remain constant during all the plotting process.
assert(all_one == false);
}
return *this;
}
BitsGeneric<T> operator++(int) {
BitsGeneric<T> result(*this);
++(*this);
return result;
}
BitsGeneric<T>& operator--() {
bool all_zero = true;
// If the last bucket is not zero, we can derement it.
if (values_[values_.size() - 1] != 0) {
values_[values_.size() - 1]--;
return *this;
}
if (values_.size() > 1) {
// Search for the first bucket different than 0.
for (int16_t i = values_.size() - 2; i >= 0; i--)
if (values_[i] != 0) {
all_zero = false;
// Decrement it.
values_[i]--;
uint128_t limit = ((uint128_t)std::numeric_limits<uint64_t> :: max() << 64) +
(uint128_t)std::numeric_limits<uint64_t> :: max();
// All buckets that were previously 0, now become full of 1s.
// (i.e. 1010000 - 1 = 1001111)
for (uint16_t j = i + 1; j < values_.size() - 1; j++)
values_[j] = limit;
values_[values_.size() - 1] = (last_size_ == 128) ? limit :
((static_cast<uint128_t>(1) << last_size_) - 1);
break;
}
}
if (all_zero) {
throw std::string("Overflow, negative number");
}
return *this;
}
BitsGeneric<T> operator--(int) {
BitsGeneric<T> result(*this);
--(*this);
return result;
}
BitsGeneric<T> operator^(const BitsGeneric<T>& other) const {
assert(GetSize() == other.GetSize());
BitsGeneric<T> res;
// Xoring individual bits is the same as xor-ing chunks of bits.
for (uint16_t i = 0; i < values_.size(); i++)
res.values_.push_back(values_[i] ^ other.values_[i]);
res.last_size_ = last_size_;
return res;
}
BitsGeneric<T> Slice(int32_t start_index) const {
return Slice(start_index, GetSize());
}
// Slices the bits from [start_index, end_index)
BitsGeneric<T> Slice(int32_t start_index, int32_t end_index) const {
if (end_index > GetSize()) {
end_index = GetSize();
}
if (start_index < 0) {
start_index = 0;
}
if (end_index == start_index) return BitsGeneric<T>();
assert(end_index > start_index);
uint32_t start_bucket = start_index / 128;
uint32_t end_bucket = end_index / 128;
if (start_bucket == end_bucket) {
// Positions inside the bucket.
start_index = start_index % 128;
end_index = end_index % 128;
uint8_t bucket_size = (start_bucket == values_.size() - 1) ? last_size_ : 128;
uint128_t val = values_[start_bucket];
// Cut the prefix [0, start_index)
if (start_index != 0)
val = val & ((static_cast<uint128_t>(1) << (bucket_size - start_index)) - 1);
// Cut the suffix after end_index
val = val >> (bucket_size - end_index);
return BitsGeneric<T>(val, end_index - start_index);
} else {
BitsGeneric<T> result;
uint128_t prefix, suffix;
// Get the prefix from the last bucket.
SplitNumberByPrefix(values_[start_bucket], 128, start_index % 128, &prefix, &suffix);
result.AppendValue(suffix, 128 - start_index % 128);
// Append all the in between buckets
for (uint32_t i = start_bucket + 1; i < end_bucket; i++)
result.AppendValue(values_[i], 128);
uint8_t bucket_size = (end_bucket == values_.size() - 1) ? last_size_ : 128;
// Get the suffix from the last bucket.
SplitNumberByPrefix(values_[end_bucket], bucket_size, end_index % 128, &prefix, &suffix);
result.AppendValue(prefix, end_index % 128);
return result;
}
}
// Same as 'Slice', but result fits into an uint64_t. Used for memory optimization.
uint64_t SliceBitsToInt(int16_t start_index, int16_t end_index) const {
/*if (end_index > GetSize()) {
end_index = GetSize();
}
if (start_index < 0) {
start_index = 0;
} */
if ((start_index >> 7) == (end_index >> 7)) {
uint128_t res = values_[start_index >> 7];
if (((uint32_t)start_index >> 7) == values_.size() - 1)
res = res >> (last_size_ - (end_index & 127));
else
res = res >> (128 - (end_index & 127));
res = res & (((uint128_t)1 << ((end_index & 127) - (start_index & 127))) - 1);
return res;
} else {
assert((start_index >> 7) + 1 == (end_index >> 7));
uint128_t prefix, suffix;
SplitNumberByPrefix(values_[(start_index >> 7)], 128, start_index & 127, &prefix, &suffix);
uint128_t result = suffix;
uint8_t bucket_size = (((uint32_t)end_index >> 7) == values_.size() - 1) ? last_size_ : 128;
SplitNumberByPrefix(values_[(end_index >> 7)], bucket_size, end_index & 127, &prefix, &suffix);
result = (result << (end_index & 127)) + prefix;
return result;
}
}
void ToBytes(uint8_t buffer[]) const {
// Append 0s to complete the last byte.
uint8_t shift = Util::ByteAlign(last_size_) - last_size_;
uint128_t val = values_[values_.size() - 1] << (shift);
uint16_t cnt = 0;
// Extract byte-by-byte from the last bucket.
uint8_t iterations = last_size_ / 8;
if (last_size_ % 8)
iterations++;
for (uint8_t i = 0; i < iterations; i++) {
buffer[cnt++] = (val & 0xff);
val >>= 8;
}
// Extract the full buckets, byte by byte.
if (values_.size() >= 2) {
for (int16_t i = values_.size() - 2; i >= 0; i--) {
uint128_t val = values_[i];
for (uint8_t j = 0; j < 16; j++) {
buffer[cnt++] = (val & 0xff);
val >>= 8;
}
}
}
// Since we extracted from end to beginning, bytes are in reversed order. Reverse everything.
uint16_t left = 0, right = cnt - 1;
while (left < right) {
std::swap(buffer[left], buffer[right]);
left++;
right--;
}
}
std::string ToString() const {
std::string str = "";
for (uint16_t i = 0; i < values_.size(); i++) {
uint128_t val = values_[i];
uint16_t size = (i == values_.size() - 1) ? last_size_ : 128;
std::string str_bucket = "";
for (int i = 0; i < size; i++) {
if (val % 2)
str_bucket = "1" + str_bucket;
else
str_bucket = "0" + str_bucket;
val /= 2;
}
str += str_bucket;
}
return str;
}
// If the bitarray fits into 128 bits, returns it as an uint128_t, otherwise throws error
uint128_t GetValue() const {
if (values_.size() != 1) {
std::cout << "Number of values is: " << values_.size() << std::endl;
std::cout << "Size of bits is: " << GetSize() << std::endl;
throw std::string("Number doesn't fit into a 128-bit type.");
}
return values_[0];
}
uint16_t GetSize() const {
if (values_.size() == 0) return 0;
// Full buckets contain each 128 bits, last one contains only 'last_size_' bits.
return (values_.size() - 1) * 128 + last_size_;
}
void AppendValue(uint128_t value, uint8_t length) {
// The last bucket is full or no bucket yet, create a new one.
if (values_.size() == 0 || last_size_ == 128) {
values_.push_back(value);
last_size_ = length;
} else {
uint8_t free_bits = 128 - last_size_;
// If the value fits into the last bucket, append it all there.
if (length <= free_bits) {
values_[values_.size() - 1] = (values_[values_.size() - 1] << length) + value;
last_size_ += length;
} else {
// Otherwise, append the prefix into the last bucket, and create a new bucket for the suffix.
uint128_t prefix, suffix;
SplitNumberByPrefix(value, length, free_bits, &prefix, &suffix);
values_[values_.size() - 1] = (values_[values_.size() - 1] << free_bits) + prefix;
values_.push_back(suffix);
last_size_ = length - free_bits;
}
}
}
template <class X>
friend std::ostream &operator<<(std::ostream&, const BitsGeneric<X>&);
template <class X>
friend bool operator==(const BitsGeneric<X>& lhs, const BitsGeneric<X>& rhs);
template <class X>
friend bool operator<(const BitsGeneric<X>& lhs, const BitsGeneric<X>& rhs);
template <class X>
friend bool operator>(const BitsGeneric<X>& lhs, const BitsGeneric<X>& rhs);
template <class X>
friend BitsGeneric<X> operator<<(BitsGeneric<X> lhs, uint32_t shift_amount);
template <class X>
friend BitsGeneric<X> operator>>(BitsGeneric<X> lhs, uint32_t shift_amount);
private:
void SplitNumberByPrefix(uint128_t number, uint8_t num_bits, uint8_t prefix_size, uint128_t* prefix,
uint128_t* suffix) const {
assert(num_bits >= prefix_size);
if (prefix_size == 0) {
*prefix = 0;
*suffix = number;
return;
}
uint8_t suffix_size = num_bits - prefix_size;
uint128_t mask = (static_cast<uint128_t>(1)) << suffix_size;
mask--;
*suffix = number & mask;
*prefix = number >> suffix_size;
}
T values_;
uint8_t last_size_;
};
template<class T>
std::ostream &operator<<(std::ostream & strm, BitsGeneric<T> const & v) {
strm << "b" << v.ToString();
return strm;
}
template <class T>
bool operator==(const BitsGeneric<T>& lhs, const BitsGeneric<T>& rhs) {
if (lhs.GetSize() != rhs.GetSize()) {
return false;
}
for (uint16_t i = 0; i < lhs.values_.size(); i++) {
if (lhs.values_[i] != rhs.values_[i]) {
return false;
}
}
return true;
}
template <class T>
bool operator<(const BitsGeneric<T>& lhs, const BitsGeneric<T>& rhs) {
if (lhs.GetSize() != rhs.GetSize())
throw std::string("Different sizes!");
for (uint16_t i = 0; i < lhs.values_.size(); i++) {
if (lhs.values_[i] < rhs.values_[i])
return true;
if (lhs.values_[i] > rhs.values_[i])
return false;
}
return false;
}
template <class T>
bool operator>(const BitsGeneric<T>& lhs, const BitsGeneric<T>& rhs) {
if (lhs.GetSize() != rhs.GetSize())
throw std::string("Different sizes!");
for (uint16_t i = 0; i < lhs.values_.size(); i++) {
if (lhs.values_[i] > rhs.values_[i])
return true;
if (lhs.values_[i] < rhs.values_[i])
return false;
}
return false;
}
template <class T>
BitsGeneric<T> operator<<(BitsGeneric<T> lhs, uint32_t shift_amount) {
if (lhs.GetSize() == 0) {
return BitsGeneric<T>();
}
BitsGeneric<T> result;
// Shifts are cyclic, shifting by the number of bits gives the same number.
int num_blocks_shift = static_cast<int>(shift_amount / 128);
uint32_t shift_remainder = shift_amount % 128;
for (uint32_t i = 0; i < lhs.values_.size(); i++) {
uint128_t new_value = 0;
if (i + num_blocks_shift < lhs.values_.size()) {
new_value += (lhs.values_[i + num_blocks_shift] << shift_remainder);
}
if (i + num_blocks_shift + 1 < lhs.values_.size()) {
new_value += (lhs.values_[i + num_blocks_shift + 1] >> (128 - shift_remainder));
}
uint8_t new_length;
if (i == (uint32_t)lhs.values_.size() - 1) {
new_length = lhs.last_size_;
} else {
new_length = 128;
}
result.AppendValue(new_value, new_length);
}
return result;
}
template <class T>
BitsGeneric<T> operator>>(BitsGeneric<T> lhs, uint32_t shift_amount) {
if (lhs.GetSize() == 0) {
return BitsGeneric<T>();
}
BitsGeneric<T> result;
int num_blocks_shift = static_cast<int>(shift_amount / 128);
uint32_t shift_remainder = shift_amount % 128;
for (int i = 0; i < lhs.values_.size(); i++) {
uint128_t new_value = 0;
if (i - num_blocks_shift >= 0) {
new_value += (lhs.values_[i - num_blocks_shift] >> shift_remainder);
}
if (i - num_blocks_shift - 1 >= 0) {
new_value += (lhs.values_[i - num_blocks_shift - 1] << (128 - shift_remainder));
}
uint8_t new_length;
if (i == lhs.values_.size() - 1) {
new_length = lhs.last_size_;
} else {
new_length = 128;
}
result.AppendValue(new_value, new_length);
}
return result;
}
typedef std::vector<uint128_t> LargeVector;
using Bits = BitsGeneric<SmallVector>;
using ParkBits = BitsGeneric<ParkVector>;
using LargeBits = BitsGeneric<LargeVector>;
#endif // SRC_CPP_BITS_HPP_