session-ios/Libraries/ProtocolBuffers/TextFormat.m

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