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.
157 lines
6.1 KiB
Objective-C
157 lines
6.1 KiB
Objective-C
#import "StringUtil.h"
|
|
#import "DataUtil.h"
|
|
#import "Constraints.h"
|
|
#import "NumberUtil.h"
|
|
|
|
@implementation NSString (Util)
|
|
-(NSData*) decodedAsHexString {
|
|
require(self.length % 2 == 0);
|
|
|
|
NSUInteger n = self.length / 2;
|
|
uint8_t result[n];
|
|
for (NSUInteger i = 0; i < n; i++) {
|
|
unsigned int r;
|
|
NSScanner* scanner = [NSScanner scannerWithString:[self substringWithRange:NSMakeRange(i*2, 2)]];
|
|
checkOperation([scanner scanHexInt:&r]);
|
|
checkOperation(r < 256);
|
|
result[i] = (uint8_t)r;
|
|
}
|
|
return [NSData dataWithBytes:result length:sizeof(result)];
|
|
}
|
|
-(NSData*) decodedAsSpaceSeparatedHexString{
|
|
NSArray* hexComponents = [self componentsSeparatedByString:@" "];
|
|
|
|
NSMutableData* result = [NSMutableData new];
|
|
for (NSString* component in hexComponents) {
|
|
unsigned int r;
|
|
NSScanner* scanner = [NSScanner scannerWithString:component];
|
|
checkOperation([scanner scanHexInt:&r]);
|
|
checkOperation(r < 256);
|
|
[result appendData:[NSData dataWithSingleByte:(uint8_t)r]];
|
|
}
|
|
|
|
return result;
|
|
}
|
|
-(NSData*) encodedAsUtf8 {
|
|
NSData* result = [self dataUsingEncoding:NSUTF8StringEncoding];
|
|
checkOperationDescribe(result != nil, @"Not a UTF8 string.");
|
|
return result;
|
|
}
|
|
-(NSData*) encodedAsAscii {
|
|
NSData* result = [self dataUsingEncoding:NSASCIIStringEncoding];
|
|
checkOperationDescribe(result != nil, @"Not an ascii string.");
|
|
return result;
|
|
}
|
|
-(NSRegularExpression*) toRegularExpression {
|
|
NSError *regexInitError = NULL;
|
|
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:self options:0 error:®exInitError];
|
|
checkOperation(regex != nil && regexInitError == NULL);
|
|
return regex;
|
|
}
|
|
-(NSString*) withMatchesAgainst:(NSRegularExpression*)regex replacedBy:(NSString*)replacement {
|
|
require(regex != nil);
|
|
require(replacement != nil);
|
|
NSMutableString* m = self.mutableCopy;
|
|
[regex replaceMatchesInString:m options:0 range:NSMakeRange(0, m.length) withTemplate:replacement];
|
|
return m;
|
|
}
|
|
-(bool) containsAnyMatches:(NSRegularExpression*)regex {
|
|
require(regex != nil);
|
|
return [regex numberOfMatchesInString:self options:0 range:NSMakeRange(0, self.length)] > 0;
|
|
}
|
|
-(NSString*) withPrefixRemovedElseNull:(NSString*)prefix {
|
|
require(prefix != nil);
|
|
if (prefix.length > 0 && ![self hasPrefix:prefix]) return nil;
|
|
return [self substringFromIndex:prefix.length];
|
|
}
|
|
-(NSData*) decodedAsJsonIntoData {
|
|
NSError* jsonParseError = nil;
|
|
id parsedJson = [NSJSONSerialization dataWithJSONObject:self.encodedAsUtf8 options:0 error:&jsonParseError];
|
|
checkOperationDescribe(jsonParseError == nil, ([NSString stringWithFormat:@"Invalid json: %@", self]));
|
|
checkOperationDescribe([parsedJson isKindOfClass:[NSData class]], @"Unexpected json data");
|
|
return parsedJson;
|
|
}
|
|
-(NSDictionary*) decodedAsJsonIntoDictionary {
|
|
NSError* jsonParseError = nil;
|
|
id parsedJson = [NSJSONSerialization JSONObjectWithData:self.encodedAsUtf8 options:0 error:&jsonParseError];
|
|
checkOperationDescribe(jsonParseError == nil, ([NSString stringWithFormat:@"Json parse error: %@, on json: %@", jsonParseError, self]));
|
|
checkOperationDescribe([parsedJson isKindOfClass:[NSDictionary class]], @"Unexpected json data");
|
|
return parsedJson;
|
|
}
|
|
-(NSData*) decodedAsBase64Data {
|
|
const NSUInteger BitsPerBase64Word = 6;
|
|
const NSUInteger BitsPerByte = 8;
|
|
const uint8_t Base64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
|
|
uint8_t CharToValueMap[256];
|
|
for (NSUInteger i = 0; i < 256; i++) {
|
|
CharToValueMap[i] = 255;
|
|
}
|
|
for (uint8_t i = 0; i < 64; i++) {
|
|
CharToValueMap[Base64Chars[i]] = i;
|
|
}
|
|
|
|
// Determine amount of information (based on length and padding)
|
|
NSUInteger paddingCount = 0;
|
|
while (paddingCount < 2 && paddingCount < self.length - 1 && [self characterAtIndex:self.length - paddingCount - 1] == '=') {
|
|
paddingCount += 1;
|
|
}
|
|
NSUInteger base64WordCount = self.length - paddingCount;
|
|
NSUInteger bitCount = self.length*BitsPerBase64Word - paddingCount*BitsPerByte;
|
|
NSUInteger byteCount = bitCount / BitsPerByte;
|
|
checkOperation(bitCount % BitsPerByte == 0);
|
|
|
|
// ASCII to base 64
|
|
NSData* asciiData = self.encodedAsAscii;
|
|
uint8_t base64Words[base64WordCount];
|
|
for (NSUInteger i = 0; i < base64WordCount; i++) {
|
|
base64Words[i] = CharToValueMap[[asciiData uint8At:i]];
|
|
require(base64Words[i] < 64);
|
|
}
|
|
|
|
// base 64 to base 2
|
|
bool bits[bitCount];
|
|
for (NSUInteger i = 0; i < base64WordCount; i++) {
|
|
for (NSUInteger j = 0; j < BitsPerBase64Word; j++) {
|
|
NSUInteger k = (i+1)*BitsPerBase64Word - 1 - j;
|
|
if (k >= bitCount) continue; // may occur due to padding
|
|
bits[k] = ((base64Words[i] >> j) & 1) != 0;
|
|
}
|
|
}
|
|
|
|
// base 2 to base 256
|
|
uint8_t bytes[byteCount];
|
|
for (NSUInteger i = 0; i < byteCount; i++) {
|
|
bytes[i] = 0;
|
|
for (NSUInteger j = 0; j < BitsPerByte; j++) {
|
|
NSUInteger k = (i+1)*BitsPerByte - 1 - j;
|
|
if (bits[k]) bytes[i] |= 1 << j;
|
|
}
|
|
}
|
|
|
|
return [NSData dataWithBytes:bytes length:sizeof(bytes)];
|
|
}
|
|
-(NSNumber*) tryParseAsDecimalNumber {
|
|
NSNumberFormatter* formatter = [NSNumberFormatter new];
|
|
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
|
|
return [formatter numberFromString:self];
|
|
}
|
|
-(NSNumber*) tryParseAsUnsignedInteger {
|
|
NSNumber* value = [self tryParseAsDecimalNumber];
|
|
return value.hasUnsignedIntegerValue ? value : nil;
|
|
}
|
|
-(NSString*) removeAllCharactersIn:(NSCharacterSet*)characterSet {
|
|
require(characterSet != nil);
|
|
return [[self componentsSeparatedByCharactersInSet:characterSet] componentsJoinedByString:@""];
|
|
}
|
|
-(NSString*) digitsOnly {
|
|
return [self removeAllCharactersIn:[NSCharacterSet.decimalDigitCharacterSet invertedSet]];
|
|
}
|
|
-(NSString*) withCharactersInRange:(NSRange)range replacedBy:(NSString*)substring {
|
|
NSMutableString* result = self.mutableCopy;
|
|
[result replaceCharactersInRange:range withString:substring];
|
|
return result;
|
|
}
|
|
|
|
@end
|