mirror of https://github.com/oxen-io/session-ios
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
239 lines
7.5 KiB
Objective-C
239 lines
7.5 KiB
Objective-C
// Copyright 2008 Cyrus Najmabadi
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#import "TextFormat.h"
|
|
|
|
#import "Utilities.h"
|
|
|
|
@implementation PBTextFormat
|
|
|
|
|
|
BOOL allZeroes(NSString* string);
|
|
BOOL allZeroes(NSString* string) {
|
|
for (int i = 0; i < (int32_t)string.length; i++) {
|
|
if ([string characterAtIndex:(NSUInteger)i] != '0') {
|
|
return NO;
|
|
}
|
|
}
|
|
|
|
return YES;
|
|
}
|
|
|
|
|
|
/** Is this an octal digit? */
|
|
BOOL isOctal(unichar c);
|
|
BOOL isOctal(unichar c) {
|
|
return '0' <= c && c <= '7';
|
|
}
|
|
|
|
|
|
/** Is this an octal digit? */
|
|
BOOL isDecimal(unichar c);
|
|
BOOL isDecimal(unichar c) {
|
|
return '0' <= c && c <= '9';
|
|
}
|
|
|
|
/** Is this a hex digit? */
|
|
BOOL isHex(unichar c);
|
|
BOOL isHex(unichar c) {
|
|
return
|
|
isDecimal(c) ||
|
|
('a' <= c && c <= 'f') ||
|
|
('A' <= c && c <= 'F');
|
|
}
|
|
|
|
|
|
+ (int64_t) parseInteger:(NSString*) text
|
|
isSigned:(BOOL) isSigned
|
|
isLong:(BOOL) isLong {
|
|
if (text.length == 0) {
|
|
@throw [NSException exceptionWithName:@"NumberFormat" reason:@"Number was blank" userInfo:nil];
|
|
}
|
|
|
|
if (isblank([text characterAtIndex:0])) {
|
|
@throw [NSException exceptionWithName:@"NumberFormat" reason:@"Invalid character" userInfo:nil];
|
|
}
|
|
|
|
if ([text hasPrefix:@"-"]) {
|
|
if (!isSigned) {
|
|
@throw [NSException exceptionWithName:@"NumberFormat" reason:@"Number must be positive" userInfo:nil];
|
|
}
|
|
}
|
|
|
|
// now call into the appropriate conversion utilities.
|
|
int64_t result;
|
|
const char* in_string = text.UTF8String;
|
|
char* out_string = NULL;
|
|
errno = 0;
|
|
if (isLong) {
|
|
if (isSigned) {
|
|
result = strtoll(in_string, &out_string, 0);
|
|
} else {
|
|
result = convertUInt64ToInt64(strtoull(in_string, &out_string, 0));
|
|
}
|
|
} else {
|
|
if (isSigned) {
|
|
result = strtol(in_string, &out_string, 0);
|
|
} else {
|
|
result = convertUInt32ToInt32((unsigned int)strtoul(in_string, &out_string, 0));
|
|
}
|
|
}
|
|
|
|
// from the man pages:
|
|
// (Thus, i* tr is not `\0' but **endptr is `\0' on return, the entire
|
|
// string was valid.)
|
|
if (*in_string == 0 || *out_string != 0) {
|
|
@throw [NSException exceptionWithName:@"NumberFormat" reason:@"IllegalNumber" userInfo:nil];
|
|
}
|
|
|
|
if (errno == ERANGE) {
|
|
@throw [NSException exceptionWithName:@"NumberFormat" reason:@"Number out of range" userInfo:nil];
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse a 32-bit signed integer from the text. This function recognizes
|
|
* the prefixes "0x" and "0" to signify hexidecimal and octal numbers,
|
|
* respectively.
|
|
*/
|
|
+ (int32_t) parseInt32:(NSString*) text {
|
|
return (int32_t)[self parseInteger:text isSigned:YES isLong:NO];
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse a 32-bit unsigned integer from the text. This function recognizes
|
|
* the prefixes "0x" and "0" to signify hexidecimal and octal numbers,
|
|
* respectively. The result is coerced to a (signed) {@code int} when returned.
|
|
*/
|
|
+ (int32_t) parseUInt32:(NSString*) text {
|
|
return (int32_t)[self parseInteger:text isSigned:NO isLong:NO];
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse a 64-bit signed integer from the text. This function recognizes
|
|
* the prefixes "0x" and "0" to signify hexidecimal and octal numbers,
|
|
* respectively.
|
|
*/
|
|
+ (int64_t) parseInt64:(NSString*) text {
|
|
return [self parseInteger:text isSigned:YES isLong:YES];
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse a 64-bit unsigned integer from the text. This function recognizes
|
|
* the prefixes "0x" and "0" to signify hexidecimal and octal numbers,
|
|
* respectively. The result is coerced to a (signed) {@code long} when
|
|
* returned.
|
|
*/
|
|
+ (int64_t) parseUInt64:(NSString*) text {
|
|
return [self parseInteger:text isSigned:NO isLong:YES];
|
|
}
|
|
|
|
/**
|
|
* Interpret a character as a digit (in any base up to 36) and return the
|
|
* numeric value. This is like {@code Character.digit()} but we don't accept
|
|
* non-ASCII digits.
|
|
*/
|
|
int32_t digitValue(unichar c);
|
|
int32_t digitValue(unichar c) {
|
|
if ('0' <= c && c <= '9') {
|
|
return c - '0';
|
|
} else if ('a' <= c && c <= 'z') {
|
|
return c - 'a' + 10;
|
|
} else {
|
|
return c - 'A' + 10;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Un-escape a byte sequence as escaped using
|
|
* {@link #escapeBytes(ByteString)}. Two-digit hex escapes (starting with
|
|
* "\x") are also recognized.
|
|
*/
|
|
+ (NSData*) unescapeBytes:(NSString*) input {
|
|
NSMutableData* result = [NSMutableData dataWithLength:input.length];
|
|
|
|
int32_t pos = 0;
|
|
for (int32_t i = 0; i < (int32_t)input.length; i++) {
|
|
unichar c = [input characterAtIndex:(NSUInteger)i];
|
|
if (c == '\\') {
|
|
if (i + 1 < (int32_t)input.length) {
|
|
++i;
|
|
c = [input characterAtIndex:(NSUInteger)i];
|
|
if (isOctal(c)) {
|
|
// Octal escape.
|
|
int32_t code = digitValue(c);
|
|
if (i + 1 < (int32_t)input.length && isOctal([input characterAtIndex:(NSUInteger)(i + 1)])) {
|
|
++i;
|
|
code = code * 8 + digitValue([input characterAtIndex:(NSUInteger)i]);
|
|
}
|
|
if (i + 1 < (int32_t)input.length && isOctal([input characterAtIndex:(NSUInteger)(i + 1)])) {
|
|
++i;
|
|
code = code * 8 + digitValue([input characterAtIndex:(NSUInteger)i]);
|
|
}
|
|
((int8_t*)result.mutableBytes)[pos++] = (int8_t)code;
|
|
} else {
|
|
switch (c) {
|
|
case 'a' : ((int8_t*)result.mutableBytes)[pos++] = 0x07; break;
|
|
case 'b' : ((int8_t*)result.mutableBytes)[pos++] = '\b'; break;
|
|
case 'f' : ((int8_t*)result.mutableBytes)[pos++] = '\f'; break;
|
|
case 'n' : ((int8_t*)result.mutableBytes)[pos++] = '\n'; break;
|
|
case 'r' : ((int8_t*)result.mutableBytes)[pos++] = '\r'; break;
|
|
case 't' : ((int8_t*)result.mutableBytes)[pos++] = '\t'; break;
|
|
case 'v' : ((int8_t*)result.mutableBytes)[pos++] = 0x0b; break;
|
|
case '\\': ((int8_t*)result.mutableBytes)[pos++] = '\\'; break;
|
|
case '\'': ((int8_t*)result.mutableBytes)[pos++] = '\''; break;
|
|
case '"' : ((int8_t*)result.mutableBytes)[pos++] = '\"'; break;
|
|
|
|
case 'x': // hex escape
|
|
{
|
|
int32_t code = 0;
|
|
if (i + 1 < (int32_t)input.length && isHex([input characterAtIndex:(NSUInteger)(i + 1)])) {
|
|
++i;
|
|
code = digitValue([input characterAtIndex:(NSUInteger)i]);
|
|
} else {
|
|
@throw [NSException exceptionWithName:@"InvalidEscape" reason:@"Invalid escape sequence: '\\x' with no digits" userInfo:nil];
|
|
}
|
|
if (i + 1 < (int32_t)input.length && isHex([input characterAtIndex:(NSUInteger)(i + 1)])) {
|
|
++i;
|
|
code = code * 16 + digitValue([input characterAtIndex:(NSUInteger)i]);
|
|
}
|
|
((int8_t*)result.mutableBytes)[pos++] = (int8_t)code;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
@throw [NSException exceptionWithName:@"InvalidEscape" reason:@"Invalid escape sequence" userInfo:nil];
|
|
}
|
|
}
|
|
} else {
|
|
@throw [NSException exceptionWithName:@"InvalidEscape" reason:@"Invalid escape sequence: '\\' at end of string" userInfo:nil];
|
|
}
|
|
} else {
|
|
((int8_t*)result.mutableBytes)[pos++] = (int8_t)c;
|
|
}
|
|
}
|
|
|
|
[result setLength:(NSUInteger)pos];
|
|
return result;
|
|
}
|
|
|
|
@end
|