Base 62 conversion in Objective-C - ios

I spent much too much time trying to find an implementation for base 62 conversion for Objective-C. I am sure this is a terrible example and there must be an elegant, super-efficient way to do this, but this works, please edit or answer to improve it! But I wanted to help people searching for this to have something that will work. There doesn't appear to be anything specific to be found for an Objective-C implementation.
#implementation Base62Converter
+(int)decode:(NSString*)string
{
int num = 0;
NSString * alphabet = #"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
for (int i = 0, len = [string length]; i < len; i++)
{
NSRange range = [alphabet rangeOfString:[string substringWithRange:NSMakeRange(i,1)]];
num = num * 62 + range.location;
}
return num;
}
+(NSString*)encode:(int)num
{
NSString * alphabet = #"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
NSMutableString * precursor = [NSMutableString stringWithCapacity:3];
while (num > 0)
{
[precursor appendString:[alphabet substringWithRange:NSMakeRange( num % 62, 1 )]];
num /= 62;
}
// http://stackoverflow.com/questions/6720191/reverse-nsstring-text
NSMutableString *reversedString = [NSMutableString stringWithCapacity:[precursor length]];
[precursor enumerateSubstringsInRange:NSMakeRange(0,[precursor length])
options:(NSStringEnumerationReverse |NSStringEnumerationByComposedCharacterSequences)
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
[reversedString appendString:substring];
}];
return reversedString;
}
#end

