I'm trying to parse some HTML. I use stringWithContentsOfURL to get the HTML. I attempt to load this into a character array so I can parse it, but I crash with the EXC_BAD_ACCESS error when getCString is called. Here is the relavent code:
- (void)parseStoryWithURL:(NSURL *)storyURL
{
_paragraphs = [[NSMutableArray alloc] initWithCapacity:10];
_read = NO;
NSError* error = nil;
NSString* originalFeed = [NSString stringWithContentsOfURL:storyURL encoding:NSUTF8StringEncoding error:&error];
_i = originalFeed.length;
char* entireFeed = malloc(_i*sizeof(char));
char* current = entireFeed;
char* lagger;
char* recentChars = malloc(7);
BOOL collectRecent = NO;
BOOL paragraphStarted = NO;
BOOL paragraphEnded = NO;
int recentIndex = 0;
int paragraphSize = 0;
NSLog(#"original Feed: %#", originalFeed);
_read = [originalFeed getCString:*entireFeed maxLength:_i encoding:NSUTF8StringEncoding];
I've also tried this passing the 'current' pointer to getCString but it behaves the same. From what I've read this error is typically thrown when you try to read from deallocated memory. I'm programming for iOS 5 with memory management. The line before that I print the HTML to the log and everything is fine. Help would be appreciated. I need to get past this error so I can test/debug my HTML parsing algorithms.
PS: If someone with enough reputation is allowed to, please add "getCString" as a tag. Apparently no one uses this function :(
There are several issues with your code - you're passing the wrong pointers and not reserving enough space. Probably the easiest is to use UTF8String instead:
char *entireFeed = strdup([originalFeed UTF8String]);
At the end you'll have to free the string with free(entireFeed) though. If you don't modify it you can use
const char *entireFeed = [originalFeed UTF8String];
directly.
If you want to use getCString, you'll need to determine the length first - which has to include the termination character as well as extra space for encoded characters, so something like:
NSUInteger len = [originalFeed lengthOfBytesUsingEncoding: NSUTF8StringEncoding] + 1;
char entireFeed[len];
[originalFeed getCString:entireFeed maxLength:len encoding:NSUTF8StringEncoding];
Try explicitly malloc'ing entireFeed with a length of _i (not 100% certain of this, as NSUTF8String might also include double byte unichars or wchars) instead of the wacky char * entireFeed[_i] thing you're doing.
I can't imagine char * entireFeed[_i] is working at run-time (and instead, you're passing a NULL pointer to your getCString method).
A few strange things;
char* entireFeed[_i]; allocates an array of char*, not an array of char. I suspect you want char entireFeed[_i] or char *entireFeed = malloc(_i*sizeof(char));
getCString takes a char* as a first parameter, that is, you should send it entireFeed instead of *entireFeed.
Also, note that the (UTF-8) encoding may add bytes to the result, so allocating the buffer the exact size of the input may cause the method to return NO (buffer too small). You should really use [originalFeed UTF8String] instead.
Related
I have an iOS application that talks to a RedBearLab Arduino device. My code that I use to send an int via bluetooth from Arduino to iOS is as follows:
void sendMyInt(int myInt) {
char b[4];
String str;
str=String(myInt);
str.toCharArray(b,4);
for (int i; i < 3; i++) {
char toPrint = b[i];
ble_write(toPrint);
}
}
Here is my code on the receiving end:
-(void) bleDidReceiveData:(unsigned char *)data length:(int)length
{
NSData *d = [NSData dataWithBytes:data length:length];
NSLog([NSString stringWithFormat:#"%#",d]);
NSString *s = [[NSString alloc] initWithData:d encoding:NSUTF8StringEncoding];
const char* clearChar = "!";
if ([self.label.text isEqualToString:#"Label"]) {
self.label.text = #"";
}
else if ([s isEqualToString:[NSString stringWithUTF8String:clearChar]]) {
self.label.text = #"";
}
else {
NSString *store = self.label.text;
NSString *full = [NSString stringWithFormat:#"%#%#",store,s];
self.label.text = full;
}
}
The final else statement fires somewhat as expected, and a value of 233! is printed out to the label over and over again, getting longer each time eventually forming things like 233!233!233! etc. As you can see, I am using a character (!) sent over a function to clear the label, but it never clears. The integer is the ASCII code for the exclamation point:
void clearLabel() {
int clearString = 33;
char excalamtion = clearString;
ble_write(excalamtion);
}
Why would this not clear the label? I assume it has something to do with the clashing formats, but I'm not really too good at that even after reading some documentation. For the else if statement I also tried this
if ([s isEqualToString:#"!"])
but that didn't work out either... Any help would be appreciated.
EDIT:
I forgot to put in my loop code so you can see function calls. Here it is:
void loop()
{
if ( ble_connected() ) {
int a = 223;
sendMyInt(a);
delay(1000);
clearLabel();
delay(1000);
}
ble_do_events();
}
EDIT 2:
Based on a suggestion by #Duncan C , I have isolated the problem to the fact that the data is being sent as one packet to the iPhone. Upon printing out my generated string when the data is received, the string 233! is received all at once rather than individual chars of 2 3 3, and one second later the signal to clear, !. The data takes two seconds to appear on my phone, indicating that both delays are being used. I need a way to separate the 2 3 3 packet from the ! packet.
First off, this line:
NSLog([NSString stringWithFormat:#"%#",d]);
Is sort of pointless. The stringWithFormat serves no real purpose, since NSLog takes a format string anyway.
Use this instead:
NSLog(#"%#", d);
You should probably also log the contents of "s" once you convert your NSData to an NSString. That will help you figure out what's going on.
What is likely going on is that your string is coming in as "233!", all together, 4 bytes at a time (assuming that your integer is == 233).
Your string is unlikely to ever contain just "!". Instead, it will likely contain "233!" (4 characters.) I say likely because it depends on how the data is packetized into BLE. Something that short should be sent all in 1 BLE packet, so you should get the entire string together.
You could use the NSString method rangeOfString: to search for your "!" string, and if it contains an "!", clear your label, but that won't really do any good either. If you're sending "233!", then the iOS code will see the exclamation point in the string it receives and simply clear the label.
Or does your arduino project first send "233", then after some other event, send the "!". You didn't make that clear.
Another problem: What does the Arduino String class do if the integer is less than 1000, or less than 100, and doesn't require 3 or 4 characters to convert to a char array? What is stored in the unused bytes? You're always sending 4 characters, which is probably wrong.
Adding in another ble_do_events(); after calling the sendMyInt(); function causes the data to be transmit in two separate packets.
Some background... I am writing code that interacts with javascript via a ObjC-JS bridge utilizing UIWebView's stringByEvaluatingJavaScriptFromString:. The idea is that the "brains" of the app be in JS which tells Objective-C how to behave. There are multiple benefits to this like reduced binary size, flexible updates, etc. However, there is a case where there is some Objective-C only object that the JS needs to have a reference to (JS instructs ObjC when to use/remove the object). This is being done by placing the native object in a dictionary with a unique identifier which can be passed as a string to JS (over the bridge). My problem stems with coming up with a nice identifier for said native Objective-C object.
Thus, I am trying to convert a reference to an object to a string with no luck. This is what I have:
// anObject is a custom class
NSValue *handle = [NSValue valueWithPointer:(__bridge const void *)anObject];
NSData *data = [NSData dataWithValue:handle];
NSString *stringHandle = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
The dataWithValue: function (taken from this SO post):
+ (NSData *)dataWithValue:(NSValue *)value {
NSUInteger size;
const char* encoding = [value objCType];
NSGetSizeAndAlignment(encoding, &size, NULL);
void* ptr = malloc(size);
[value getValue:ptr];
NSData* data = [NSData dataWithBytes:ptr length:size];
free(ptr);
return data;
}
Walking through it in the debugger shows me a nil value for stringHandle:
What am I doing wrong?
What you're doing wrong is trying to treat an address as if it's a UTF-8 encoded string. An address -- or any other chunk of arbitrary data -- isn't very likely to be valid UTF-8 data. (If by chance it were, it still wouldn't be the string you expect.)
If you're trying to get a string containing the pointer value, i.e., the address of the original object, that's just [NSString stringWithFormat:#"%p", anObject];
If you really need to do it from the NSValue, then replace anObject with [theValue pointerValue].
If you want to pretty-print arbitrary data, see How to convert an NSData into an NSString Hex string?
You can get a string representation by calling the NSObject method "description". You can override the "description" method in a subclass if you need.
An NSValue of a pointer will be an object holding the 4 bytes of the 32-bit pointer. It will not hold any of the data pointed to in RAM.
I'm using the following code to obfuscate a passcode for a test app of mine.
- (NSString *)obfuscate:(NSString *)string withKey:(NSString *)key
{
// Create data object from the string
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
// Get pointer to data to obfuscate
char *dataPtr = (char *) [data bytes];
// Get pointer to key data
char *keyData = (char *) [[key dataUsingEncoding:NSUTF8StringEncoding] bytes];
// Points to each char in sequence in the key
char *keyPtr = keyData;
int keyIndex = 0;
// For each character in data, xor with current value in key
for (int x = 0; x < [data length]; x++)
{
// Replace current character in data with
// current character xor'd with current key value.
// Bump each pointer to the next character
*dataPtr = *dataPtr++ ^ *keyPtr++;
// If at end of key data, reset count and
// set key pointer back to start of key value
if (++keyIndex == [key length])
keyIndex = 0, keyPtr = keyData;
}
return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
This works like a charm with all strings, but i've ran into a bit of a problem comparing the following results
NSLog([[self obfuscate:#"0000", #"maki"]); //Returns 0]<W
NSLog([[self obfuscate:#"0809", #"maki"]); //Returns 0]<W
As you can see, the two strings with numbers in, while different, return the same result! Whats gone wrong in the code i've attached to result in the same result for these two numbers?
Another example:
NSLog([self obfuscate:#"8000" withKey:#"maki"]); //Returns 8U4_
NSLog([self obfuscate:#"8290" withKey:#"maki"]); //Returns 8U4_ as well
I may be misunderstanding the concept of obfuscation, but I was under the impression that each unique string returns a unique obfuscated string!
Please help me fix this bug/glitch
Source of Code: http://iosdevelopertips.com/cocoa/obfuscation-encryption-of-string-nsstring.html
The problem is your last line. You create the new string with the original, unmodified data object.
You need to create a new NSData object from the modified dataPtr bytes.
NSData *newData = [NSData dataWithBytes:dataPtr length:data.length];
return [[NSString alloc] initWithData:newData encoding:NSUTF8StringEncoding];
But you have some bigger issues.
The calls to bytes returns a constant, read-only reference to the bytes in the NSData object. You should NOT be modifying that data.
The result of your XOR on the character data could, in theory, result in a byte stream that is no longer a valid UTF-8 encoded string.
The obfuscation algorithm that you have selected is based on XORing the data and the "key" values together. Generally, this is not very strong. Moreover, since XOR is symmetric, the results are very prone to producing duplicates.
Although your implementation is currently broken, fixing it would not be of much help in preventing the algorithm from producing identical results for different data: it is relatively straightforward to construct key/data pairs that produce the same obfuscated string - for example,
[self obfuscate:#"0123" withKey:#"vwxy"]
[self obfuscate:#"pqrs" withKey:#"6789"]
will produce identical results "FFJJ", even though both the strings and the keys look sufficiently different.
If you would like to "obfuscate" your strings in a cryptographically strong way, use a salted secure hash algorithm: it will produce very different results for even slightly different strings.
I have some NSData that I am passing in as bytes
const void *bytes = [responseData bytes];
Those bytes were originally UTF8 formatted, I am now trying to get them into a UTF8 NSString without messing with the encoding at all.
I have previously written this if that copies the bytes into a cstring which normally would be fine unless I have any non english characters in the bytes which take two byte instead of one. This means any international characters in my string get messed up when I copy them into a cstring.
Hence the reason for needing to copying the bytes directly into a UTF8 formatted object.. preferably a NSString.. if possible.
This is how I was handling the conversion which I later found out is wrong but will hopefully give you a good idea of what I am trying to achieve.
else if (typeWithLocalOrdering == METHOD_RESPONSE)
{
cstring = (char *) malloc(sizeWithLocalOrdering + 1);
strncpy(cstring, bytes, sizeWithLocalOrdering);
cstring[sizeWithLocalOrdering] = '\0';
NSString *resultString = [NSString stringWithCString:cstring encoding:NSUTF16StringEncoding];
methodResponseData =[resultString dataUsingEncoding:NSUTF16StringEncoding]; // methodResponseData is used later on in my parsing method
// Take care of the memory allocatoin, so that you can find the endoffile notification
free(cstring);
bytes += sizeWithLocalOrdering;
length -= sizeWithLocalOrdering;
}
Any help would be greatly appreciated.
I don't understand this: "This means any international characters in my string get messed up when I copy them into a cstring." If "sizeWithLocalOrdering" is correct for the actual length of the byte string, it seems like your original code should work (though I would have used memcpy rather than strncpy). If not, nothing's going to work.
Update: OK, I see it. Your original code was wrong here:
[NSString stringWithCString:cstring encoding:NSUTF16StringEncoding];
That should have been NSUTF8StringEncoding.
So it turns out I had a few interesting things happening that I was not expecting..
This is the code I used to get around working with the cstring and just take the bytes straight to a NSString as its original encoding then
NSString *tempstring = [[NSString alloc] initWithBytes:bytes length:sizeWithLocalOrdering encoding:NSUTF8StringEncoding];
methodResponseData =[tempstring dataUsingEncoding:NSUTF16StringEncoding]; // methodResponseData is used later on in my parsing method
I am trying to create an NSString object from a const unichar buffer where I don't know the length of the buffer.
I want to use the NSString stringWithCharacters: length: method to create the string (this seems to work), but please can you help me find out the length?
I have:
const unichar *c_emAdd = [... returns successfully from a C++ function...]
NSString *emAdd = [NSString stringWithCharacters:c_emAdd length = unicharLen];
Can anyone help me find out how to check what unicharLen is? I don't get this length passed back to me by the call to the C++ function, so I presume I'd need to iterate until I find a terminating character? Anyone have a code snippet to help? Thanks!
Is your char buffer null terminated?
Is it 16-bit unicode?
NSString *emAdd = [NSString stringWithFormat:#"%S", c_emAdd];
Your unichars should be null terminated so you when you reach two null bytes (a unichar = 0x0000) in the pointer you will know the length.
unsigned long long unistrlen(unichar *chars)
{
unsigned long long length = 0llu;
if(NULL == chars) return length;
while(NULL != chars[length])
length++;
return length;
}
//...
//Inside Some method or function
unichar chars[] = { 0x005A, 0x0065, 0x0062, 0x0072, 0x0061, 0x0000 };
NSString *string = [NSString stringWithCharacters:chars length:unistrlen(chars)];
NSLog(#"%#", string);
Or even simpler format with %S specifier