Skip to content

Instantly share code, notes, and snippets.

@fjolnir
Created August 27, 2013 14:34
Show Gist options
  • Save fjolnir/6354354 to your computer and use it in GitHub Desktop.
Save fjolnir/6354354 to your computer and use it in GitHub Desktop.
#import <ObjFW/ObjFW.h>
@interface OFBuffer : OFObject {
size_t _length, _capacity;
void *_bytes;
BOOL _externalStorage, _freeWhenDone;
}
// This pointer is only guaranteed to be valid as long as the
// OFBuffer object remains unchanged.
@property(readonly) void *bytes;
@property size_t length;
+ (OFBuffer *)bufferWithLength:(size_t)length capacity:(size_t)capacity;
- (id)initWithLength:(size_t)length capacity:(size_t)capacity;
- (id)initWithBytes:(const void *)bytes length:(size_t)length;
- (id)initWithBytesNoCopy:(const void *)bytes length:(size_t)length freeWhenDone:(BOOL)freeWhenDone;
- (void)getBytes:(void *)outBuf range:(of_range_t)range;
- (void)getBytes:(void *)outBuf length:(size_t)length;
- (void)getBytes:(void *)outBuf;
- (void)appendBytes:(const void *)bytes length:(size_t)length;
- (void)appendBuffer:(OFBuffer *)buffer;
- (void)replaceBytesInRange:(of_range_t)range withBytes:(const void *)bytes;
- (void)replaceBytesInRange:(of_range_t)range withBytes:(const void *)bytes length:(size_t)bytesLength;
- (void)resetBytesInRange:(of_range_t)range;
@end
#import "OFBuffer.h"
#import <assert.h>
#import <string.h>
@interface OFBuffer ()
- (void)_ensureFit:(size_t)length;
@end
@implementation OFBuffer
@synthesize length=_length, bytes=_bytes;
+ (OFBuffer *)bufferWithLength:(size_t)length capacity:(size_t)capacity
{
return [[self alloc] initWithLength:length capacity:capacity];
}
- (id)initWithLength:(size_t)length capacity:(size_t)capacity
{
if((self = [super init])) {
_length = length;
_capacity = capacity == 0 ? length : capacity;
_bytes = malloc(_capacity);
}
return self;
}
- (id)initWithBytes:(const void *)bytes length:(size_t)length
{
if((self = [self initWithLength:0 capacity:length])) {
[self appendBytes:bytes length:length];
}
return self;
}
- (id)initWithBytesNoCopy:(const void *)bytes
length:(size_t)length
freeWhenDone:(BOOL)freeWhenDone
{
if((self = [super init])) {
_externalStorage = YES;
_freeWhenDone = freeWhenDone;
_length = length;
_capacity = length;
_bytes = (void *)bytes;
}
return self;
}
- (void)dealloc
{
if(!_externalStorage || (_externalStorage && _freeWhenDone))
free(_bytes);
}
- (void)_copyStorageIfNeeded
{
if(_externalStorage) {
void *extBuf = _bytes;
_bytes = malloc(_capacity);
if(_freeWhenDone)
free(extBuf);
_externalStorage = NO;
}
}
- (void *)bytes
{
[self _copyStorageIfNeeded];
return _bytes;
}
- (void)getBytes:(void *)outBuf range:(of_range_t)range
{
assert(range.location + range.length < _length);
memcpy(outBuf, _bytes + range.location, range.length);
}
- (void)getBytes:(void *)outBuf length:(size_t)length
{
[self getBytes:outBuf range:(of_range_t) { 0, length }];
}
- (void)getBytes:(void *)outBuf
{
[self getBytes:outBuf length:_length];
}
- (void)_ensureFit:(size_t)length;
{
[self _copyStorageIfNeeded];
if(length > _capacity) {
_capacity *= 1.5;
_bytes = realloc(_bytes, _capacity);
}
}
- (void)appendBytes:(const void *)bytes length:(size_t)length
{
size_t newLength = _length + length;
[self _ensureFit:length];
memcpy(_bytes + _length, bytes, length);
_length = newLength;
}
- (void)appendBuffer:(OFBuffer *)buffer
{
[self appendBytes:buffer.bytes length:buffer.length];
}
- (void)replaceBytesInRange:(of_range_t)range withBytes:(const void *)bytes
{
[self replaceBytesInRange:range withBytes:bytes length:range.length];
}
- (void)replaceBytesInRange:(of_range_t)range withBytes:(const void *)bytes length:(size_t)bytesLength
{
assert(range.location + range.length < _length);
size_t const newLength = MAX(_length, MAX(range.location+range.length, range.location+bytesLength));
[self _ensureFit:newLength];
if(range.length != bytesLength)
memmove(_bytes + range.location + bytesLength,
_bytes + range.location + range.length,
_length - range.location - range.length);
memcpy(_bytes + range.location, bytes, bytesLength);
}
- (void)resetBytesInRange:(of_range_t)range
{
assert(range.location+range.length < _length);
memset(_bytes + range.location, 0, range.length);
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment