How to pass const void *bytes to an Objective-C block from Swift - objective-c

I'm writing some Swift (1.2) code that calls an Objective-C library (transcribing some earlier Objective-C code). It's not clear how it's supposed to work. I've tried a bunch of variations, and nothing satisfies the compiler. Here's the Objective-C code I started with:
// Objective-C
NSData *fileData = //
const void *bytes = fileData.bytes;
unsigned int bufferSize = 1024; //Arbitrary
[object writeDataWithBlock:
^BOOL(BOOL(^writeData)(const void *bytes, unsigned int length), NSError**(actionError)) {
for (NSUInteger offset = 0; offset <= fileData.length; offset += bufferSize) {
unsigned int size = (unsigned int)MIN(fileData.length - i, bufferSize);
writeData(&bytes[i], size);
}
return YES;
}];
So far, I've gotten as far as this Swift code:
// Swift
let fileData = //
let ptr = UnsafePointer<UInt8>(fileData.bytes)
let bufferSize = 1024 // Arbitrary
object.writeDataWithBlock() { (writeData, actionError) -> Bool in
for var offset = 0; offset <= fileData.length; offset += bufferSize {
let size = CUnsignedInt(min(fileData.length - offset, bufferSize))
writeData(UnsafePointer<Void>(ptr[offset]), size)
}
return true
}
That's giving me this error:
Cannot find an initializer for type 'UnsafePointer' that accepts an arguments list of type '(UInt8)'
When I remove the UnsafePointer<Void> conversion to do a more direct translation, yielding this line:
writeData(&ptr[offset], size)
I get this error, pointing at the & character:
Type of expression is ambiguous without more context
Without the &, it yields a UInt8, giving me this error:
Cannot invoke 'writeData' with an argument list of type '(Uint8, UInt32)'
What do I need to do to read the bytes out of the NSData sequentially and pass them onto another method?

Pointer arithmetic should (and does) get you the results you're looking for.
let fileData = //
let bufferSize = 1024 // Arbitrary
object.writeDataWithBlock() { (writeData, actionError) -> Bool in
for var offset = 0; offset <= fileData.length; offset += bufferSize {
let size = CUnsignedInt(min(fileData.length - offset, bufferSize))
writeData(fileData.bytes + offset, size)
}
return true
}

Related

obj-c to Swift convert code

