Question

I have an instance of NSMutableData at hand, and I want to read from it using an input stream (Similar to Java's ByteArrayInputStream).

I noticed CFReadStream has a initForReadingWithData method, but I was not able to figure out how to use it.

can anyone provide a sample bit on how to use it, or how to otherwise have a memory input stream?

Was it helpful?

Solution 2

This is the solution I have reached:

NSMutableData *msg;
CFReadStreamRef in = CFReadStreamCreateWithBytesNoCopy(nil, [msg mutableBytes], [_msg length], kCFAllocatorNull);
CFReadStreamOpen(in);
// read here as stream using CFReadStreamRead
CFRelease(in);

OTHER TIPS

I noticed CFReadStream has a initForReadingWithData method, …

Huh? CFReadStream doesn't have methods; it has functions. NSInputStream has initWithData:.

For NSInputStream, you need to be the stream's delegate, open the stream, and respond to its delegate messages. Most of this is in the abstract class NSStream, which is the superclass of both NSInputStream and NSOutputStream.

You may find CFReadStream easier to use, since it lets you just call a function to read directly from the stream, without having to conform to asynchronous “don't-call-me-I'll-call-you” requirements. For reading from a network, I would advise against this, but since you're reading from a memory buffer, it should be harmless.

That said, the main reason to create a stream rather than just walk through the bytes yourself is to pass it to something that accepts any stream, including one that may read from the network. If you're implementing that side, you should do so asynchronously.

The answer to this question depends on whether you want to read the data and remove it similarly to the way that NSStream manages its data or if you want to do a soft read and want to be able to re-read the data again.

For the first approach the easiest way is to create an NSMutableData category that will take as a parameter the number of bytes to read and then have it return the data after removing it.

#import "NSMutableData+Queue.h"

@implementation NSMutableData (Queue)

- (NSData *)read:(NSUInteger)length
{
    if(length <= self.length && length > 0)
    {
        NSData *data = [self subdataWithRange:NSMakeRange(0, length)];
        [self replaceBytesInRange:NSMakeRange(0, length) withBytes:NULL length:0];
        return data;
    }

    return nil;
}

- (void)removeBytes:(NSUInteger)length
{
    if(length <= self.length && length > 0)
    {
        [self replaceBytesInRange:NSMakeRange(0, length) withBytes:NULL length:0];
    }
}

@end

The second approach would be to write a class that manages the NSData object and performs soft reads on it while maintaining a reference to its offset.

//
//  CUDataReader.m
//  Test
//
//  Created by Abel Duarte on 10/28/15.
//  Copyright © 2015 Abel Duarte. All rights reserved.
//

#import "CUDataReader.h"

@interface CUDataReader()
@property (nonatomic, assign) CUDataReaderEndianness endianness;
@property (nonatomic, retain) NSData *data;
@end

@implementation CUDataReader

#pragma mark - Init

- (id)initWithData:(NSData *)data
{
    self = [super init];
    if(self)
    {
        self.data = data;
        self.offset = 0;
        self.endianness = CUDataReaderDefaultEndian;
    }
    return self;
}

+ (id)dataReaderWithData:(NSData *)data
{
    return [[CUDataReader alloc] initWithData:data];
}

- (void)dealloc
{
    self.data = nil;
}

#pragma mark - Read

- (NSData *)read:(NSUInteger)length
{
    NSUInteger readLength = self.offset + length;

    if(readLength <= self.data.length && length > 0)
    {
        NSData *data = [self.data subdataWithRange:NSMakeRange(self.offset, length)];
        self.offset += length;

        return data;
    }

    return nil;
}

- (NSData *)readUntilData:(NSData *)data
{
    NSRange dataRange = [self.data rangeOfData:data
                                       options:0
                                         range:NSMakeRange(self.offset, self.data.length - self.offset)];

    if(dataRange.location != NSNotFound)
    {
        NSUInteger length = dataRange.location - self.offset;
        return [self read:length];
    }

    return nil;
}

- (NSData *)readUntilDelimeter:(char)delimeter
{
    NSData *data = [NSData dataWithBytes:&delimeter length:1];
    return [self readUntilData:data];
}

#pragma mark - Endianess

- (void)setEndianness:(CUDataReaderEndianness)endianness
{
    _endianness = endianness;
}

- (NSData *)endiannessDecodedData:(NSData *)data
{
    if(self.endianness == CUDataReaderLittleEndian)
    {
        return data;
    }
    else
    {
        NSMutableData *endiannessDecodedData = [NSMutableData data];

        NSUInteger length = data.length;
        char *bytes = (char *)[data bytes];

        for(NSInteger i = length - 1; i >= 0; i--)
        {
            char *byte = bytes + i;
            [endiannessDecodedData appendBytes:byte length:1];
        }

        return endiannessDecodedData;
    }

    return data;
}

#pragma mark - Type utilities

- (BOOL)readBoolean
{
    NSUInteger length = sizeof(BOOL);
    return *(BOOL *)[[self endiannessDecodedData:[self read:length]] bytes];
}

- (char)readCharacter
{
    NSUInteger length = sizeof(char);
    return *(char *)[[self endiannessDecodedData:[self read:length]] bytes];
}

- (short)readShort
{
    NSUInteger length = sizeof(short);
    return *(short *)[[self endiannessDecodedData:[self read:length]] bytes];
}

- (int)readInteger
{
    NSUInteger length = sizeof(int);
    return *(int *)[[self endiannessDecodedData:[self read:length]] bytes];
}

- (float)readFloat
{
    NSUInteger length = sizeof(float);
    return *(float *)[[self endiannessDecodedData:[self read:length]] bytes];
}

- (double)readDouble
{
    NSUInteger length = sizeof(double);
    return *(double *)[[self endiannessDecodedData:[self read:length]] bytes];
}

- (long)readLong
{
    NSUInteger length = sizeof(long);
    return *(long *)[[self endiannessDecodedData:[self read:length]] bytes];
}

#pragma mark - Offset

- (void)rewind
{
    self.offset = 0;
}

- (void)rewind:(NSUInteger)length
{
    if(length <= self.offset)
    {
        self.offset -= length;
    }
    else
    {
        self.offset = 0;
    }
}

- (void)setOffset:(NSUInteger)offset
{
    if(offset <= self.data.length)
        _offset = offset;
}

- (void)skip:(NSUInteger)offset
{
    NSUInteger x = self.offset + offset;
    [self setOffset:x];
}

- (NSUInteger)bytesRemaining
{
    return self.data.length - self.offset;
}

@end

Please take a look at https://github.com/abelduarte/CoreUtilities for additional information.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top