279 lines
7.8 KiB
C
279 lines
7.8 KiB
C
#include <common/toolkit/Serialization.h>
|
|
#include "BitStore.h"
|
|
|
|
/**
|
|
* Set or unset a bit at the given index.
|
|
*
|
|
* Note: The bit operations in here are atomic.
|
|
*
|
|
* @param isSet true to set or false to unset the corresponding bit.
|
|
*/
|
|
void BitStore_setBit(BitStore* this, unsigned bitIndex, bool isSet)
|
|
{
|
|
unsigned index = BitStore_getBitBlockIndex(bitIndex);
|
|
unsigned indexInBitBlock = BitStore_getBitIndexInBitBlock(bitIndex);
|
|
|
|
if(unlikely(bitIndex >= this->numBits) )
|
|
{
|
|
BEEGFS_BUG_ON(true, "index out of bounds: bitIndex >= this->numBits");
|
|
return;
|
|
}
|
|
|
|
if (index == 0)
|
|
{
|
|
if (isSet)
|
|
set_bit(indexInBitBlock, &this->lowerBits);
|
|
else
|
|
clear_bit(indexInBitBlock, &this->lowerBits);
|
|
}
|
|
else
|
|
{
|
|
if (isSet)
|
|
set_bit(indexInBitBlock, &this->higherBits[index - 1]);
|
|
else
|
|
clear_bit(indexInBitBlock, &this->higherBits[index - 1]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* grow/shrink internal buffers when needed for new size.
|
|
*
|
|
* note: this method does not reset or copy bits, so consider the bits to be uninitialized after
|
|
* calling this method. thus, you might want to call clearBits() afterwards to clear all bits.
|
|
*
|
|
* @param size number of bits
|
|
*/
|
|
void BitStore_setSize(BitStore* this, unsigned newSize)
|
|
{
|
|
unsigned oldBitBlockCount = BitStore_calculateBitBlockCount(this->numBits);
|
|
unsigned newBitBlockCount = BitStore_calculateBitBlockCount(newSize);
|
|
|
|
if(newBitBlockCount != oldBitBlockCount)
|
|
{
|
|
SAFE_KFREE(this->higherBits);
|
|
|
|
if(newBitBlockCount > 1)
|
|
{
|
|
this->higherBits = (bitstore_store_type*)os_kmalloc(
|
|
BITSTORE_BLOCK_SIZE * (newBitBlockCount - 1) );
|
|
}
|
|
|
|
this->numBits = newBitBlockCount * BITSTORE_BLOCK_BIT_COUNT;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* reset all bits to 0
|
|
*/
|
|
void BitStore_clearBits(BitStore* this)
|
|
{
|
|
this->lowerBits = BITSTORE_BLOCK_INIT_MASK;
|
|
|
|
if(this->higherBits)
|
|
{
|
|
unsigned bitBlockCount = BitStore_calculateBitBlockCount(this->numBits);
|
|
|
|
memset(this->higherBits, 0, (bitBlockCount-1) * BITSTORE_BLOCK_SIZE);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* note: serialized format is always one or more full 64bit blocks to keep the format independent
|
|
* of 32bit/64bit archs.
|
|
* note: serialized format is 8 byte aligned.
|
|
*/
|
|
void BitStore_serialize(SerializeCtx* ctx, const BitStore* this)
|
|
{
|
|
unsigned index;
|
|
unsigned blockCount = BitStore_calculateBitBlockCount(this->numBits);
|
|
|
|
// size
|
|
Serialization_serializeUInt(ctx, this->numBits);
|
|
|
|
// padding for 8byte alignment
|
|
Serialization_serializeUInt(ctx, 0);
|
|
|
|
{ // lowerBits
|
|
if (BITSTORE_BLOCK_SIZE == sizeof(uint64_t) )
|
|
Serialization_serializeUInt64(ctx, this->lowerBits);
|
|
else
|
|
if (BITSTORE_BLOCK_SIZE == sizeof(uint32_t) )
|
|
Serialization_serializeUInt(ctx, this->lowerBits);
|
|
}
|
|
|
|
{ // higherBits
|
|
for(index = 0; index < (blockCount - 1); index++)
|
|
{
|
|
if (BITSTORE_BLOCK_SIZE == sizeof(uint64_t) )
|
|
Serialization_serializeUInt64(ctx, this->higherBits[index]);
|
|
else
|
|
if (BITSTORE_BLOCK_SIZE == sizeof(uint32_t) )
|
|
Serialization_serializeUInt(ctx, this->higherBits[index]);
|
|
}
|
|
}
|
|
|
|
// add padding to allow 64bit deserialize from 32bit serialize
|
|
if( (BITSTORE_BLOCK_SIZE == sizeof(uint32_t) ) && (blockCount & 1) )
|
|
{ // odd number of 32bit blocks
|
|
Serialization_serializeUInt(ctx, 0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param outBitStoreStart deserialization buffer pointer for deserialize()
|
|
*/
|
|
bool BitStore_deserializePreprocess(DeserializeCtx* ctx, const char** outBitStoreStart)
|
|
{
|
|
unsigned padding;
|
|
unsigned tmpSize = 0; // =0, because it is used later and otherwise gcc complains
|
|
// with "may be uninitialized"
|
|
unsigned blockCount;
|
|
size_t blocksLen;
|
|
|
|
*outBitStoreStart = ctx->data;
|
|
|
|
// size
|
|
if(unlikely(!Serialization_deserializeUInt(ctx, &tmpSize) ) )
|
|
return false;
|
|
|
|
// padding for 8byte alignment
|
|
if(unlikely(!Serialization_deserializeUInt(ctx, &padding) ) )
|
|
return false;
|
|
|
|
// bit blocks
|
|
|
|
blockCount = BitStore_calculateBitBlockCount(tmpSize);
|
|
|
|
if(BITSTORE_BLOCK_SIZE == sizeof(uint64_t) )
|
|
blocksLen = Serialization_serialLenUInt64() * blockCount;
|
|
else
|
|
if(BITSTORE_BLOCK_SIZE == sizeof(uint32_t) )
|
|
{
|
|
// add padding that allows 64bit deserialize from 32bit serialize
|
|
if(blockCount & 1)
|
|
blockCount++;
|
|
|
|
blocksLen = Serialization_serialLenUInt() * blockCount;
|
|
}
|
|
else
|
|
return false;
|
|
|
|
// check buffer length
|
|
if(unlikely(blocksLen > ctx->length) )
|
|
return false;
|
|
|
|
ctx->data += blocksLen;
|
|
ctx->length -= blocksLen;
|
|
|
|
return true;
|
|
}
|
|
|
|
void BitStore_deserialize(BitStore* this, DeserializeCtx* ctx)
|
|
{
|
|
unsigned padding;
|
|
unsigned blockCount;
|
|
unsigned bitCount = 0;
|
|
|
|
// size
|
|
// store the bit count in a temp variable because setSizeAndReset() below needs old count
|
|
Serialization_deserializeUInt(ctx, &bitCount);
|
|
|
|
// padding for 8byte alignment
|
|
Serialization_deserializeUInt(ctx, &padding);
|
|
|
|
// clear and alloc memory for the higher bits if needed
|
|
|
|
BitStore_setSize(this, bitCount);
|
|
|
|
{ // lowerBits
|
|
if (BITSTORE_BLOCK_SIZE == sizeof(uint64_t) )
|
|
Serialization_deserializeUInt64(ctx, (uint64_t*)&this->lowerBits);
|
|
else
|
|
if (BITSTORE_BLOCK_SIZE == sizeof(uint32_t) )
|
|
Serialization_deserializeUInt(ctx, (uint32_t*)&this->lowerBits);
|
|
}
|
|
|
|
blockCount = BitStore_calculateBitBlockCount(this->numBits);
|
|
|
|
{ // higherBits
|
|
unsigned index;
|
|
|
|
BEEGFS_BUG_ON_DEBUG( (blockCount > 1) && !this->higherBits, "Bug: higherBits==NULL");
|
|
|
|
for(index = 0; index < (blockCount - 1); index++)
|
|
{
|
|
bitstore_store_type* higherBits = &this->higherBits[index];
|
|
|
|
if (BITSTORE_BLOCK_SIZE == sizeof(uint64_t) )
|
|
Serialization_deserializeUInt64(ctx, (uint64_t*)higherBits);
|
|
else
|
|
if (BITSTORE_BLOCK_SIZE == sizeof(uint32_t) )
|
|
Serialization_deserializeUInt(ctx, (uint32_t*)higherBits);
|
|
}
|
|
}
|
|
|
|
// add padding that allows 64bit deserialize from 32bit serialize
|
|
if( (BITSTORE_BLOCK_SIZE == sizeof(uint32_t) ) && (blockCount & 1) )
|
|
Serialization_deserializeUInt(ctx, &padding);
|
|
}
|
|
|
|
/**
|
|
* Update this store with values from given other store.
|
|
*/
|
|
void BitStore_copy(BitStore* this, BitStore* other)
|
|
{
|
|
BitStore_setSize(this, other->numBits);
|
|
|
|
this->lowerBits = other->lowerBits;
|
|
|
|
if(!this->higherBits)
|
|
return;
|
|
|
|
// we have higherBits to copy
|
|
{
|
|
unsigned blockCount = BitStore_calculateBitBlockCount(this->numBits);
|
|
|
|
memcpy(this->higherBits, other->higherBits, (blockCount-1) * BITSTORE_BLOCK_SIZE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update this store with values from given other store.
|
|
*
|
|
* To make this copy thread-safe, this method does not attempt any re-alloc of internal buffers,
|
|
* so the copy will not be complete if the second store contains more blocks.
|
|
*/
|
|
void BitStore_copyThreadSafe(BitStore* this, const BitStore* other)
|
|
{
|
|
unsigned thisBlockCount;
|
|
unsigned otherBlockCount;
|
|
|
|
this->lowerBits = other->lowerBits;
|
|
|
|
if(!this->higherBits)
|
|
return;
|
|
|
|
// copy (or clear) our higherBits
|
|
|
|
thisBlockCount = BitStore_calculateBitBlockCount(this->numBits);
|
|
otherBlockCount = BitStore_calculateBitBlockCount(other->numBits);
|
|
|
|
if(otherBlockCount > 1)
|
|
{ // copy as much as we can from other's higherBits
|
|
unsigned minCommonBlockCount = MIN(thisBlockCount, otherBlockCount);
|
|
|
|
memcpy(this->higherBits, other->higherBits, (minCommonBlockCount-1) * BITSTORE_BLOCK_SIZE);
|
|
}
|
|
|
|
// (this store must at least have one higherBits block, because higherBits!=NULL)
|
|
if(thisBlockCount > otherBlockCount)
|
|
{ // zero remaining higherBits of this store
|
|
unsigned numBlocksDiff = thisBlockCount - otherBlockCount;
|
|
|
|
// ("otherBlockCount-1": -1 for lowerBits block)
|
|
memset(&(this->higherBits[otherBlockCount-1]), 0, numBlocksDiff * BITSTORE_BLOCK_SIZE);
|
|
}
|
|
}
|