views:

91

answers:

2

I'm working on transcoding image data from a file to a base64 encoded string and then back to bytes as the file is read using NSStream. I think I'm almost there, but I keep running into EXC_BAD_ACCESS at various points during the conversion.

I'm fairly new to the world of NSStream and buffers so feel free to let me know if I'm taking the absolute wrong approach here.

Here's what I've got so far:

// Copy the bytes from our file input stream buffer
void *base64buffer = malloc(self.buffer[self.bufferOffset]);

// Convert the bytes to NSData for the base64 encode
NSData *dataToEncode = [NSData dataWithBytesNoCopy:base64buffer length:sizeof(base64buffer) freeWhenDone:YES];

// Convert our NSData into a base64 encoded string
NSString *base64EncodedData = [dataToEncode base64EncodedString];

// Convert our base64 encoded string back into NSData
NSData *encodedData = [base64EncodedData dataUsingEncoding:NSUTF8StringEncoding];

// Write the bytes to our output stream
bytesWritten = [self.producerStream write:[encodedData bytes] maxLength:[encodedData length]];

// Clean up
dataToEncode = nil;
base64EncodedData = nil;
encodedData = nil;
free(base64buffer);
+2  A: 

Several issues here:

// Copy the bytes from our file input stream buffer
void *base64buffer = malloc(self.buffer[self.bufferOffset]);

This does not do what your comment says it does. What you have just done is allocated a buffer the same size as the size_t (4 byte or 8 byte unsigned integer) at self.buffer[self.bufferOffset] Basically you try to allocate a buffer of essentially random size. If the size is too big, you'll get NULL back and that is probably what is causing your crashes.

// Convert the bytes to NSData for the base64 encode
NSData *dataToEncode = [NSData dataWithBytesNoCopy:base64buffer length:sizeof(base64buffer) freeWhenDone:YES];

This does not do what you think it does. sizeof(base64buffer) is either 4 or 8 depending on whether you are compiling in 32 bit or 64 bit mode. It is the size of the pointer, not the size of the buffer.

free(base64buffer);

When you created the initial NSData, you said yes to "freeWhenDone". This means the NSData took ownership of base64buffer and will try to free it when it is deallocated. Since you have already freed it, that operation will fail in unpredictable nasty ways.


How can we fix this? Try something like this:

size_t bufferLengthInBytes = .... // you'll need to figure out how to get this

// Next, instead of mallocing my own buffer for the data, I'll let the runtime do it for me.
// I'm assuming that self.buffer is a pointer to the bytes you want to copy.
NSData* dataToEncode = [NSData dataWithBytes: self.buffer length:bufferLengthInBytes];

// I'm not sure where the method base64EncodedString comes from.  I assume you have a category to do this.
NSString *base64EncodedString = [dataToEncode base64EncodedString];

The rest of the method should be the same except you don't have a buffer to free anymore (in fact you didn't before really).

JeremyP
Thank you. This worked beautifully. As for bufferLengthInBytes, I'm using:size_t bufferLengthInBytes = sizeof(self.buffer);Special thanks to Toro, your tips helped me along as well.
frsh
@frsh: A good way to express your gratitude to Toro is to up vote his answer.
JeremyP
+1  A: 
Toro