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
 |