Easiest way to perform a deep copy of a string to pass to a scanner? - objective-c

I am following the code shown here : Convert hex string to long
to read a long from a string. In their example, they have a hard-coded string, but in my own context have another value I am taking the string from and that value seems to change but I don't think it is supposed to. I'd like to keep the value being read at the start and as such pass a deep-copy of the string to the scanner instead.
I am aware that [string copy] and [string mutableCopy] provide a shallow copy and in NSString copy not copying? question we point out that NSString is supposed to be immutable and so the scenario where it is changing is also when its pointer changes. However I am wondering if using scanner causes issues with this premise? Could I be understanding the issue here entirely wrong? I don't think the original string is supposed to even change and yet in the debugger it is changing after a few lines.
1 NSString* pString = someIdentifier;
2
3 unsigned long long val;
4 NSScanner* scanner = [NSScanner scannerWithString: pString];
5 [scanner scanHexLongLong: &val];
6
Debug breakpoints
1 pString = #"0x12345"
4 pString = #"9876543" (not exactly a hex string)
val = "74576" (some decimal close to, but not the same as the original pString)
scanner = 0x12b45 (same a pString with 1 bit different)
5 scanner = previous pString
6 pString = 0xFFFFF
val = 0xFFFFF ( in decimal)

Related

objective-c NSString to 0x%04x format

NSString *testValue = #"3141";
I want to convert "3141" to 0x0c45, use hexadecimal format(0x%04x).
like this:
printf("0x%04x", testValue);
The value printed out is:
0x0c45
But I don't know how to save it, how can I do it?
====================================================
NSString *testValue = #"3141";
NSUInteger unsignedValue = (NSUInteger)testValue.integerValue;
NSString *formatted = [NSString stringWithFormat:#"0x%04X", unsignedValue];
The result of this transfer seems to be incorrect. The result is 0x0000. Let me paste the source code as follows:
UInt16 myValue = 3141;
printf("0x%04x", myValue);
The result printed is 0x0c45. But what I need is not to print, but to save the value 0x0c45.
If you take a look at the parameters that printf expects, there's a table of format specifiers that printf understands, and the input that printf expects to correspond to those format specifiers. Specifically, the x/X specifiers:
converts an unsigned integer into hexadecimal representation hhhh
When printf tries to format testValue, it is expecting an unsigned integer of some variety (unsigned int, unsigned long, etc.), and the testValue you pass in is an NSString * (a pointer to an NSString). The pointer value itself is being interpreted as an unsigned integer, and printed out by printf.
In order to get printf to print the value you actually expect, you will first need to get the integer value of the string (either via integerValue, or a different method), and then pass that to printf. For example:
NSString *testValue = #"3141";
NSUInteger unsignedValue = (NSUInteger)testValue.integerValue;
printf("0x%04X", unsignedValue);
(Note that in a production environment, you'll probably want to use something more failure tolerant than just integerValue, and you'll need to be aware of the domain of how large testValue might need to be, the size of the storage of unsignedValue, etc.)
Update: If you'd like to get the result of formatting a value in this way beyond just printing it, you can use something like +[NSString stringWithFormat:] to get the string value. This method accepts the same specifiers that printf does, so you can use it in the same way:
NSString *testValue = #"3141";
NSUInteger unsignedValue = (NSUInteger)testValue.integerValue;
NSString *formatted = [NSString stringWithFormat:#"0x%04X", unsignedValue];
// Do anything you want with `formatted`.

HexDex Colour :Converting String to int removing 0

I have a colour class set up where it takes red/blue/green values and uses them to create a hex string which is then used to return a colour. It can be initialized by providing a red, blue and green colour like this
Colour *col = [[Colour alloc] initWithRed:200 Green:100 Blue:05];
The problem with this method is, if I pass in a value like 05, the 0 is stripped off, and just a 5 is passed in, so the blue will set to just be 5 rather than 05. My method to return a valid hex string is this
-(NSString *)getHexString
{
NSString *hexString = [NSString stringWithFormat:#"#%x%x%x", iRed_i,iGreen_i,iBlue_i];
hexString = [hexString uppercaseString];
return hexString;
}
It is expecting 2 values for each number to return the correct colour code, but the 0 is being stripped off meaning it returns an incorrect colour.
The other method I use to create a colour is to initialise the colour object with a hex string like this:
Colour *colour = [[Colour alloc] initWithHex:#"#782402"];
I then use scanner to separate the 3 values like so
if ([sHex_p hasPrefix:#"#"]) sHex_p = [sHex_p substringFromIndex:1];
unsigned int outVal;
NSString *sRed = [sHex_p substringToIndex:2];
NSScanner* scanner = [NSScanner scannerWithString:sRed];
[scanner scanHexInt:&outVal];
[self setRed:outVal];
NSString *sGreen = [[sHex_p substringFromIndex:2] substringToIndex:2];
scanner = [NSScanner scannerWithString:sGreen];
[scanner scanHexInt:&outVal];
[self setGreen:outVal];
NSString *sBlue = [sHex_p substringFromIndex:4];
scanner = [NSScanner scannerWithString:sBlue];
[scanner scanHexInt:&outVal];
[self setBlue:outVal];
}
But again same problem, in the hex string I provided the last 2 values are 02, but when converting from int to string, that 0 will be stripped out, so just a 2 will be passed, again ending up with an incorrect colour.
I really am unsure as what the best way to solve this is. Would be really grateful if someone could be point in the right direction.
I have been testing it using this site here
http://www.colorpicker.com/
Quick example I am trying to create this hex string "#DB4200". It requires red = 219 green = 66, blue = 00
But as the blue is just set to 0 when converts from string to int, ends up returning an incorrect colour, hex string ends up as "#DB420"
Well, this actually involves three different situations. First, 05 is just 5,
in both hexadecimal and decimal bases. The problem is not with your input in
this case, but probably with the output. However, remember this is a decimal
literal, so if you want #101010 to mean 16 out of 255 of each color, use
hexadecimal constants like 0x10.
Second: Regarding output. The format string you're using does not enforce 2
digits in each color, and that's why your 05 becomes just 5. You can force
the output of an integer (in hexadecimal form) to two digits padded with zeros
by using %02x instead of %x.
Third, is actually first. Looks like your method is working, but the output is
wrong. Fixing the format string should solve this. Anyway, here's an
alternative version using sscanf.
- (void)interpretHexString(NSString *str)
{
int r,g,b;
const char *s = [str cStringUsingEncoding:NSUTF8StringEncoding];
if (sscanf(s, "#%02x%02x%02x", &r, &g, &b) != 3) {
/* problem with input */
} else {
/* set ivars */
[self setRed:r];
[self setGreen:g];
[self setBlue:b];
}
}

Multi-character character sequence in 3rd party library

I'm using a 3rd party library for an iOS project I work on, and I'm down to one warning left in the project, namely on this line of code
[NSNumber numberWithUnsignedLongLong:'oaut']
And the warning is
Multi-character character constant
I suck at C, so I don't know how to fix this, but I'm sure the fix is relatively easy. Help?
EDIT: More context.
#implementation MPOAuthCredentialConcreteStore (KeychainAdditions)
- (void)addToKeychainUsingName:(NSString *)inName andValue:(NSString *)inValue {
NSString *serverName = [self.baseURL host];
NSString *securityDomain = [self.authenticationURL host];
// NSString *itemID = [NSString stringWithFormat:#"%#.oauth.%#", [[NSBundle mainBundle] bundleIdentifier], inName];
NSDictionary *searchDictionary = nil;
NSDictionary *keychainItemAttributeDictionary = [NSDictionary dictionaryWithObjectsAndKeys: (id)kSecClassInternetPassword, kSecClass,
securityDomain, kSecAttrSecurityDomain,
serverName, kSecAttrServer,
inName, kSecAttrAccount,
kSecAttrAuthenticationTypeDefault, kSecAttrAuthenticationType,
[NSNumber numberWithUnsignedLongLong:"oaut"], kSecAttrType,
[inValue dataUsingEncoding:NSUTF8StringEncoding], kSecValueData,
nil];
if ([self findValueFromKeychainUsingName:inName returningItem:&searchDictionary]) {
NSMutableDictionary *updateDictionary = [keychainItemAttributeDictionary mutableCopy];
[updateDictionary removeObjectForKey:(id)kSecClass];
SecItemUpdate((CFDictionaryRef)keychainItemAttributeDictionary, (CFDictionaryRef)updateDictionary);
[updateDictionary release];
} else {
OSStatus success = SecItemAdd( (CFDictionaryRef)keychainItemAttributeDictionary, NULL);
if (success == errSecNotAvailable) {
[NSException raise:#"Keychain Not Available" format:#"Keychain Access Not Currently Available"];
} else if (success == errSecDuplicateItem) {
[NSException raise:#"Keychain duplicate item exception" format:#"Item already exists for %#", keychainItemAttributeDictionary];
}
}
}
EDIT 2: They were attempting to meet the requirements of this by creating that NSNumber:
#constant kSecAttrType Specifies a dictionary key whose value is the item's
type attribute. You use this key to set or get a value of type
CFNumberRef that represents the item's type. This number is the
unsigned integer representation of a four-character code (e.g.,
'aTyp').
In C and Obj-C the single-quote ' is used only for single-character constants. You need to use the double-quote: "
Like so:
[NSNumber numberWithUnsignedLongLong:"oaut"]
That covers the warning, but there's also a semantic issue here. Although a single character constant, such as 'o', can be treated as an integer (and can be promoted to an unsigned long long), a "string" (char * or char []) cannot, which means you can't use "oaut" as an argument to numberWithUnsignedLongLong:
Update:
I guess the four-character code is supposed to be treated as an integer, i.e., the 8 bits of each char put in place as if they together were a 32-bit int:
char code[] = "oaut";
uint32_t code_as_int = code[0] | (code[1] << 8) | (code[2] << 16) | (code[3] << 24);
[NSNumber numberWithUnsignedLongLong:code_as_int]
although I'm not sure which endianness would be expected here, nor why this is calling for an unsigned long long, unless just to be certain there are enough bits.
Rudy's comment, now that I think of it, is correct -- multi-character constants are allowed by some compilers for exactly this purpose (it is "implementation-defined" behavior).
'oaut' (single quotes) is a character, so the compiler tries to interpret it as a multi-byte character but can't make any sense of it. That explains the error message.
I guess that if you gave a proper string, like #"oaut", you'd get another error message, since numberWithUnsignedLongLong: expects an unsigned long long, not a string or a character. Are you trying to pass a variable with the name "oaut"? If so, use
[NSNumber numberWithUnsignedLongLong: oaut];
If not, then please explain what "oaut" is.
Edit
'oaut' may actually be the original value. There are/were multi-character character constants in C. Using a (4 byte) char, used as int and promoted to unsigned long long would then be possible. This must be old code. It seems such code was accepted by CodeWarrior.
Assuming that really a multi-char char const was meant, 'oaut' looks like a "magic number" and this value was chosen because it is the beginning of "oauth". I guess it should either be value 0x6F617574 or 0x7475616F.
#Josh Caswell 's answer is partially right, the simplest and "official" solution is:
[NSNumber numberWithUnsignedInt:'oaut']
unsigned int's length is 32-bit in both 32-bit and 64-bit cpu, there's a practical example from Apple: https://developer.apple.com/library/ios/samplecode/CryptoExercise/Listings/Classes_SecKeyWrapper_m.html

How to get inout to an NSString?

How to get input from an NSString as scanf ("%#", &str); doesn't work?
scanf will read into a C string and not into a NSString (as far as I know). So, to do what you're trying to do you need to first read your input into a C string (i.e. str) and then make that into an NString as follows
myString = [NSString stringWithUTF8String:str];
By the way, you don't need to pass the address of str i.e. &str if str is an array. Simply do:
scanf("%s",str);

Storing and retrieving unsigned long long value to/from NSString

I have an unsigned long long value which I want to store into an NSString and retrieve from the string.
Initially I have the value in an NSNumber and I am using this to get the string
NSString *numStr = [NSString stringWithFormat:#"%llu", [myNum unsignedLongLongValue]];
where myNum is an NSNumber.
To get back the NSNumber from the NSString I have to first get the unsigned long long value. But there is no method in the NSString class to do that (we just have one for getting the long long value, not the unsigned long long value).
Can someone please tell me how I can get back the value into an NSNumber variable.
Thanks.
There are a lot of ways to accomplish this. The following is the most pragmatic:
NSString *numStr = [NSString stringWithFormat:#"%llu", [myNum unsignedLongLongValue]];
// .. code and time in between when numStr was created
// .. and now needs to be converted back to a long long.
// .. Therefore, numStr used below does not imply the same numStr above.
unsigned long long ullvalue = strtoull([numStr UTF8String], NULL, 0);
This makes a few reasonable assumptions such as numStr will only contain numeric digits and it contains a 'valid' unsigned long long value. A drawback to this approach is that UTF8String creates what essentially amounts to [[numStr dataUsingEncoding:NSUTF8StringEncoding] bytes], or in other words something along the lines of 32 bytes of autoreleased memory per call. For the vast majority of uses, this is no problem what-so-ever.
For an example of how to add something like unsignedLongLongValue to NSString that is both very fast and uses no autoreleased memory as a side effect, take a look at the end of my (long) answer to this SO question. Specifically the example implementation of rklIntValue, which would require only trivial modifications to implement unsignedLongLongValue.
More information regarding strtoull can be found in its man page.