RosettaCodeData/Task/Bitwise-IO/D/bitwise-io.d

134 lines
3.2 KiB
D

import std.stdio: File;
import core.stdc.stdio: FILE, fputc, fgetc;
import std.string: representation;
/***********
Bitwise I/O, the file must be in binary mode, and its FILE*
must be kept open during the usage of BitwiseFile.
*/
struct BitwiseFile {
FILE* fp;
uint accu;
int bits;
this(File f)
in {
assert(f.isOpen);
//assert(f.isBinary);
} body {
this.fp = f.getFP();
}
void write(const(ubyte)[] buf, size_t nBits, size_t shift)
nothrow {
auto accu = this.accu;
auto bits = this.bits;
auto bufPtr = buf.ptr;
bufPtr += shift / 8;
shift %= 8;
while (nBits || bits >= 8) {
while (bits >= 8) {
bits -= 8;
fputc(accu >> bits, this.fp);
accu &= (1 << bits) - 1;
}
while (bits < 8 && nBits) {
accu = (accu << 1) |
(((128 >> shift) & *bufPtr) >> (7 - shift));
nBits--;
bits++;
shift++;
if (shift == 8) {
shift = 0;
bufPtr++;
}
}
}
this.accu = accu;
this.bits = bits;
}
size_t read(ubyte[] buf, size_t nBits, size_t shift) nothrow {
auto accu = this.accu;
auto bits = this.bits;
auto bufPtr = buf.ptr;
int i = 0;
bufPtr += shift / 8;
shift %= 8;
while (nBits) {
while (bits && nBits) {
immutable mask = 128u >> shift;
if (accu & (1 << (bits - 1)))
*bufPtr |= mask;
else
*bufPtr &= ~mask;
nBits--;
bits--;
shift++;
if (shift >= 8) {
shift = 0;
bufPtr++;
}
}
if (!nBits)
break;
accu = (accu << 8) | fgetc(this.fp);
bits += 8;
}
this.accu = accu;
this.bits = bits;
return i;
}
void detach() nothrow {
if (this.bits) {
this.accu <<= 8 - this.bits;
fputc(this.accu, this.fp);
}
this.fp = null;
this.accu = 0;
this.bits = 0;
}
nothrow ~this() {
detach;
}
}
void main() { // Demo code.
import core.stdc.stdio: fopen, fclose;
import std.string: assumeUTF;
import std.stdio: writeln;
immutable data = "abcdefghijk".representation;
enum n = data.length;
// For each ubyte in data, write 7 bits skipping 1.
auto fout = File("bitwise_io_test.bin", "wb");
auto bf1 = BitwiseFile(fout);
foreach (immutable i; 0 .. n)
bf1.write(data[i .. $], 7, 1);
bf1.detach();
fout.close();
// Read 7 bits and expand to each ubyte of result skipping 1 bit.
ubyte[n + 1] result = '\0';
auto fin = File("bitwise_io_test.bin", "rb");
auto bf2 = BitwiseFile(fin);
foreach (immutable i; 0 .. n)
bf2.read(result[i .. $], 7, 1);
bf2.detach();
fin.close();
// Should be the same chars as 'data'.
result.assumeUTF.writeln;
}