File: BitUtil.h

package info (click to toggle)
abyss 2.3.10-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 8,284 kB
  • sloc: cpp: 78,182; ansic: 6,512; makefile: 2,252; perl: 672; sh: 509; haskell: 412; python: 4
file content (160 lines) | stat: -rw-r--r-- 4,689 bytes parent folder | download | duplicates (6)
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
#ifndef BITUTILS_H
#define BITUTILS_H 1

#include "config.h"
#include <cstdlib> // for exit
#include <stdint.h>
#include <cassert>
#include <iostream>

enum BitwiseOp { BITWISE_OVERWRITE, BITWISE_OR, BITWISE_AND };

/** The return value of the CPUID instruction. */
struct CPUID { unsigned a, b, c, d; };

/** Return the result of the CPUID instruction
 * or -1 if it is not supported.
 */
static inline CPUID cpuid(unsigned op)
{
	CPUID x;
#if __GNUC__ && __x86_64__
	__asm__("cpuid" : "=a" (x.a), "=b" (x.b), "=c" (x.c), "=d" (x.d)
			: "a" (op));
	return x;
#else
	(void)op;
	x.a = x.b = x.c = x.d = static_cast<unsigned>(-1);
	return x;
#endif
}

/** Return whether this processor has the POPCNT instruction. */
static inline bool havePopcnt() { return cpuid(1).c & (1 << 23); }

static const bool hasPopcnt = havePopcnt();

/** Return the Hamming weight of x. */
static inline uint64_t popcount(uint64_t x)
{
#if HAVE_POPCNT && __GNUC__ && __x86_64__
	if (hasPopcnt) {
		__asm__("popcnt %1,%0" : "=r" (x) : "r" (x));
		return x;
	}
#endif
	x = (x & 0x5555555555555555ULL) +
		((x >> 1) & 0x5555555555555555ULL);
	x = (x & 0x3333333333333333ULL) +
		((x >> 2) & 0x3333333333333333ULL);
	x = (x + (x >> 4)) & 0x0f0f0f0f0f0f0f0fULL;
	x = x + (x >>  8);
	x = x + (x >> 16);
	x = x + (x >> 32);
	return x & 0x7FLLU;
}

/**
 * Memory copy with bit-level resolution.
 *
 * @param src source data to copy (byte array)
 * @param dest destination of copied data (byte array)
 * @param bits number of bits to copy from src, starting from
 *        first bit of first byte of src
 * @param bitOffset bit offset into dest
 * @param op how to combine existing bits in dest with bits
 *        src (options: BITWISE_OVERWRITE, BITWISE_OR,
 *        BITWISE_AND)
 */
static inline void copyBits(char* src, char* dest, size_t bits,
	size_t bitOffset = 0, BitwiseOp op = BITWISE_OVERWRITE)
{
	size_t bytes = (bits + 7) / 8;
	size_t fullBytes = (bits % 8 == 0) ? bytes : bytes - 1;
	size_t byteOffset = bitOffset / 8;
	unsigned char shift = bitOffset % 8;
	unsigned char carryMask = 0xFF << (8 - shift);
	for (size_t i = 0; i < fullBytes; i++) {
		if (op == BITWISE_OVERWRITE) {
			dest[byteOffset + i] &= carryMask;
			dest[byteOffset + i + 1] &= ~carryMask;
		}
		if (op == BITWISE_AND) {
			dest[byteOffset + i] &= src[i] >> shift | carryMask;
			dest[byteOffset + i + 1] &= src[i] << (8 - shift) | ~carryMask;
		}
		else {
			dest[byteOffset + i] |= src[i] >> shift;
			dest[byteOffset + i + 1] |= src[i] << (8 - shift);
		}
	}
	if (fullBytes < bytes) {
		unsigned char bitsInLastByte = bits % 8;
		unsigned char lastByteMask = 0xFF << (8 - bitsInLastByte);
		unsigned char lastCarryMask = lastByteMask << (8 - shift);
		char lastByte = src[bytes - 1];
		lastByte &= lastByteMask;
		size_t lastByteIndex = byteOffset + bytes - 1;
		if (op == BITWISE_OVERWRITE)
			dest[lastByteIndex] &= ~(lastByteMask >> shift);
		if (op == BITWISE_AND)
			dest[lastByteIndex] &= lastByte >> shift | ~(lastByteMask >> shift);
		else
			dest[lastByteIndex] |= lastByte >> shift;
		if (lastCarryMask > 0) {
			if (op == BITWISE_OVERWRITE)
				dest[lastByteIndex + 1] &= ~lastCarryMask;
			if (op == BITWISE_AND)
				dest[lastByteIndex + 1] &= lastByte << (8 - shift) | ~lastCarryMask;
			else
				dest[lastByteIndex + 1] |= lastByte << (8 - shift);
		}
	}
}

/**
 * Read data from a stream with bit-level resolution.
 *
 * @param in input byte stream
 * @param dest destination of copied data (byte array)
 * @param bits number of bits to read from in, starting from
 *        first bit of first byte of in
 * @param bitOffset bit offset into dest
 */
static inline void readBits(std::istream& in, char* dest, size_t bits,
	size_t bitOffset = 0, BitwiseOp op = BITWISE_OVERWRITE)
{
	(void)readBits;

	const size_t IO_BUFFER_SIZE = 32 * 1024;
	size_t byteOffset = bitOffset / 8;
	unsigned char shift = bitOffset % 8;

	if (op == BITWISE_OVERWRITE && shift == 0) {
		// Simple byte-aligned copy.
		size_t bytes = (bits + 7) / 8;
		size_t fullBytes = (bits % 8 == 0) ? bytes : bytes - 1;
		in.read(dest + byteOffset, fullBytes);
		assert(in);
		if (fullBytes < bytes) {
			char lastByte;
			in.read(&lastByte, 1);
			assert(in);
			copyBits(&lastByte, dest + byteOffset + fullBytes, bits % 8);
		}
	} else {
		// Non-byte-aligned copy. A portion of each src byte is
		// carried over into the next dest byte.
		char buffer[IO_BUFFER_SIZE];
		for(size_t i = 0; i < bits;) {
			size_t bitsRead = std::min(IO_BUFFER_SIZE * 8, bits - i);
			size_t bytesRead = (bitsRead + 7)/8;
			in.read(buffer, bytesRead);
			assert(in);
			copyBits(buffer, dest + byteOffset, bitsRead, i + shift, op);
			i += bitsRead;
		}
	}
}

#endif