Working solution for Speakable

#1
#import "NSData+Speakable.h"

void printBits(size_t const size, void const * const ptr)
{
    unsigned char *b = (unsigned char*) ptr;
    unsigned char byte;
    long int i, j;
    
    for (i = size - 1; i >= 0; i--)
    {
        for (j = 7; j >= 0; j--)
        {
            byte = b[i] & (1 << j);
            byte >>= j;
            printf("%u", byte);
        }
    }
    puts("");
}

@implementation NSData (Speakable)

static unsigned char highMask = 0xe0;
static unsigned char lowMask = 0x1f;

+ (NSArray *) brands
{
    return @[@"Camry", @"Nikon", @"Apple", @"Ford", @"Audi", @"Google", @"Nike", @"Amazon",
             @"Honda", @"Mazda", @"Buick", @"Fiat", @"Jeep", @"Lexus", @"Volvo", @"Fuji",
             @"Sony", @"Delta", @"Focus", @"Puma", @"Samsung", @"Tivo", @"Halo", @"Sting",
             @"Shrek", @"Avatar", @"Shell", @"Visa", @"Vogue", @"Twitter", @"Lego", @"Pepsi"];
}

- (NSString *)encodeAsSpeakableString
{
    NSMutableString *speakable = [[NSMutableString alloc] init];
    const unsigned char *data = [self bytes];
    if(data) {
        for (int i = 0; i < [self length]; i++) {
            unsigned char lower = data[i] & lowMask;
            unsigned char higher = ((data[i] & highMask) >> 5) + 2;
            if ([speakable length]) {
                [speakable appendString:@" "];
            }
            [speakable appendString:[NSString stringWithFormat:@"%d %@", higher, [NSData brands][lower]]];
        }
    }
    return speakable;
}

+ (NSData *)dataWithSpeakableString:(NSString *)s error:(NSError **)e
{
    BOOL success = YES;
    BOOL continueParsing = YES;
    NSRange searchRange = NSMakeRange(0, s.length);
    NSCharacterSet *validDigits = [NSCharacterSet characterSetWithCharactersInString:@"23456789"];
    NSCharacterSet *chars = [NSCharacterSet letterCharacterSet];
    NSMutableData *data = [[NSMutableData alloc] init];
    while (continueParsing) {
        int8_t encodedByte = 0;
        NSRange resultRange = [s rangeOfCharacterFromSet:validDigits options:NSLiteralSearch range:searchRange];
        if (resultRange.location != NSNotFound) {
            searchRange.location = resultRange.location + resultRange.length;
            searchRange.length = s.length - searchRange.location;
            encodedByte = ([[s substringWithRange:resultRange] integerValue] - 2) << 5;
            printBits(sizeof(encodedByte), &encodedByte);
            NSLog(@"1. Result %@ | Search %@", NSStringFromRange(resultRange), NSStringFromRange(searchRange));
            NSLog(@"2. %@", [s substringWithRange:resultRange]);
        } else {
            success = YES;
            NSLog(@"A. Breaking out");
            break;
        }
        resultRange = [s rangeOfCharacterFromSet:chars options:NSCaseInsensitiveSearch range:searchRange];
        if (resultRange.location != NSNotFound) {
            NSScanner *scanner = [NSScanner scannerWithString:s];
            scanner.caseSensitive = NO;
            scanner.scanLocation = resultRange.location;
            NSString *word = [[NSString alloc] init];
            [scanner scanCharactersFromSet:chars intoString:&word];
            searchRange.location = resultRange.location + word.length;
            searchRange.length = s.length - searchRange.location;
            NSUInteger wordIndex = [[NSData brands] indexOfObject:[word capitalizedString]];
            if (wordIndex != NSNotFound) {
                NSLog(@"3. wordIndex: %lu", (unsigned long)wordIndex);
                encodedByte = encodedByte | wordIndex;
            } else {
                success = NO;
                break;
            }
            [data appendBytes:&encodedByte length:1];
            NSLog(@"4. Result %@ | Search %@", NSStringFromRange(resultRange), NSStringFromRange(searchRange));
            NSLog(@"5. %@", word);
            NSLog(@"6. %@", data);
        }
        else {
            continueParsing = NO;
            success = NO;
        }
    }

    if (!success) {
        if (e) {
            NSDictionary *userInfo = @{NSLocalizedDescriptionKey : @"Unable to parse"};
            *e = [NSError errorWithDomain:@"SpeakableBytes" code:1 userInfo:userInfo];
        }
        return nil;
    }
    return data;
}


@end