Your code is fine. If anything, make it more generic. Here is a recursive version for any base (same code):
#import <Foundation/Foundation.h>
#interface BaseConversion : NSObject
+(NSString*) formatNumber:(NSUInteger)n toBase:(NSUInteger)base;
+(NSString*) formatNumber:(NSUInteger)n usingAlphabet:(NSString*)alphabet;
#end
#implementation BaseConversion
// Uses the alphabet length as base.
+(NSString*) formatNumber:(NSUInteger)n usingAlphabet:(NSString*)alphabet
{
NSUInteger base = [alphabet length];
if (n<base){
// direct conversion
NSRange range = NSMakeRange(n, 1);
return [alphabet substringWithRange:range];
} else {
return [NSString stringWithFormat:#"%#%#",
// Get the number minus the last digit and do a recursive call.
// Note that division between integer drops the decimals, eg: 769/10 = 76
[self formatNumber:n/base usingAlphabet:alphabet],
// Get the last digit and perform direct conversion with the result.
[alphabet substringWithRange:NSMakeRange(n%base, 1)]];
}
}
+(NSString*) formatNumber:(NSUInteger)n toBase:(NSUInteger)base
{
NSString *alphabet = #"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; // 62 digits
NSAssert([alphabet length]>=base,#"Not enough characters. Use base %ld or lower.",(unsigned long)[alphabet length]);
return [self formatNumber:n usingAlphabet:[alphabet substringWithRange:NSMakeRange (0, base)]];
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
NSLog(#"%#",[BaseConversion formatNumber:3735928559 toBase:16]); // deadbeef
return EXIT_SUCCESS;
}
}
A Swift 3 version: https://gist.github.com/j4n0/056475333d0ddfe963ac5dc44fa53bf2

You could improve your encode method in such a way that reversing the final string is not necessary:
+ (NSString *)encode:(NSUInteger)num
{
NSString *alphabet = #"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
NSUInteger base = [alphabet length];
NSMutableString *result = [NSMutableString string];
while (num > 0) {
NSString *digit = [alphabet substringWithRange:NSMakeRange(num % base, 1)];
[result insertString:digit atIndex:0];
num /= base;
}
return result;
}
Of course, this could also be generalized for arbitrary bases or alphabets, as suggested by #Jano in his answer.
Note that this method (as well as your original encode method) returns an empty string for num = 0, so you might want to consider this case separately (or just replace while (num > 0) { ... } by do { ... } while (num > 0).
For more efficiency, one could avoid all intermediate NSString objects altogether, and work with plain C strings:
+ (NSString *)encode:(NSUInteger)num
{
static const char *alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
NSUInteger base = 62;
char result[20]; // sufficient room to encode 2^64 in Base-62
char *p = result + sizeof(result);
*--p = 0; // NULL termination
while (num > 0) {
*--p = alphabet[num % base];
num /= base;
}
return [NSString stringWithUTF8String:p];
}

Related

iOS hex addition

i have an NSString with hex value
NSString* someString = #"AAB827EB5A6E225CAA
i want to extract from b (the second char) to 2 (-5 char)
make an addition of all extracted char and i have to find the 5C as result (-4 and -3 char)
i have tried this :
NSMutableArray *hex = [[NSMutableArray alloc]init];
unichar firstChar = [[someString uppercaseString] characterAtIndex:0];
unichar seconChar = [[someString uppercaseString] characterAtIndex:1];
unichar lastChar = [[someString uppercaseString] characterAtIndex:[print length]-1];
unichar beforeLastChar = [[someString uppercaseString] characterAtIndex:[print length]-2];
if (firstChar == 'A' && seconChar == 'A' && lastChar =='A' && beforeLastChar=='A') {
for (int i=2;i< [print length]-4; i++) {
NSString *decim =[NSString stringWithFormat:#"%hu",[someString characterAtIndex:i]];
[hex addObject:decim];
}
NSLog(#"hex : %#",hex);
}
but the log is
hex : (
98,
56,
50,
55,
101,
98,
53,
97,
54,
101,
50,
50, )
i've tried to covert it to string then int for calculation but if i can avoid conversion and continue with hex i would prefer
thanks for help
The code could be probably simplifed even more but one possibility:
NSString *someString = #"AAB827EB5A6E225CAA";
// I have improved a bit your check for prefix and suffix
if ([someString hasPrefix:#"AA"] && [someString hasSuffix:#"AA"]) {
NSMutableArray *hexNumbers = [[NSMutableArray alloc] init];
for (int i = 2; i < [someString length] - 4; i++) {
unichar digit = [someString characterAtIndex:i];
NSUInteger value;
// we have to convert the character into its numeric value
// we could also use NSScanner for it but this is a simple way
if (digit >= 'A') {
value = digit - 'A' + 10;
} else {
value = digit - '0';
}
// add the value to the array
[hexNumbers addObject:#(value)];
}
NSLog(#"hex : %#", hexNumbers);
// a trick to get the sum of an array
NSNumber *sum = [hexNumbers valueForKeyPath:#"#sum.self"];
// print the sum in decadic and in hexadecimal
NSLog(#"Sum: %#, in hexa: %X", sum, [sum integerValue]);
}

who can tell me what the function's affect?

The first method is:
-(NSData *)stringToAddBytes:(NSString*)addString
{
int length = (int)[addString length];
if(length < 2)
{
return nil;
}
Byte buf[length / 2];
for(int i = 0 ;i < length/2 ;i++)
{
NSString *str = [addString substringWithRange:NSMakeRange(i * 2, 2)];
Byte b = [self hexStringToByte:str];
buf[i]=b;
}
NSData * myD = [[NSData alloc]initWithBytes:buf length:length/2];
return myD;
}
THe method that the first method called.
-(Byte)hexStringToByte:(NSString*)str
{
NSArray *charArray = [[NSArray alloc]initWithObjects:#"0",#"1",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",
#"A",#"B",#"C",#"D",#"E",#"F",nil];
NSString *str1 = [str substringWithRange:NSMakeRange(0, 1)];
int num1 = (int)[charArray indexOfObject:str1];
NSString *str2 = [str substringWithRange:NSMakeRange(1, 1)];
int num2 = (int)[charArray indexOfObject:str2];
Byte b = num1*16+num2;
return b;
}
Thank you for your answer.It looks change large char to small char.
hexStringToByte: wil convert string with hexadecimal number representation (example #"FF") to Byte value (in this example 255).
stringToAddBytes: uses hexStringToByte: to create NSData of bytes breaking addString into two letter peases and converting them to Byte values.
In other words, this is string serialization.
Example:
// 255 = 0xFF
// 170 = 0xAA
// 136 = 0x88
NSString* addString = #"FFAA88";
NSData* data = [self stringToAddBytes:addString];
// data will be [255, 170, 136]
Be aware that NSData is not an array, instead, it represents a raw object.

Objective-c negative Hex values

I have the following category method on NSData. I'm trying to extract the bit field at the given index and have it return as an NSNumber. I have it working perfectly for all positive but I need it to work with negative numbers as well.
My Implementation looks as follows:
#import <Foundation/Foundation.h>
#interface NSData (ExternalDevices)
#end
#implementation NSData (ExternalDevices)
- (NSNumber *)extractLittleEndianBitFieldAtIndex:(int)index forLength:(int)length
{
//This function has limitations on the "length" parameter that are not yet know/defined
//These limitations are due to the max size of "NSInteger intData" defined below
int first_byte = index/8; //Index of the first byte containing this bit field
int last_byte = (length+index-1)/8; //Index of the last byte containing this bit field
int byte_length = last_byte - first_byte + 1; //number of bytes containing this bit field
Byte *byteArray = (Byte*)malloc(byte_length);
memcpy(byteArray, [[self subdataWithRange:NSMakeRange(first_byte, byte_length)] bytes], byte_length);
NSInteger intData = *((NSInteger *)byteArray);
free(byteArray);
return [NSNumber numberWithInt:intData];
}
+ (NSData *)dataFromHexString:(NSString *)string
{
string = [string lowercaseString];
NSMutableData *data= [NSMutableData new];
unsigned char whole_byte;
char byte_chars[3] = {'\0','\0','\0'};
int i = 0;
NSUInteger length = string.length;
while (i < length-1) {
char c = [string characterAtIndex:i++];
if (c < '0' || (c > '9' && c < 'a') || c > 'f')
continue;
byte_chars[0] = c;
byte_chars[1] = [string characterAtIndex:i++];
whole_byte = strtol(byte_chars, NULL, 16);
[data appendBytes:&whole_byte length:1];
}
return data;
}
#end
#interface Testing:NSObject
#end
#implementation Testing
- (instancetype)init
{
self = [super init];
if (self)
{
{
NSData *data = [NSData dataFromHexString:#"e30b"];
NSLog(#"%# should be 3043", [data extractLittleEndianBitFieldAtIndex:0 forLength:16]);
}
{
NSData *data = [NSData dataFromHexString:#"46e0"];
NSLog(#"%# should be -8122", [data extractLittleEndianBitFieldAtIndex:0 forLength:16]);
}
{
NSData *data = [NSData dataFromHexString:#"f208"];
NSLog(#"%# should be 2290", [data extractLittleEndianBitFieldAtIndex:0 forLength:16]);
}
{
NSData *data = [NSData dataFromHexString:#"10e6"];
NSLog(#"%# should be -6640", [data extractLittleEndianBitFieldAtIndex:0 forLength:16]);
}
{
NSData *data = [NSData dataFromHexString:#"018900"];
NSLog(#"%# should be 137", [data extractLittleEndianBitFieldAtIndex:8 forLength:16]);
}
}
return self;
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
[[Testing alloc] init];
}
}
The following website seems to always yield the results I want under INT16 - Little Endian (BA)
http://www.scadacore.com/field-applications/miscellaneous/online-hex-converter.html
Although it is important to note that not every number I work with will be an INT16
Your line:
NSInteger intData = *((NSInteger *)byteArray);
is your key problem for two reasons:
byteArray may be shorter (or less likely, longer) than an NSInteger and you'll end up reading garbage. E.g. if byteArray is 2 bytes as in your examples and NSInteger is 4 bytes - which it will be in 64-bit - you'll read two bytes of garbage.
If you are converting signed values you need to sign-extend the value - that is replicate the sign bit into the higher unused bits. E.g. if you are converting a signed 16-bit field into a 32-bit signed value then the upper 16-bits need to be a replication of the most significant bit of the 16-bit value, so 0x7000 -> 0x00007000 and 0x8000 -> 0xFFFF8000.
You need to come up with an algorithm that handles these issues. You may find it easier to do the conversion a byte at a time using masking (and'ing), or'ing and shifting.
HTH

Creating Random Alpha numeric on IOS

i'm java programmer that 'must' move on to obj-C for a while,
i got some confuse when generating random alphanumeric code... here my javacode:
PS: i want to generate code like this :Gh12PU67, AC88pP13, Bk81gH89
private String generateCode(){
String code = "";
Random r = new Random();
char[] c = new char[]{'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
for(int i = 0; i<4; i++){
int uplow = r.nextInt(2);
String temp = ""+ c[r.nextInt(c.length)];
if(uplow==1)
code = code + temp.toUpperCase();
else
code = code + temp;
if((i+1)%2==0){
code += r.nextInt(10);
code += r.nextInt(10);
}
}
return code;
}
then i create on OBJ-C
-(void)generateCode{
NSString *alphabet = #"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXZY0123456789";
NSMutableString *s = [NSMutableString stringWithCapacity:4];
for (NSUInteger i = 0U; i < 4; i++) {
u_int32_t r = arc4random() % [alphabet length];
unichar c = [alphabet characterAtIndex:r];
[s appendFormat:#"%C", c];
}
NSLog(#"s-->%#",s);
}
but i got "HpNz" for result AC88pP13 insted that hve pattern String,string, numeric,numeric, lowescase string,numeric,numeric...
that case screw my life for 3 days...
Your Objective-C code looks good, but (as #Wain correctly said in a comment above),
the Java function function contains logic to insert 2 digits after 2 letters, which you
have not replicated in the Objective-C method.
I would make that logic slightly less obscure and write it as
- (void)generateCode
{
static NSString *letters = #"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXZY";
static NSString *digits = #"0123456789";
NSMutableString *s = [NSMutableString stringWithCapacity:8];
for (NSUInteger i = 0; i < 2; i++) {
uint32_t r;
// Append 2 random letters:
r = arc4random_uniform((uint32_t)[letters length]);
[s appendFormat:#"%C", [letters characterAtIndex:r]];
r = arc4random_uniform((uint32_t)[letters length]);
[s appendFormat:#"%C", [letters characterAtIndex:r]];
// Append 2 random digits:
r = arc4random_uniform((uint32_t)[digits length]);
[s appendFormat:#"%C", [digits characterAtIndex:r]];
r = arc4random_uniform((uint32_t)[digits length]);
[s appendFormat:#"%C", [digits characterAtIndex:r]];
}
NSLog(#"s-->%#",s);
}
Remark (from the man page):
arc4random_uniform(length) is preferred over arc4random() % length,
as it avoids "modulo bias" when the upper bound is not a power of two.
Remark: A more verbatim translation of the Java code code += r.nextInt(10);
to Objective-C would be
r = arc4random_uniform(10);
[s appendString:[#(r) stringValue]];
which creates a NSNumber object #(r) from the random number, and then
converts that to a string.
if you want a secure random string you should use this code:
#define ASCII_START_NUMERS 0x30
#define ASCII_END_NUMERS 0x39
#define ASCII_START_LETTERS_A 0x41
#define ASCII_END_LETTERS_Z 0x5A
#define ASCII_START_LETTERS_a 0x61
#define ASCII_END_LETTERS_z 0x5A
-(NSString *)getRandomString:(int)length {
NSMutableString *result = [[NSMutableString alloc]init];
while (result.length != length) {
NSMutableData* data = [NSMutableData dataWithLength:1];
SecRandomCopyBytes(kSecRandomDefault, 1, [data mutableBytes]);
Byte currentChar = 0;
[data getBytes:&currentChar length:1];
NSString *s = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if (currentChar > ASCII_START_NUMERS && currentChar < ASCII_END_NUMERS) { // 0 to 0
[result appendString:s];
continue;
}
if (currentChar > ASCII_START_LETTERS_A && currentChar < ASCII_END_LETTERS_Z) { // A to Z
[result appendString:s];
continue;
}
if (currentChar > ASCII_START_LETTERS_a && currentChar < ASCII_END_LETTERS_z) { // a to z
[result appendString:s];
continue;
}
}
return result;
}

Parsing a NSString to a byte array

I need to parse an NSString to a byte array and am having some trouble doing it. I have a padded byte array in a method and convert that into a mutablestring, then I have a method that needs to place those numbers back into a byte array.
In C# it would be as simple as:
do
{
val = byte.Parse(str.Substring(i, 3));
byteArr[j++] = val;
i += 3;
}
Here is the code snippit Note** Ive been trying a lot of different things in the do loop so its a mess in there right now:
-(NSData*) StrToByteArray: (NSString*)str
{
NSLog(#"StrToByteArray. String: %#", str);
if([str length]==0)
NSLog(#"Invailid String");
int val;
Byte byteArr[[str length]/3];
int i = 0;
int j = 0;
NSRange range;
do {
range = NSMakeRange(i, 3);
val = (int)[str substringFromIndex:i];
NSLog(#"StrToByteArray. VAR: %i", val);
byteArr[j++] = val;
NSLog(#"byteArr: %i",byteArr[i]);
i+=3;
}while(i<str.length);
NSData* wrappedByteArr = [NSData dataWithBytes:&byteArr length: sizeof(byteArr)];
return wrappedByteArr;
}
Here is the loop that makes the padded string:
for(int i = 0; i<=len;i++)
{
val = byteArr[i];
NSLog(#"byteArr to string original: %i", val);
if(val<(Byte)10)
{
[tempStr appendString:(#"00")];
[tempStr appendString:[NSString stringWithFormat:#"%d",val]];
}
else if(val<(Byte)100)
{
[tempStr appendString:(#"0")];
[tempStr appendString:[NSString stringWithFormat:#"%d",val]];
}
else {
[tempStr appendString:[NSString stringWithFormat:#"%d",val]];
}
}
NSLog(#"string: %#", tempStr);
return tempStr;
Take 2
Now that I know what the data looks like and how you want to parse it, I would approach it like this:
- (NSData *) parseStringToData:(NSString *) str
{
if ([str length] % 3 != 0)
{
// raise an exception, because the string's length should be a multiple of 3.
}
NSMutableData *result = [NSMutableData dataWithLength:[str length] / 3];
unsigned char *buffer = [result mutableBytes];
for (NSUInteger i = 0; i < [result length]; i++)
{
NSString *byteString = [str substringWithRange:NSMakeRange(i * 3, 3)];
buffer[i] = [byteString intValue];
}
return result;
}
Edit:
Your padding method could be simplified as well by providing the correct format specifier that automatically pads integers.
for(int i = 0; i<=len;i++)
{
val = byteArr[i];
NSLog(#"byteArr to string original: %i", val);
[tempStr appendFormat:#"%03d", val];
}

Resources