So I'm trying to convert some obj-c code into swift but I'm stuck on a part of it.
How can I do something like this in swift?
- (void)sometMethod:(NSString *)s {
NSString * crlfString = [s stringByAppendingString:#"\r\n"];
uint8_t *buf = (uint8_t *)[crlfString UTF8String];}
The real problem is this line
uint8_t *buf = (uint8_t *)[crlfString UTF8String];
What do you want to do with the UTF-8 buffer? Generally, it is more convenient to convert the string directly to an NSData object. But you can also translate your Obj-C code one by one to Swift. Here are both variants:
import Foundation
func appendCRLFAndConvertToUTF8_1(s: String) -> NSData {
let crlfString: NSString = s.stringByAppendingString("\r\n")
let buffer = crlfString.UTF8String
let bufferLength = crlfString.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
let data = NSData(bytes: buffer, length: bufferLength)
return data;
}
func appendCRLFAndConvertToUTF8_2(s: String) -> NSData {
let crlfString = s + "\r\n"
return crlfString.dataUsingEncoding(NSUTF8StringEncoding)!
}
let s = "Hello 😄"
let data1 = appendCRLFAndConvertToUTF8_1(s)
data1.description
let data2 = appendCRLFAndConvertToUTF8_2(s)
data2.description
data1 == data2
And if you want to iterate over the UTF-8 code units and not deal with a buffer, use something like:
for codeUnit in String.utf8 {
println(codeUnit)
}

How to return generated array from c function to objective-c

Need to generated some random 10 byte length string in c function and call the function from objective-c. So, I'm creating a pointer to uint8_t and passing it to C function. The function generates random bytes and assigns them to *randomString. However, after returning from function to objective-c randomValue pointer points to NULL.
Here's my random function in C:
void randomString(uint8_t *randomString)
{
randomString = malloc(10);
char randomByte;
char i;
for (i = 0; i < 10; i++) {
srand((unsigned)time(NULL));
randomByte = (rand() % 255 ) + 1;
*randomString = randomByte;
randomString++;
}
}
Here's objective-c part:
uint8_t *randomValue = NULL;
randomString(randomValue); //randomValue points to 0x000000
NSString *randomString = [[NSString alloc] initWithBytes:randomValue length:10 encoding:NSASCIIStringEncoding];
NSLog(#"Random string: %#", randomString);
A more natural semantic, like malloc() itself would be:
uint8_t * randomString()
{
uint8_t *randomString = malloc(10);
srand((unsigned)time(NULL));
for (unsigned i = 0; i < 10; i++)
randomString[i] = (rand() % 254) + 1;
return randomString;
}
Pointers are passed by value, so randomValue will remain NULL after the call of randomString. You need to pass a pointer to a pointer in order to make it work:
void randomString(uint8_t **randomString) {
*randomString = malloc(10);
// ... the rest of your code goes here, with an extra level of indirection
}
uint8_t *randomValue = NULL;
randomString(&randomValue);
You probably should be using uint8_t **randomeValue instead of uint8_t *.

How do I convert a Hexa-Tri-Decimal number into an int in objective c?

The Hexa-Tri-Decimal number is 0-9 and A-Z. I know I can covert from hex with a NSScanner but not sure how to go about converting Hexa-Tri-Decimal.
For example I have a NSString with "0XPM" the int value should be 43690, "1BLC" would be 61680.
Objective C is built on top of C, and luckily enough you can use the functions there to accomplish the conversion. What you're looking for is strtol or one of it's sibling functions. If I recall correctly strtol handles up to base36 (the hexa-tri-decimal you refer to).
http://www.cplusplus.com/reference/clibrary/cstdlib/strtol/
I can only think to do this using C strings, as they offer easier access to individual characters.
This seemed like an interesting problem to solve, so I had a go at writing it:
int parseBase36Number(NSString *input)
{
const char *inputCString = [[input lowercaseString] UTF8String];
size_t inputLength = [input length];
int orderOfMagnitudeMultiplier = 1;
int result = 0;
// iterate backward through the number
for (int i = inputLength - 1; i >= 0; i--)
{
char inputChar = inputCString[i];
int charNumericValue;
if (isdigit(inputChar))
{
charNumericValue = inputChar - '0';
}
else if (islower(inputChar))
{
charNumericValue = inputChar - 'a' + 10;
}
else
{
// unhanded character, throw error
}
result += charNumericValue * orderOfMagnitudeMultiplier;
orderOfMagnitudeMultiplier *= 36;
}
return result;
}
NOTE: I've not tested this at all, so take care and let me know how it goes!

EXC_BAD_ACCESS when passing a pointer-to-pointer in a struct?

I've got a c-array of CGPoints in a struct. I need to replace this array when another CGPoint is added. I'd swear I'm doing this right and it seems to work fine a few times but eventually I'll get a EXC_BAD_ACCESS. What am I missing?
Here's the struct, which I've truncated to remove a lot of items that don't pertain.
typedef struct{
CGPoint **focalPoints;
NSUInteger focalPointCount;
CGRect boundingRect;
}FocalPoints;
Here's how I initialize it:
CGPoint *fPoints = (CGPoint *)malloc(sizeof(CGPoint));
FocalPoints focalInfo = {&fPoints, 0, rect};
Note that focalInfo is passed by reference to another function, like so: anotherFunction(&focalInfo).
Now here's the function that replaces the Points array with a new one:
void AddFocalPoint (CGPoint focalPoint, FocalPoints *focal){
if (focalPoint.x == CGFLOAT_MAX) return;
if (!CGRectContainsPoint(focal->boundingRect, focalPoint)) return;
int origCount = focal->focalPointCount;
int newCount = origCount + 1;
CGPoint *newPoints = (CGPoint *) malloc((newCount) * sizeof(CGPoint));
for (int i = 0; i < newCount; i++)
newPoints[i] = (i < origCount) ? *focal->focalPoints[i] : focalPoint; //error occurs here
free(*focal->focalPoints);
*focal->focalPoints = newPoints;
focal->focalPointCount = newCount;
}
The EXC_BAD_ACCESS error occurs in the above code on line 8: newPoints[i] = (i < origCount) ? *focal->focalPoints[i] : focalPoint;. So what exactly am I doing wrong?
This is a bit of a long shot, but maybe there's an issue with operator priority in *focal->focalPoints[i]. Have you try adding parentheses according to what you are trying to achieve ?
I believe the issue comes with where GCPoint *fPoints allocated as &fPoints evaluates to an address of that ... which is no longer valid once the function exits.
(The data to which it points was allocated fine with malloc.)
Aside from the suggestion I made in a comment, of using a linked list/NSMutableArray, my other suggestion would be that you use realloc() instead of constantly using malloc(), copying by hand, and then free()ing the old allocation.
void * realloc(void *ptr, size_t size);
The realloc() function tries to change the size of the allocation pointed to by ptr to size, and returns ptr. If there is not enough room to enlarge the memory allocation pointed to by ptr, realloc() creates a new allocation, copies as much of the old data pointed to by ptr as will fit to the new allocation, frees the old allocation, and returns a pointer to the allocated memory.
This is pretty much exactly what you are doing, but you can let the library handle it for you.
(May I also humbly suggest using the word "focal" slightly less to name variables in your function?) (Also also, I'm not really clear on why focalPoints in your struct is a pointer-to-pointer. You just want an array of structs -- a single pointer should be fine.)
Consider the following (somewhat extensive) rewrite; hope that it's helpful in some way.
typedef struct{
CGPoint *points; // Single pointer
NSUInteger count;
CGRect boundingRect;
} FocalPoints;
// Renamed to match Apple's style, like e.g. CGRectIntersectsRect()
void FocalPointsAddPoint (FocalPoints *, CGPoint);
void FocalPointsAddPoint (FocalPoints *f, CGPoint thePoint){
if (thePoint.x == CGFLOAT_MAX) return;
if (!CGRectContainsPoint(f->boundingRect, thePoint)) return;
NSUInteger origCount = f->count; // |count| is typed as NSUInteger; |origCount|
NSUInteger newCount = origCount + 1; // and |newCount| should be consistent
// Greatly simplified by using realloc()
f->points = (CGPoint *) realloc(f->points, newCount * sizeof(CGPoint));
(f->points)[newCount-1] = thePoint;
f->count = newCount;
}
int main(int argc, const char * argv[])
{
#autoreleasepool {
// Just for testing; any point should be inside this rect
CGRect maxRect = CGRectMake(0, 0, CGFLOAT_MAX, CGFLOAT_MAX);
// Can initialize |points| to NULL; both realloc() and free() know what to do
FocalPoints fp = (FocalPoints){NULL, 0, maxRect};
int i;
for( i = 0; i < 10; i++ ){
FocalPointsAddPoint(&fp, CGPointMake(arc4random() % 100, arc4random() % 100));
NSLog(#"%#", NSStringFromPoint(fp.points[i]));
}
}
return 0;
}

LLVM GCC 4.2 EXC_BAD_ACCESS

Below code runs just fine on GCC 4.2 but fails with EXC_BAD_ACCESS in LLVM GCC 4.2
- (double_t)readDouble {
double_t *dt = (double_t *)(buffer+offset);
double_t ret = *dt; // Program received signal: EXC_BAD_ACCESS
offset += 8;
return ret;
}
That's how I allocate
int dataLength = [data length];
buffer = malloc(dataLength + 1);
buffer[dataLength] = 0; // null terminate to log
[data getBytes:(void *)buffer length:[data length]];
//NSLog(#"%s", buffer);
Offset and buffer is like
#interface PRDataSet : NSObject {
NSMutableArray *tables;
NSMutableDictionary *tablesByName;
NSMutableDictionary *tablesById;
#private
NSURLConnection *conn;
int offset;
char *buffer;
}
Yes offset is within range.
I do not free the buffer before I use it.
Any ideas?
This could be an aligment problem. The ARM processors (and many other processors) have restrictions regarding the data alignment, e.g. they can only read and write floating-point numbers from addresses that are a multiple of 4 or 8.
From the way the buffer is allocated in your code, it might not be allocated properly, or your double_t data elements aren't aligned within the buffer.
In order to avoid the problem, you should try to first copy the data into an aligned buffer and read it from there.
LLVM just doesn't read float directly.
Here's the solution:
- (uint32_t)readUInt32 {
uint32_t ret = *(uint32_t *)(buffer+offset);
offset += 4;
return ret;
}
- (uint16_t)readUInt16 {
uint16_t ret = *(uint16_t *)(buffer+offset);
offset += 2;
return ret;
}
- (uint64_t)readUInt64 {
uint64_t ret = *(uint64_t *)(buffer+offset);
offset += 8;
return ret;
}
- (float_t)readSingle {
uint32_t t = [self readUInt32];
float_t ret = *((float_t *)(&t));
return ret;
}
- (double_t)readDouble {
uint64_t t = [self readUInt64];
double_t ret = *((double_t *)(&t));
return ret;
}