2012-06-11 13:57:03 +01:00
|
|
|
#ifndef BITSET_H
|
|
|
|
#define BITSET_H
|
|
|
|
|
|
|
|
#include "util.h"
|
2012-05-17 20:14:22 +01:00
|
|
|
|
2012-05-18 13:24:35 +01:00
|
|
|
#include <inttypes.h>
|
2012-05-17 20:14:22 +01:00
|
|
|
#include <string.h>
|
2012-05-20 14:38:46 +01:00
|
|
|
#include <pthread.h>
|
|
|
|
|
2012-05-17 20:14:22 +01:00
|
|
|
|
2013-07-24 10:34:22 +01:00
|
|
|
static inline char char_with_bit_set(uint64_t num) { return 1<<(num%8); }
|
2012-05-18 13:24:35 +01:00
|
|
|
|
2012-05-29 04:03:28 +01:00
|
|
|
/** Return 1 if the bit at ''idx'' in array ''b'' is set */
|
2013-07-24 10:34:22 +01:00
|
|
|
static inline int bit_is_set(char* b, uint64_t idx) {
|
2012-05-17 20:14:22 +01:00
|
|
|
return (b[idx/8] & char_with_bit_set(idx)) != 0;
|
|
|
|
}
|
2012-05-29 04:03:28 +01:00
|
|
|
/** Return 1 if the bit at ''idx'' in array ''b'' is clear */
|
2013-07-24 10:34:22 +01:00
|
|
|
static inline int bit_is_clear(char* b, uint64_t idx) {
|
2012-05-17 20:14:22 +01:00
|
|
|
return !bit_is_set(b, idx);
|
|
|
|
}
|
2012-05-29 04:03:28 +01:00
|
|
|
/** Tests whether the bit at ''idx'' in array ''b'' has value ''value'' */
|
2013-07-24 10:34:22 +01:00
|
|
|
static inline int bit_has_value(char* b, uint64_t idx, int value) {
|
2012-06-11 14:34:17 +01:00
|
|
|
if (value) { return bit_is_set(b, idx); }
|
|
|
|
else { return bit_is_clear(b, idx); }
|
2012-05-18 13:24:35 +01:00
|
|
|
}
|
2012-05-29 04:03:28 +01:00
|
|
|
/** Sets the bit ''idx'' in array ''b'' */
|
2013-07-24 10:34:22 +01:00
|
|
|
static inline void bit_set(char* b, uint64_t idx) {
|
2012-05-18 13:24:35 +01:00
|
|
|
b[idx/8] |= char_with_bit_set(idx);
|
2012-05-20 14:38:46 +01:00
|
|
|
//__sync_fetch_and_or(b+(idx/8), char_with_bit_set(idx));
|
2012-05-17 20:14:22 +01:00
|
|
|
}
|
2012-05-29 04:03:28 +01:00
|
|
|
/** Clears the bit ''idx'' in array ''b'' */
|
2013-07-24 10:34:22 +01:00
|
|
|
static inline void bit_clear(char* b, uint64_t idx) {
|
2012-05-17 20:14:22 +01:00
|
|
|
b[idx/8] &= ~char_with_bit_set(idx);
|
2012-05-20 14:38:46 +01:00
|
|
|
//__sync_fetch_and_nand(b+(idx/8), char_with_bit_set(idx));
|
2012-05-17 20:14:22 +01:00
|
|
|
}
|
2012-05-29 04:03:28 +01:00
|
|
|
/** Sets ''len'' bits in array ''b'' starting at offset ''from'' */
|
2013-07-24 11:19:52 +01:00
|
|
|
static inline void bit_set_range(char* b, uint64_t from, uint64_t len)
|
|
|
|
{
|
|
|
|
for ( ; from%8 != 0 && len > 0 ; len-- ) {
|
|
|
|
bit_set( b, from++ );
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len >= 8) {
|
|
|
|
memset(b+(from/8), 255, len/8 );
|
|
|
|
from += len;
|
|
|
|
len = (len%8);
|
|
|
|
from -= len;
|
|
|
|
}
|
|
|
|
|
|
|
|
for ( ; len > 0 ; len-- ) {
|
|
|
|
bit_set( b, from++ );
|
|
|
|
}
|
2012-05-17 20:14:22 +01:00
|
|
|
}
|
2012-05-29 04:03:28 +01:00
|
|
|
/** Clears ''len'' bits in array ''b'' starting at offset ''from'' */
|
2013-07-24 11:19:52 +01:00
|
|
|
static inline void bit_clear_range(char* b, uint64_t from, uint64_t len)
|
|
|
|
{
|
|
|
|
for ( ; from%8 != 0 && len > 0 ; len-- ) {
|
|
|
|
bit_clear( b, from++ );
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len >= 8) {
|
2013-07-25 10:58:50 +01:00
|
|
|
memset(b+(from/8), 0, len/8 );
|
2013-07-24 11:19:52 +01:00
|
|
|
from += len;
|
|
|
|
len = (len%8);
|
|
|
|
from -= len;
|
|
|
|
}
|
|
|
|
|
|
|
|
for ( ; len > 0 ; len-- ) {
|
|
|
|
bit_clear( b, from++ );
|
|
|
|
}
|
2012-05-17 20:14:22 +01:00
|
|
|
}
|
|
|
|
|
2013-07-23 17:22:23 +01:00
|
|
|
/** Counts the number of contiguous bits in array ''b'', starting at ''from''
|
2012-05-29 04:03:28 +01:00
|
|
|
* up to a maximum number of bits ''len''. Returns the number of contiguous
|
2013-08-09 16:49:38 +01:00
|
|
|
* bits that are the same as the first one specified. If ''run_is_set'' is
|
|
|
|
* non-NULL, the value of that bit is placed into it.
|
2012-05-29 04:03:28 +01:00
|
|
|
*/
|
2013-08-09 16:49:38 +01:00
|
|
|
static inline uint64_t bit_run_count(char* b, uint64_t from, uint64_t len, int *run_is_set) {
|
2013-07-24 12:03:24 +01:00
|
|
|
uint64_t* current_block;
|
|
|
|
uint64_t count = 0;
|
2012-05-18 13:24:35 +01:00
|
|
|
int first_value = bit_is_set(b, from);
|
|
|
|
|
2013-08-09 16:49:38 +01:00
|
|
|
if ( run_is_set != NULL ) {
|
|
|
|
*run_is_set = first_value;
|
|
|
|
}
|
|
|
|
|
2013-07-24 12:03:24 +01:00
|
|
|
for ( ; (from+count) % 64 != 0 && len > 0; len--) {
|
|
|
|
if (bit_has_value(b, from+count, first_value)) {
|
2012-05-18 13:24:35 +01:00
|
|
|
count++;
|
2013-07-24 12:03:24 +01:00
|
|
|
} else {
|
2012-05-18 13:24:35 +01:00
|
|
|
return count;
|
2013-07-24 12:03:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for ( ; len >= 64 ; len -= 64 ) {
|
|
|
|
current_block = (uint64_t*) (b + ((from+count)/8));
|
|
|
|
if (*current_block == ( first_value ? UINT64_MAX : 0 ) ) {
|
2012-05-18 13:24:35 +01:00
|
|
|
count += 64;
|
2013-07-24 12:03:24 +01:00
|
|
|
} else {
|
2012-05-18 13:24:35 +01:00
|
|
|
break;
|
2013-07-24 12:03:24 +01:00
|
|
|
}
|
2012-05-18 13:24:35 +01:00
|
|
|
}
|
2013-07-24 12:03:24 +01:00
|
|
|
|
|
|
|
for ( ; len > 0; len-- ) {
|
|
|
|
if ( bit_has_value(b, from+count, first_value) ) {
|
2013-07-23 17:22:23 +01:00
|
|
|
count++;
|
2013-07-24 12:03:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-18 13:24:35 +01:00
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2013-07-23 17:22:23 +01:00
|
|
|
/** An application of a bitset - a bitset mapping represents a file of ''size''
|
2012-05-29 04:03:28 +01:00
|
|
|
* broken down into ''resolution''-sized chunks. The bit set is assumed to
|
2012-10-07 21:55:01 +01:00
|
|
|
* represent one bit per chunk. We also bundle a lock so that the set can be
|
|
|
|
* written reliably by multiple threads.
|
2012-05-29 04:03:28 +01:00
|
|
|
*/
|
2012-05-20 14:38:46 +01:00
|
|
|
struct bitset_mapping {
|
2012-10-07 21:55:01 +01:00
|
|
|
pthread_mutex_t lock;
|
2012-05-20 14:38:46 +01:00
|
|
|
uint64_t size;
|
|
|
|
int resolution;
|
|
|
|
char bits[];
|
|
|
|
};
|
|
|
|
|
2012-05-29 04:03:28 +01:00
|
|
|
/** Allocate a bitset_mapping for a file of the given size, and chunks of the
|
|
|
|
* given resolution.
|
|
|
|
*/
|
2012-05-20 14:38:46 +01:00
|
|
|
static inline struct bitset_mapping* bitset_alloc(
|
2013-07-23 17:22:23 +01:00
|
|
|
uint64_t size,
|
2012-05-20 14:38:46 +01:00
|
|
|
int resolution
|
|
|
|
)
|
|
|
|
{
|
|
|
|
struct bitset_mapping *bitset = xmalloc(
|
|
|
|
sizeof(struct bitset_mapping)+
|
|
|
|
(size+resolution-1)/resolution
|
|
|
|
);
|
|
|
|
bitset->size = size;
|
|
|
|
bitset->resolution = resolution;
|
2012-10-07 21:55:01 +01:00
|
|
|
/* don't actually need to call pthread_mutex_destroy '*/
|
|
|
|
pthread_mutex_init(&bitset->lock, NULL);
|
2012-05-20 14:38:46 +01:00
|
|
|
return bitset;
|
|
|
|
}
|
|
|
|
|
2013-09-11 14:41:59 +01:00
|
|
|
static inline void bitset_free( struct bitset_mapping * set )
|
|
|
|
{
|
|
|
|
/* TODO: free our mutex... */
|
|
|
|
free( set );
|
|
|
|
}
|
|
|
|
|
2012-05-20 14:38:46 +01:00
|
|
|
#define INT_FIRST_AND_LAST \
|
2013-07-24 10:34:22 +01:00
|
|
|
uint64_t first = from/set->resolution, \
|
2013-07-24 17:42:08 +01:00
|
|
|
last = ((from+len)-1)/set->resolution, \
|
|
|
|
bitlen = (last-first)+1
|
2012-05-20 14:38:46 +01:00
|
|
|
|
2012-10-07 21:55:01 +01:00
|
|
|
#define BITSET_LOCK \
|
|
|
|
FATAL_IF_NEGATIVE(pthread_mutex_lock(&set->lock), "Error locking bitset")
|
|
|
|
|
|
|
|
#define BITSET_UNLOCK \
|
|
|
|
FATAL_IF_NEGATIVE(pthread_mutex_unlock(&set->lock), "Error unlocking bitset")
|
|
|
|
|
|
|
|
|
2012-05-29 04:03:28 +01:00
|
|
|
/** Set the bits in a bitset which correspond to the given bytes in the larger
|
|
|
|
* file.
|
|
|
|
*/
|
2012-05-20 14:38:46 +01:00
|
|
|
static inline void bitset_set_range(
|
2013-07-23 17:22:23 +01:00
|
|
|
struct bitset_mapping* set,
|
|
|
|
uint64_t from,
|
2012-05-20 14:38:46 +01:00
|
|
|
uint64_t len)
|
|
|
|
{
|
|
|
|
INT_FIRST_AND_LAST;
|
2012-10-07 21:55:01 +01:00
|
|
|
BITSET_LOCK;
|
2012-05-20 14:38:46 +01:00
|
|
|
bit_set_range(set->bits, first, bitlen);
|
2012-10-07 21:55:01 +01:00
|
|
|
BITSET_UNLOCK;
|
2012-05-20 14:38:46 +01:00
|
|
|
}
|
|
|
|
|
2012-06-22 10:05:41 +01:00
|
|
|
|
|
|
|
/** Set every bit in the bitset. */
|
|
|
|
static inline void bitset_set(
|
|
|
|
struct bitset_mapping* set
|
|
|
|
)
|
|
|
|
{
|
|
|
|
bitset_set_range(set, 0, set->size);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-07-23 17:22:23 +01:00
|
|
|
/** Clear the bits in a bitset which correspond to the given bytes in the
|
2012-05-29 04:03:28 +01:00
|
|
|
* larger file.
|
|
|
|
*/
|
2012-05-20 14:38:46 +01:00
|
|
|
static inline void bitset_clear_range(
|
2013-07-23 17:22:23 +01:00
|
|
|
struct bitset_mapping* set,
|
|
|
|
uint64_t from,
|
2012-05-20 14:38:46 +01:00
|
|
|
uint64_t len)
|
|
|
|
{
|
|
|
|
INT_FIRST_AND_LAST;
|
2012-10-07 21:55:01 +01:00
|
|
|
BITSET_LOCK;
|
2012-05-20 14:38:46 +01:00
|
|
|
bit_clear_range(set->bits, first, bitlen);
|
2012-10-07 21:55:01 +01:00
|
|
|
BITSET_UNLOCK;
|
2012-05-20 14:38:46 +01:00
|
|
|
}
|
|
|
|
|
2012-06-22 10:05:41 +01:00
|
|
|
|
|
|
|
/** Clear every bit in the bitset. */
|
|
|
|
static inline void bitset_clear(
|
|
|
|
struct bitset_mapping *set
|
|
|
|
)
|
|
|
|
{
|
|
|
|
bitset_clear_range(set, 0, set->size);
|
|
|
|
}
|
|
|
|
|
2013-08-09 16:49:38 +01:00
|
|
|
/** As per bitset_run_count but also tells you whether the run it found was set
|
|
|
|
* or unset, atomically.
|
2012-05-29 04:03:28 +01:00
|
|
|
*/
|
2013-08-09 16:49:38 +01:00
|
|
|
static inline uint64_t bitset_run_count_ex(
|
2013-07-23 17:22:23 +01:00
|
|
|
struct bitset_mapping* set,
|
|
|
|
uint64_t from,
|
2013-08-09 16:49:38 +01:00
|
|
|
uint64_t len,
|
|
|
|
int* run_is_set
|
|
|
|
)
|
2012-05-20 14:38:46 +01:00
|
|
|
{
|
2013-07-24 10:34:22 +01:00
|
|
|
uint64_t run;
|
|
|
|
|
|
|
|
/* Clip our requests to the end of the bitset, avoiding uint underflow. */
|
|
|
|
if ( from > set->size ) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
len = ( len + from ) > set->size ? ( set->size - from ) : len;
|
|
|
|
|
2012-05-20 14:38:46 +01:00
|
|
|
INT_FIRST_AND_LAST;
|
2013-07-24 17:42:08 +01:00
|
|
|
|
2012-10-07 21:55:01 +01:00
|
|
|
BITSET_LOCK;
|
2013-08-09 16:49:38 +01:00
|
|
|
run = bit_run_count(set->bits, first, bitlen, run_is_set) * set->resolution;
|
2013-07-24 17:42:08 +01:00
|
|
|
run -= (from % set->resolution);
|
2013-08-09 16:49:38 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
2012-10-07 21:55:01 +01:00
|
|
|
BITSET_UNLOCK;
|
2013-07-24 17:42:08 +01:00
|
|
|
|
2012-10-07 21:55:01 +01:00
|
|
|
return run;
|
2012-05-20 14:38:46 +01:00
|
|
|
}
|
|
|
|
|
2013-08-09 16:49:38 +01:00
|
|
|
/** Counts the number of contiguous bytes that are represented as a run in
|
|
|
|
* the bit field.
|
|
|
|
*/
|
|
|
|
static inline uint64_t bitset_run_count(
|
|
|
|
struct bitset_mapping* set,
|
|
|
|
uint64_t from,
|
|
|
|
uint64_t len)
|
|
|
|
{
|
|
|
|
return bitset_run_count_ex( set, from, len, NULL );
|
|
|
|
}
|
|
|
|
|
2012-05-29 04:03:28 +01:00
|
|
|
/** Tests whether the bit field is clear for the given file offset.
|
|
|
|
*/
|
2012-05-20 14:38:46 +01:00
|
|
|
static inline int bitset_is_clear_at(
|
|
|
|
struct bitset_mapping* set,
|
|
|
|
uint64_t at
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return bit_is_clear(set->bits, at/set->resolution);
|
|
|
|
}
|
|
|
|
|
2012-05-29 04:03:28 +01:00
|
|
|
/** Tests whether the bit field is set for the given file offset.
|
|
|
|
*/
|
2012-05-20 14:38:46 +01:00
|
|
|
static inline int bitset_is_set_at(
|
|
|
|
struct bitset_mapping* set,
|
|
|
|
uint64_t at
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return bit_is_set(set->bits, at/set->resolution);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-05-17 20:14:22 +01:00
|
|
|
#endif
|
|
|
|
|