I'm reading measurement data that is saved in 9 bit binary form with no spacing, e.g.
101001110 001011100 001101010 101010110 100001011 etc.
The data is from old measurement equipment, and sometimes, I have to read in chunks of e.g. 10, 11, or 12 bits. I will always know what integer size to read as there's some file info in the beginning of the file.
But in iOS / Objective-C I know only how to get binary data down to the size of 8 bit bytes, e.g. Int8, int16 etc. And convert those 8- or 16-bit chunks to integers.
How do I step through say an NSData object in bit-wise predefined steps?
You'd have to write your own deserialiser. For example:
@interface CHBinaryStream
// init with an NSData from which values will be read
- (id)initWithData:(NSData *)data;
// e.g. readBits:9 will assemble 9 bits from the data stream,
// return them in the low 9 bits of an NSUInteger. If you
// ask for more bits than an NSUInteger can store then they'll
// just drop off the end. This will also advance the read pointer
// by 9 bits
- (NSUInteger)readBits:(int)numberOfBits;
@end
And:
@implementation CHBinaryStream
{
NSData *sourceData;
NSUInteger readPointer;
}
- (id)initWithData:(NSData *)data
{
self = [super init];
if(self)
{
sourceData = [data retain];
}
return self;
}
- (void)dealloc
{
[sourceData release], sourceData = nil;
[super dealloc];
}
- (NSUInteger)readBit
{
// we'll just return a stream of 0s if we
// go past the end of the source data
if((readPointer >> 3) >= [sourceData length]) return 0;
// otherwise we'll read the byte at:
//
// sourceData >> 3
//
// and return the bit at:
//
// sourceData & 7
//
// (where for our purposes, the 0th bit in a byte
// is the most significant)
uint8_t sourceByte = ((uint8_t *)[sourceData bytes])[sourceData >> 3];
sourceByte <<= sourceData&7; // the bit we want is now where the MSB was
sourceByte >>= 7; // the bit we want is now in the LSB
sourceByte &= 1; // this clears all the other bits
/*
Alternative: (sourceByte >> ((sourceData & 7) ^ 7))&1;
*/
// advance the read pointer, and then return the value
readPointer++;
return sourceByte;
}
- (NSUInteger)readBits:(int)numberOfBits
{
NSUInteger result = 0;
// shift in the required number of bits;
// since we're going to get the most significant
// bits first, we add incoming bits in the least
// significant location and shift upward
while(numberOfBits--)
result = (result << 1) | [self readBit];
return result;
}
@end
All untested, but hopefully correct. Notice that I'm counting bits in an NSUInteger, which from memory are 32 bits wide under iOS so the maximum file size it can cope with is 512mb. You could explicitly use a 64 bit long long if you wanted to go to 2 exabytes.
The obvious next thing to add would be a Boolean getter for whether the stream is finished.