mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
1445 lines
48 KiB
Plaintext
1445 lines
48 KiB
Plaintext
#import "TGStringUtils.h"
|
||
|
||
#import "LegacyComponentsInternal.h"
|
||
|
||
#import <CommonCrypto/CommonDigest.h>
|
||
|
||
#import "TGLocalization.h"
|
||
#import "TGPluralization.h"
|
||
|
||
typedef struct {
|
||
__unsafe_unretained NSString *escapeSequence;
|
||
unichar uchar;
|
||
} HTMLEscapeMap;
|
||
|
||
// Taken from http://www.w3.org/TR/xhtml1/dtds.html#a_dtd_Special_characters
|
||
// Ordered by uchar lowest to highest for bsearching
|
||
static HTMLEscapeMap gAsciiHTMLEscapeMap[] = {
|
||
// A.2.2. Special characters
|
||
{ @""", 34 },
|
||
{ @"&", 38 },
|
||
{ @"'", 39 },
|
||
{ @"<", 60 },
|
||
{ @">", 62 },
|
||
|
||
// A.2.1. Latin-1 characters
|
||
{ @" ", 160 },
|
||
{ @"¡", 161 },
|
||
{ @"¢", 162 },
|
||
{ @"£", 163 },
|
||
{ @"¤", 164 },
|
||
{ @"¥", 165 },
|
||
{ @"¦", 166 },
|
||
{ @"§", 167 },
|
||
{ @"¨", 168 },
|
||
{ @"©", 169 },
|
||
{ @"ª", 170 },
|
||
{ @"«", 171 },
|
||
{ @"¬", 172 },
|
||
{ @"­", 173 },
|
||
{ @"®", 174 },
|
||
{ @"¯", 175 },
|
||
{ @"°", 176 },
|
||
{ @"±", 177 },
|
||
{ @"²", 178 },
|
||
{ @"³", 179 },
|
||
{ @"´", 180 },
|
||
{ @"µ", 181 },
|
||
{ @"¶", 182 },
|
||
{ @"·", 183 },
|
||
{ @"¸", 184 },
|
||
{ @"¹", 185 },
|
||
{ @"º", 186 },
|
||
{ @"»", 187 },
|
||
{ @"¼", 188 },
|
||
{ @"½", 189 },
|
||
{ @"¾", 190 },
|
||
{ @"¿", 191 },
|
||
{ @"À", 192 },
|
||
{ @"Á", 193 },
|
||
{ @"Â", 194 },
|
||
{ @"Ã", 195 },
|
||
{ @"Ä", 196 },
|
||
{ @"Å", 197 },
|
||
{ @"Æ", 198 },
|
||
{ @"Ç", 199 },
|
||
{ @"È", 200 },
|
||
{ @"É", 201 },
|
||
{ @"Ê", 202 },
|
||
{ @"Ë", 203 },
|
||
{ @"Ì", 204 },
|
||
{ @"Í", 205 },
|
||
{ @"Î", 206 },
|
||
{ @"Ï", 207 },
|
||
{ @"Ð", 208 },
|
||
{ @"Ñ", 209 },
|
||
{ @"Ò", 210 },
|
||
{ @"Ó", 211 },
|
||
{ @"Ô", 212 },
|
||
{ @"Õ", 213 },
|
||
{ @"Ö", 214 },
|
||
{ @"×", 215 },
|
||
{ @"Ø", 216 },
|
||
{ @"Ù", 217 },
|
||
{ @"Ú", 218 },
|
||
{ @"Û", 219 },
|
||
{ @"Ü", 220 },
|
||
{ @"Ý", 221 },
|
||
{ @"Þ", 222 },
|
||
{ @"ß", 223 },
|
||
{ @"à", 224 },
|
||
{ @"á", 225 },
|
||
{ @"â", 226 },
|
||
{ @"ã", 227 },
|
||
{ @"ä", 228 },
|
||
{ @"å", 229 },
|
||
{ @"æ", 230 },
|
||
{ @"ç", 231 },
|
||
{ @"è", 232 },
|
||
{ @"é", 233 },
|
||
{ @"ê", 234 },
|
||
{ @"ë", 235 },
|
||
{ @"ì", 236 },
|
||
{ @"í", 237 },
|
||
{ @"î", 238 },
|
||
{ @"ï", 239 },
|
||
{ @"ð", 240 },
|
||
{ @"ñ", 241 },
|
||
{ @"ò", 242 },
|
||
{ @"ó", 243 },
|
||
{ @"ô", 244 },
|
||
{ @"õ", 245 },
|
||
{ @"ö", 246 },
|
||
{ @"÷", 247 },
|
||
{ @"ø", 248 },
|
||
{ @"ù", 249 },
|
||
{ @"ú", 250 },
|
||
{ @"û", 251 },
|
||
{ @"ü", 252 },
|
||
{ @"ý", 253 },
|
||
{ @"þ", 254 },
|
||
{ @"ÿ", 255 },
|
||
|
||
// A.2.2. Special characters cont'd
|
||
{ @"Œ", 338 },
|
||
{ @"œ", 339 },
|
||
{ @"Š", 352 },
|
||
{ @"š", 353 },
|
||
{ @"Ÿ", 376 },
|
||
|
||
// A.2.3. Symbols
|
||
{ @"ƒ", 402 },
|
||
|
||
// A.2.2. Special characters cont'd
|
||
{ @"ˆ", 710 },
|
||
{ @"˜", 732 },
|
||
|
||
// A.2.3. Symbols cont'd
|
||
{ @"Α", 913 },
|
||
{ @"Β", 914 },
|
||
{ @"Γ", 915 },
|
||
{ @"Δ", 916 },
|
||
{ @"Ε", 917 },
|
||
{ @"Ζ", 918 },
|
||
{ @"Η", 919 },
|
||
{ @"Θ", 920 },
|
||
{ @"Ι", 921 },
|
||
{ @"Κ", 922 },
|
||
{ @"Λ", 923 },
|
||
{ @"Μ", 924 },
|
||
{ @"Ν", 925 },
|
||
{ @"Ξ", 926 },
|
||
{ @"Ο", 927 },
|
||
{ @"Π", 928 },
|
||
{ @"Ρ", 929 },
|
||
{ @"Σ", 931 },
|
||
{ @"Τ", 932 },
|
||
{ @"Υ", 933 },
|
||
{ @"Φ", 934 },
|
||
{ @"Χ", 935 },
|
||
{ @"Ψ", 936 },
|
||
{ @"Ω", 937 },
|
||
{ @"α", 945 },
|
||
{ @"β", 946 },
|
||
{ @"γ", 947 },
|
||
{ @"δ", 948 },
|
||
{ @"ε", 949 },
|
||
{ @"ζ", 950 },
|
||
{ @"η", 951 },
|
||
{ @"θ", 952 },
|
||
{ @"ι", 953 },
|
||
{ @"κ", 954 },
|
||
{ @"λ", 955 },
|
||
{ @"μ", 956 },
|
||
{ @"ν", 957 },
|
||
{ @"ξ", 958 },
|
||
{ @"ο", 959 },
|
||
{ @"π", 960 },
|
||
{ @"ρ", 961 },
|
||
{ @"ς", 962 },
|
||
{ @"σ", 963 },
|
||
{ @"τ", 964 },
|
||
{ @"υ", 965 },
|
||
{ @"φ", 966 },
|
||
{ @"χ", 967 },
|
||
{ @"ψ", 968 },
|
||
{ @"ω", 969 },
|
||
{ @"ϑ", 977 },
|
||
{ @"ϒ", 978 },
|
||
{ @"ϖ", 982 },
|
||
|
||
// A.2.2. Special characters cont'd
|
||
{ @" ", 8194 },
|
||
{ @" ", 8195 },
|
||
{ @" ", 8201 },
|
||
{ @"‌", 8204 },
|
||
{ @"‍", 8205 },
|
||
{ @"‎", 8206 },
|
||
{ @"‏", 8207 },
|
||
{ @"–", 8211 },
|
||
{ @"—", 8212 },
|
||
{ @"‘", 8216 },
|
||
{ @"’", 8217 },
|
||
{ @"‚", 8218 },
|
||
{ @"“", 8220 },
|
||
{ @"”", 8221 },
|
||
{ @"„", 8222 },
|
||
{ @"†", 8224 },
|
||
{ @"‡", 8225 },
|
||
// A.2.3. Symbols cont'd
|
||
{ @"•", 8226 },
|
||
{ @"…", 8230 },
|
||
|
||
// A.2.2. Special characters cont'd
|
||
{ @"‰", 8240 },
|
||
|
||
// A.2.3. Symbols cont'd
|
||
{ @"′", 8242 },
|
||
{ @"″", 8243 },
|
||
|
||
// A.2.2. Special characters cont'd
|
||
{ @"‹", 8249 },
|
||
{ @"›", 8250 },
|
||
|
||
// A.2.3. Symbols cont'd
|
||
{ @"‾", 8254 },
|
||
{ @"⁄", 8260 },
|
||
|
||
// A.2.2. Special characters cont'd
|
||
{ @"€", 8364 },
|
||
|
||
// A.2.3. Symbols cont'd
|
||
{ @"ℑ", 8465 },
|
||
{ @"℘", 8472 },
|
||
{ @"ℜ", 8476 },
|
||
{ @"™", 8482 },
|
||
{ @"ℵ", 8501 },
|
||
{ @"←", 8592 },
|
||
{ @"↑", 8593 },
|
||
{ @"→", 8594 },
|
||
{ @"↓", 8595 },
|
||
{ @"↔", 8596 },
|
||
{ @"↵", 8629 },
|
||
{ @"⇐", 8656 },
|
||
{ @"⇑", 8657 },
|
||
{ @"⇒", 8658 },
|
||
{ @"⇓", 8659 },
|
||
{ @"⇔", 8660 },
|
||
{ @"∀", 8704 },
|
||
{ @"∂", 8706 },
|
||
{ @"∃", 8707 },
|
||
{ @"∅", 8709 },
|
||
{ @"∇", 8711 },
|
||
{ @"∈", 8712 },
|
||
{ @"∉", 8713 },
|
||
{ @"∋", 8715 },
|
||
{ @"∏", 8719 },
|
||
{ @"∑", 8721 },
|
||
{ @"−", 8722 },
|
||
{ @"∗", 8727 },
|
||
{ @"√", 8730 },
|
||
{ @"∝", 8733 },
|
||
{ @"∞", 8734 },
|
||
{ @"∠", 8736 },
|
||
{ @"∧", 8743 },
|
||
{ @"∨", 8744 },
|
||
{ @"∩", 8745 },
|
||
{ @"∪", 8746 },
|
||
{ @"∫", 8747 },
|
||
{ @"∴", 8756 },
|
||
{ @"∼", 8764 },
|
||
{ @"≅", 8773 },
|
||
{ @"≈", 8776 },
|
||
{ @"≠", 8800 },
|
||
{ @"≡", 8801 },
|
||
{ @"≤", 8804 },
|
||
{ @"≥", 8805 },
|
||
{ @"⊂", 8834 },
|
||
{ @"⊃", 8835 },
|
||
{ @"⊄", 8836 },
|
||
{ @"⊆", 8838 },
|
||
{ @"⊇", 8839 },
|
||
{ @"⊕", 8853 },
|
||
{ @"⊗", 8855 },
|
||
{ @"⊥", 8869 },
|
||
{ @"⋅", 8901 },
|
||
{ @"⌈", 8968 },
|
||
{ @"⌉", 8969 },
|
||
{ @"⌊", 8970 },
|
||
{ @"⌋", 8971 },
|
||
{ @"⟨", 9001 },
|
||
{ @"⟩", 9002 },
|
||
{ @"◊", 9674 },
|
||
{ @"♠", 9824 },
|
||
{ @"♣", 9827 },
|
||
{ @"♥", 9829 },
|
||
{ @"♦", 9830 }
|
||
};
|
||
|
||
@implementation TGStringUtils
|
||
|
||
+ (void)reset
|
||
{
|
||
|
||
}
|
||
|
||
+ (NSString *)stringByEscapingForURL:(NSString *)string
|
||
{
|
||
static NSString * const kAFLegalCharactersToBeEscaped = @"?!@#$^&%*+=,.:;'\"`<>()[]{}/\\|~ ";
|
||
|
||
#pragma clang diagnostic push
|
||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||
NSString *unescapedString = [string stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
||
if (unescapedString == nil)
|
||
unescapedString = string;
|
||
|
||
return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)unescapedString, NULL, (CFStringRef)kAFLegalCharactersToBeEscaped, CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding));
|
||
#pragma clang diagnostic pop
|
||
}
|
||
|
||
+ (NSString *)stringByEscapingForActorURL:(NSString *)string
|
||
{
|
||
static NSString * const kAFLegalCharactersToBeEscaped = @"?!@#$^&%*+=,:;'\"`<>()[]{}/\\|~ ";
|
||
|
||
#pragma clang diagnostic push
|
||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||
NSString *unescapedString = [string stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
||
if (unescapedString == nil)
|
||
unescapedString = string;
|
||
|
||
return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)unescapedString, NULL, (CFStringRef)kAFLegalCharactersToBeEscaped, CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding));
|
||
#pragma clang diagnostic pop
|
||
}
|
||
|
||
+ (NSString *)stringByEncodingInBase64:(NSData *)data
|
||
{
|
||
NSUInteger length = [data length];
|
||
NSMutableData *mutableData = [[NSMutableData alloc] initWithLength:((length + 2) / 3) * 4];
|
||
|
||
uint8_t *input = (uint8_t *)[data bytes];
|
||
uint8_t *output = (uint8_t *)[mutableData mutableBytes];
|
||
|
||
for (NSUInteger i = 0; i < length; i += 3)
|
||
{
|
||
NSUInteger value = 0;
|
||
for (NSUInteger j = i; j < (i + 3); j++)
|
||
{
|
||
value <<= 8;
|
||
if (j < length)
|
||
{
|
||
value |= (0xFF & input[j]);
|
||
}
|
||
}
|
||
|
||
static uint8_t const kAFBase64EncodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||
|
||
NSUInteger idx = (i / 3) * 4;
|
||
output[idx + 0] = kAFBase64EncodingTable[(value >> 18) & 0x3F];
|
||
output[idx + 1] = kAFBase64EncodingTable[(value >> 12) & 0x3F];
|
||
output[idx + 2] = (i + 1) < length ? kAFBase64EncodingTable[(value >> 6) & 0x3F] : '=';
|
||
output[idx + 3] = (i + 2) < length ? kAFBase64EncodingTable[(value >> 0) & 0x3F] : '=';
|
||
}
|
||
|
||
return [[NSString alloc] initWithData:mutableData encoding:NSASCIIStringEncoding];
|
||
}
|
||
|
||
+ (NSString *)stringByUnescapingFromHTML:(NSString *)srcString
|
||
{
|
||
NSRange range = NSMakeRange(0, [srcString length]);
|
||
NSRange subrange = [srcString rangeOfString:@"&" options:NSBackwardsSearch range:range];
|
||
NSRange tagSubrange = NSMakeRange(0, 0);
|
||
|
||
if (subrange.length == 0)
|
||
{
|
||
tagSubrange = [srcString rangeOfString:@"<" options:NSBackwardsSearch range:range];
|
||
if (tagSubrange.length == 0)
|
||
return srcString;
|
||
}
|
||
|
||
NSMutableString *finalString = [NSMutableString stringWithString:srcString];
|
||
if (subrange.length != 0)
|
||
{
|
||
do
|
||
{
|
||
NSRange semiColonRange = NSMakeRange(subrange.location, NSMaxRange(range) - subrange.location);
|
||
semiColonRange = [srcString rangeOfString:@";" options:0 range:semiColonRange];
|
||
range = NSMakeRange(0, subrange.location);
|
||
// if we don't find a semicolon in the range, we don't have a sequence
|
||
if (semiColonRange.location == NSNotFound)
|
||
{
|
||
continue;
|
||
}
|
||
NSRange escapeRange = NSMakeRange(subrange.location, semiColonRange.location - subrange.location + 1);
|
||
NSString *escapeString = [srcString substringWithRange:escapeRange];
|
||
NSUInteger length = [escapeString length];
|
||
|
||
// a squence must be longer than 3 (<) and less than 11 (ϑ)
|
||
if (length > 3 && length < 11)
|
||
{
|
||
if ([escapeString characterAtIndex:1] == '#')
|
||
{
|
||
unichar char2 = [escapeString characterAtIndex:2];
|
||
if (char2 == 'x' || char2 == 'X') {
|
||
// Hex escape squences £
|
||
NSString *hexSequence = [escapeString substringWithRange:NSMakeRange(3, length - 4)];
|
||
NSScanner *scanner = [NSScanner scannerWithString:hexSequence];
|
||
unsigned value;
|
||
if ([scanner scanHexInt:&value] &&
|
||
value < USHRT_MAX &&
|
||
value > 0
|
||
&& [scanner scanLocation] == length - 4) {
|
||
unichar uchar = (unichar)value;
|
||
NSString *charString = [NSString stringWithCharacters:&uchar length:1];
|
||
[finalString replaceCharactersInRange:escapeRange withString:charString];
|
||
}
|
||
|
||
}
|
||
else
|
||
{
|
||
// Decimal Sequences {
|
||
NSString *numberSequence = [escapeString substringWithRange:NSMakeRange(2, length - 3)];
|
||
NSScanner *scanner = [NSScanner scannerWithString:numberSequence];
|
||
int value;
|
||
if ([scanner scanInt:&value] &&
|
||
value < USHRT_MAX &&
|
||
value > 0
|
||
&& [scanner scanLocation] == length - 3)
|
||
{
|
||
unichar uchar = (unichar)value;
|
||
NSString *charString = [NSString stringWithCharacters:&uchar length:1];
|
||
[finalString replaceCharactersInRange:escapeRange withString:charString];
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
for (unsigned i = 0; i < sizeof(gAsciiHTMLEscapeMap) / sizeof(HTMLEscapeMap); ++i)
|
||
{
|
||
if ([escapeString isEqualToString:gAsciiHTMLEscapeMap[i].escapeSequence])
|
||
{
|
||
[finalString replaceCharactersInRange:escapeRange withString:[NSString stringWithCharacters:&gAsciiHTMLEscapeMap[i].uchar length:1]];
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} while ((subrange = [srcString rangeOfString:@"&" options:NSBackwardsSearch range:range]).length != 0);
|
||
}
|
||
|
||
[finalString replaceOccurrencesOfString:@"<br/>" withString:@"\n" options:NSLiteralSearch range:NSMakeRange(0, finalString.length)];
|
||
|
||
return finalString;
|
||
}
|
||
|
||
+ (NSString *)stringWithLocalizedNumber:(NSInteger)number
|
||
{
|
||
return [self stringWithLocalizedNumberCharacters:[[NSString alloc] initWithFormat:@"%d", (int)number]];
|
||
}
|
||
|
||
+ (NSString *)stringWithLocalizedNumberCharacters:(NSString *)string
|
||
{
|
||
NSString *resultString = string;
|
||
|
||
if (TGIsArabic())
|
||
{
|
||
static NSString *arabicNumbers = @"٠١٢٣٤٥٦٧٨٩";
|
||
NSMutableString *mutableString = [[NSMutableString alloc] init];
|
||
for (int i = 0; i < (int)string.length; i++)
|
||
{
|
||
unichar c = [string characterAtIndex:i];
|
||
if (c >= '0' && c <= '9')
|
||
[mutableString replaceCharactersInRange:NSMakeRange(mutableString.length, 0) withString:[arabicNumbers substringWithRange:NSMakeRange(c - '0', 1)]];
|
||
else
|
||
[mutableString replaceCharactersInRange:NSMakeRange(mutableString.length, 0) withString:[string substringWithRange:NSMakeRange(i, 1)]];
|
||
}
|
||
resultString = mutableString;
|
||
}
|
||
|
||
return resultString;
|
||
}
|
||
|
||
+ (NSString *)md5:(NSString *)string
|
||
{
|
||
/*static const char *md5PropertyKey = "MD5Key";
|
||
NSString *result = objc_getAssociatedObject(string, md5PropertyKey);
|
||
if (result != nil)
|
||
return result;*/
|
||
|
||
const char *ptr = [string UTF8String];
|
||
unsigned char md5Buffer[16];
|
||
CC_MD5(ptr, (CC_LONG)[string lengthOfBytesUsingEncoding:NSUTF8StringEncoding], md5Buffer);
|
||
NSString *output = [[NSString alloc] initWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", md5Buffer[0], md5Buffer[1], md5Buffer[2], md5Buffer[3], md5Buffer[4], md5Buffer[5], md5Buffer[6], md5Buffer[7], md5Buffer[8], md5Buffer[9], md5Buffer[10], md5Buffer[11], md5Buffer[12], md5Buffer[13], md5Buffer[14], md5Buffer[15]];
|
||
//objc_setAssociatedObject(string, md5PropertyKey, output, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||
return output;
|
||
}
|
||
|
||
+ (NSString *)md5ForData:(NSData *)data {
|
||
unsigned char md5Buffer[16];
|
||
CC_MD5(data.bytes, (CC_LONG)data.length, md5Buffer);
|
||
NSString *output = [[NSString alloc] initWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", md5Buffer[0], md5Buffer[1], md5Buffer[2], md5Buffer[3], md5Buffer[4], md5Buffer[5], md5Buffer[6], md5Buffer[7], md5Buffer[8], md5Buffer[9], md5Buffer[10], md5Buffer[11], md5Buffer[12], md5Buffer[13], md5Buffer[14], md5Buffer[15]];
|
||
return output;
|
||
}
|
||
|
||
+ (NSDictionary *)argumentDictionaryInUrlString:(NSString *)string
|
||
{
|
||
NSMutableDictionary *queryStringDictionary = [[NSMutableDictionary alloc] init];
|
||
NSArray *urlComponents = [string componentsSeparatedByString:@"&"];
|
||
|
||
for (NSString *keyValuePair in urlComponents)
|
||
{
|
||
NSRange equalsSignRange = [keyValuePair rangeOfString:@"="];
|
||
if (equalsSignRange.location != NSNotFound) {
|
||
#pragma clang diagnostic push
|
||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||
NSString *key = [keyValuePair substringToIndex:equalsSignRange.location];
|
||
NSString *value = [[[keyValuePair substringFromIndex:equalsSignRange.location + equalsSignRange.length] stringByReplacingOccurrencesOfString:@"+" withString:@" "] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
||
#pragma clang diagnostic pop
|
||
|
||
[queryStringDictionary setObject:value forKey:key];
|
||
}
|
||
}
|
||
|
||
return queryStringDictionary;
|
||
}
|
||
|
||
+ (bool)stringContainsEmoji:(NSString *)string
|
||
{
|
||
__block bool returnValue = NO;
|
||
[string enumerateSubstringsInRange:NSMakeRange(0, [string length]) options:NSStringEnumerationByComposedCharacterSequences usingBlock:
|
||
^(NSString *substring, __unused NSRange substringRange, __unused NSRange enclosingRange, __unused BOOL *stop)
|
||
{
|
||
const unichar hs = [substring characterAtIndex:0];
|
||
|
||
if (0xd800 <= hs && hs <= 0xdbff)
|
||
{
|
||
if (substring.length > 1)
|
||
{
|
||
const unichar ls = [substring characterAtIndex:1];
|
||
const int uc = ((hs - 0xd800) * 0x400) + (ls - 0xdc00) + 0x10000;
|
||
if (0x1d000 <= uc && uc <= 0x1f77f)
|
||
{
|
||
returnValue = YES;
|
||
}
|
||
}
|
||
} else if (substring.length > 1)
|
||
{
|
||
const unichar ls = [substring characterAtIndex:1];
|
||
if (ls == 0x20e3)
|
||
{
|
||
returnValue = YES;
|
||
}
|
||
|
||
} else
|
||
{
|
||
if (0x2100 <= hs && hs <= 0x27ff)
|
||
{
|
||
returnValue = YES;
|
||
} else if (0x2B05 <= hs && hs <= 0x2b07)
|
||
{
|
||
returnValue = YES;
|
||
} else if (0x2934 <= hs && hs <= 0x2935)
|
||
{
|
||
returnValue = YES;
|
||
} else if (0x3297 <= hs && hs <= 0x3299)
|
||
{
|
||
returnValue = YES;
|
||
} else if (hs == 0xa9 || hs == 0xae || hs == 0x303d || hs == 0x3030 || hs == 0x2b55 || hs == 0x2b1c || hs == 0x2b1b || hs == 0x2b50)
|
||
{
|
||
returnValue = YES;
|
||
}
|
||
}
|
||
|
||
if (returnValue && stop != NULL)
|
||
*stop = true;
|
||
}];
|
||
|
||
return returnValue;
|
||
}
|
||
|
||
static bool isEmojiCharacter(NSString *singleChar)
|
||
{
|
||
const unichar high = [singleChar characterAtIndex:0];
|
||
|
||
if (0xd800 <= high && high <= 0xdbff && singleChar.length >= 2)
|
||
{
|
||
const unichar low = [singleChar characterAtIndex:1];
|
||
const int codepoint = ((high - 0xd800) * 0x400) + (low - 0xdc00) + 0x10000;
|
||
|
||
return (0x1d000 <= codepoint && codepoint <= 0x1f77f);
|
||
}
|
||
|
||
return (0x2100 <= high && high <= 0x27bf);
|
||
}
|
||
|
||
+ (bool)stringContainsEmojiOnly:(NSString *)string length:(NSUInteger *)length
|
||
{
|
||
if (string.length == 0)
|
||
return false;
|
||
|
||
__block bool result = true;
|
||
|
||
__block NSUInteger count = 0;
|
||
[string enumerateSubstringsInRange:NSMakeRange(0, string.length)
|
||
options:NSStringEnumerationByComposedCharacterSequences
|
||
usingBlock: ^(NSString *substring, __unused NSRange substringRange, __unused NSRange enclosingRange, BOOL *stop)
|
||
{
|
||
if (!isEmojiCharacter(substring))
|
||
{
|
||
result = false;
|
||
*stop = true;
|
||
}
|
||
count++;
|
||
}];
|
||
|
||
if (length != NULL)
|
||
*length = count;
|
||
|
||
return result;
|
||
}
|
||
|
||
+ (NSString *)stringForMessageTimerSeconds:(NSUInteger)seconds
|
||
{
|
||
if (seconds < 60)
|
||
{
|
||
int number = (int)seconds;
|
||
|
||
return [legacyEffectiveLocalization() getPluralized:@"MessageTimer.Seconds" count:(int32_t)number];
|
||
}
|
||
else if (seconds < 60 * 60)
|
||
{
|
||
int number = (int)seconds / 60;
|
||
|
||
return [legacyEffectiveLocalization() getPluralized:@"MessageTimer.Minutes" count:(int32_t)number];
|
||
}
|
||
else if (seconds < 60 * 60 * 24)
|
||
{
|
||
int number = (int)seconds / (60 * 60);
|
||
|
||
return [legacyEffectiveLocalization() getPluralized:@"MessageTimer.Hours" count:(int32_t)number];
|
||
}
|
||
else if (seconds < 60 * 60 * 24 * 7)
|
||
{
|
||
int number = (int)seconds / (60 * 60 * 24);
|
||
|
||
return [legacyEffectiveLocalization() getPluralized:@"MessageTimer.Days" count:(int32_t)number];
|
||
}
|
||
else if (seconds < 60 * 60 * 24 * 7 * 4)
|
||
{
|
||
int number = (int)seconds / (60 * 60 * 24 * 7);
|
||
|
||
return [legacyEffectiveLocalization() getPluralized:@"MessageTimer.Weeks" count:(int32_t)number];
|
||
}
|
||
else
|
||
{
|
||
int number = MAX(1, (int)ceilf((int)(seconds / (60 * 60 * 24 * 29))));
|
||
|
||
return [legacyEffectiveLocalization() getPluralized:@"MessageTimer.Months" count:(int32_t)number];
|
||
}
|
||
|
||
return @"";
|
||
}
|
||
|
||
+ (NSString *)stringForShortMessageTimerSeconds:(NSUInteger)seconds
|
||
{
|
||
if (seconds < 60)
|
||
{
|
||
int number = (int)seconds;
|
||
|
||
return [legacyEffectiveLocalization() getPluralized:@"MessageTimer.ShortSeconds" count:(int32_t)number];
|
||
}
|
||
else if (seconds < 60 * 60)
|
||
{
|
||
int number = (int)seconds / 60;
|
||
|
||
return [legacyEffectiveLocalization() getPluralized:@"MessageTimer.ShortMinutes" count:(int32_t)number];
|
||
}
|
||
else if (seconds < 60 * 60 * 24)
|
||
{
|
||
int number = (int)seconds / (60 * 60);
|
||
|
||
return [legacyEffectiveLocalization() getPluralized:@"MessageTimer.ShortHours" count:(int32_t)number];
|
||
}
|
||
else if (seconds < 60 * 60 * 24 * 7)
|
||
{
|
||
int number = (int)seconds / (60 * 60 * 24);
|
||
|
||
return [legacyEffectiveLocalization() getPluralized:@"MessageTimer.ShortDays" count:(int32_t)number];
|
||
}
|
||
else
|
||
{
|
||
int number = (int)seconds / (60 * 60 * 24 * 7);
|
||
|
||
return [legacyEffectiveLocalization() getPluralized:@"MessageTimer.ShortWeeks" count:(int32_t)number];
|
||
}
|
||
|
||
return @"";
|
||
}
|
||
|
||
+ (NSArray *)stringComponentsForMessageTimerSeconds:(NSUInteger)seconds
|
||
{
|
||
NSString *first = @"";
|
||
NSString *second = @"";
|
||
|
||
if (seconds < 60)
|
||
{
|
||
int number = (int)seconds;
|
||
|
||
NSString *format = TGLocalized([self integerValueFormat:@"MessageTimer.Seconds_" value:number]);
|
||
|
||
NSRange range = [format rangeOfString:@"%@"];
|
||
if (range.location != NSNotFound)
|
||
{
|
||
first = [[NSString alloc] initWithFormat:@"%d", number];
|
||
second = [[format substringFromIndex:range.location + range.length] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||
} else {
|
||
first = format;
|
||
}
|
||
}
|
||
else if (seconds < 60 * 60)
|
||
{
|
||
int number = (int)seconds / 60;
|
||
|
||
NSString *format = TGLocalized([self integerValueFormat:@"MessageTimer.Minutes_" value:number]);
|
||
|
||
NSRange range = [format rangeOfString:@"%@"];
|
||
if (range.location != NSNotFound)
|
||
{
|
||
first = [[NSString alloc] initWithFormat:@"%d", number];
|
||
second = [[format substringFromIndex:range.location + range.length] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||
} else {
|
||
first = format;
|
||
}
|
||
}
|
||
else if (seconds < 60 * 60 * 24)
|
||
{
|
||
int number = (int)seconds / (60 * 60);
|
||
|
||
NSString *format = TGLocalized([self integerValueFormat:@"MessageTimer.Hours_" value:number]);
|
||
|
||
NSRange range = [format rangeOfString:@"%@"];
|
||
if (range.location != NSNotFound)
|
||
{
|
||
first = [[NSString alloc] initWithFormat:@"%d", number];
|
||
second = [[format substringFromIndex:range.location + range.length] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||
} else {
|
||
first = format;
|
||
}
|
||
}
|
||
else if (seconds < 60 * 60 * 24 * 7)
|
||
{
|
||
int number = (int)seconds / (60 * 60 * 24);
|
||
|
||
NSString *format = TGLocalized([self integerValueFormat:@"MessageTimer.Days_" value:number]);
|
||
|
||
NSRange range = [format rangeOfString:@"%@"];
|
||
if (range.location != NSNotFound)
|
||
{
|
||
first = [[NSString alloc] initWithFormat:@"%d", number];
|
||
second = [[format substringFromIndex:range.location + range.length] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||
} else {
|
||
first = format;
|
||
}
|
||
}
|
||
else if (seconds < 60 * 60 * 24 * 30)
|
||
{
|
||
int number = (int)seconds / (60 * 60 * 24 * 7);
|
||
|
||
NSString *format = TGLocalized([self integerValueFormat:@"MessageTimer.Weeks_" value:number]);
|
||
|
||
NSRange range = [format rangeOfString:@"%@"];
|
||
if (range.location != NSNotFound)
|
||
{
|
||
first = [[NSString alloc] initWithFormat:@"%d", number];
|
||
second = [[format substringFromIndex:range.location + range.length] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||
} else {
|
||
first = format;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
int number = (int)ceilf((seconds / (60 * 60 * 24 * 30.5f)));
|
||
|
||
NSString *format = TGLocalized([self integerValueFormat:@"MessageTimer.Months_" value:number]);
|
||
|
||
NSRange range = [format rangeOfString:@"%@"];
|
||
if (range.location != NSNotFound)
|
||
{
|
||
first = [[NSString alloc] initWithFormat:@"%d", number];
|
||
second = [[format substringFromIndex:range.location + range.length] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||
} else {
|
||
first = format;
|
||
}
|
||
}
|
||
|
||
return @[first, second];
|
||
}
|
||
|
||
+ (NSString *)stringForCallDurationSeconds:(NSUInteger)seconds
|
||
{
|
||
if (seconds < 60)
|
||
{
|
||
int number = (int)seconds;
|
||
|
||
return [legacyEffectiveLocalization() getPluralized:@"Call.Seconds" count:number];
|
||
}
|
||
else
|
||
{
|
||
int number = (int)seconds / 60;
|
||
|
||
return [legacyEffectiveLocalization() getPluralized:@"Call.Minutes" count:number];
|
||
}
|
||
}
|
||
|
||
+ (NSString *)stringForShortCallDurationSeconds:(NSUInteger)seconds
|
||
{
|
||
if (seconds < 60)
|
||
{
|
||
int number = (int)seconds;
|
||
|
||
return [legacyEffectiveLocalization() getPluralized:@"Call.ShortSeconds" count:(int32_t)number];
|
||
}
|
||
else
|
||
{
|
||
int number = (int)seconds / 60;
|
||
|
||
return [legacyEffectiveLocalization() getPluralized:@"Call.ShortMinutes" count:(int32_t)number];
|
||
}
|
||
}
|
||
|
||
+ (NSString *)stringForUserCount:(NSUInteger)userCount
|
||
{
|
||
NSUInteger number = userCount;
|
||
|
||
return [legacyEffectiveLocalization() getPluralized:@"UserCount" count:(int32_t)number];
|
||
}
|
||
|
||
+ (NSString *)stringForFileSize:(int64_t)size
|
||
{
|
||
NSString *format = @"";
|
||
float floatSize = size;
|
||
bool useFloat = false;
|
||
|
||
if (floatSize < 1024)
|
||
format = TGLocalized(@"FileSize.B");
|
||
else if (size < 1024 * 1024)
|
||
{
|
||
format = TGLocalized(@"FileSize.KB");
|
||
floatSize = size / 1024;
|
||
}
|
||
else if (size < 1024 * 1024 * 1024)
|
||
{
|
||
format = TGLocalized(@"FileSize.MB");
|
||
floatSize = size / (1024 * 1024);
|
||
} else {
|
||
format = TGLocalized(@"FileSize.GB");
|
||
floatSize = size / (1024.0f * 1024.0f * 1024.0f);
|
||
useFloat = true;
|
||
}
|
||
|
||
if (useFloat) {
|
||
return [[NSString alloc] initWithFormat:format, [[NSString alloc] initWithFormat:@"%0.1f", floatSize]];
|
||
} else {
|
||
return [[NSString alloc] initWithFormat:format, [[NSString alloc] initWithFormat:@"%d", (int)floatSize]];
|
||
}
|
||
}
|
||
|
||
+ (NSString *)stringForFileSize:(int64_t)size precision:(NSInteger)precision
|
||
{
|
||
NSString *string = @"";
|
||
if (size < 1024)
|
||
{
|
||
string = [[NSString alloc] initWithFormat:TGLocalized(@"FileSize.B"), [[NSString alloc] initWithFormat:@"%d", (int)size]];}
|
||
else if (size < 1024 * 1024)
|
||
{
|
||
string = [[NSString alloc] initWithFormat:TGLocalized(@"FileSize.KB"), [[NSString alloc] initWithFormat:@"%d", (int)(size / 1024)]];
|
||
}
|
||
else
|
||
{
|
||
NSString *format = [NSString stringWithFormat:@"%%0.%df", (int)precision];
|
||
string = [[NSString alloc] initWithFormat:TGLocalized(@"FileSize.MB"), [[NSString alloc] initWithFormat:format, (CGFloat)(size / 1024.0f / 1024.0f)]];
|
||
}
|
||
|
||
return string;
|
||
}
|
||
|
||
+ (NSString *)integerValueFormat:(NSString *)prefix value:(NSInteger)value
|
||
{
|
||
TGLocalization *localization = legacyEffectiveLocalization();
|
||
NSString *form = @"any";
|
||
switch (TGPluralForm(localization.languageCodeHash, (int)value)) {
|
||
case TGPluralFormZero:
|
||
form = @"0";
|
||
break;
|
||
case TGPluralFormOne:
|
||
form = @"1";
|
||
break;
|
||
case TGPluralFormTwo:
|
||
form = @"2";
|
||
break;
|
||
case TGPluralFormFew:
|
||
form = @"3_10";
|
||
break;
|
||
case TGPluralFormMany:
|
||
form = @"many";
|
||
break;
|
||
case TGPluralFormOther:
|
||
form = @"any";
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
NSString *result = [prefix stringByAppendingString:form];
|
||
if ([localization contains:result]) {
|
||
return result;
|
||
} else {
|
||
return [prefix stringByAppendingString:@"any"];
|
||
}
|
||
}
|
||
|
||
+ (NSString *)stringForMuteInterval:(int)value
|
||
{
|
||
value = MAX(1 * 60, value);
|
||
|
||
if (value < 24 * 60 * 60)
|
||
{
|
||
value /= 60 * 60;
|
||
NSString *format = TGLocalized([self integerValueFormat:@"MuteFor.Hours_" value:value]);
|
||
return [[NSString alloc] initWithFormat:format, [[NSString alloc] initWithFormat:@"%d", value]];
|
||
}
|
||
else
|
||
{
|
||
value /= 24 * 60 * 60;
|
||
NSString *format = TGLocalized([self integerValueFormat:@"MuteFor.Days_" value:value]);
|
||
return [[NSString alloc] initWithFormat:format, [[NSString alloc] initWithFormat:@"%d", value]];
|
||
}
|
||
|
||
return @"";
|
||
}
|
||
|
||
+ (NSString *)stringForRemainingMuteInterval:(int)value
|
||
{
|
||
value = MAX(1 * 60, value);
|
||
|
||
if (value <= 1 * 60 * 60)
|
||
{
|
||
value = (int)roundf(value / 60.0f);
|
||
NSString *format = TGLocalized([self integerValueFormat:@"MuteExpires.Minutes_" value:value]);
|
||
return [[NSString alloc] initWithFormat:format, [[NSString alloc] initWithFormat:@"%d", value]];
|
||
}
|
||
else if (value <= 24 * 60 * 60)
|
||
{
|
||
value = (int)roundf(value / (60.0f * 60.0f));
|
||
NSString *format = TGLocalized([self integerValueFormat:@"MuteExpires.Hours_" value:value]);
|
||
return [[NSString alloc] initWithFormat:format, [[NSString alloc] initWithFormat:@"%d", value]];
|
||
}
|
||
else
|
||
{
|
||
value = (int)roundf(value / (24.0f * 60.0f * 60.0f));
|
||
NSString *format = TGLocalized([self integerValueFormat:@"MuteExpires.Days_" value:value]);
|
||
return [[NSString alloc] initWithFormat:format, [[NSString alloc] initWithFormat:@"%d", value]];
|
||
}
|
||
|
||
return @"";
|
||
}
|
||
|
||
+ (NSString *)stringForDeviceType
|
||
{
|
||
NSString *model = @"iPhone";
|
||
NSString *rawModel = [[[UIDevice currentDevice] model] lowercaseString];
|
||
if ([rawModel rangeOfString:@"ipod"].location != NSNotFound)
|
||
model = @"iPod";
|
||
else if ([rawModel rangeOfString:@"ipad"].location != NSNotFound)
|
||
model = @"iPad";
|
||
|
||
return model;
|
||
}
|
||
|
||
+ (NSString *)stringForCurrency:(NSString *)__unused currency amount:(int64_t)__unused amount {
|
||
return nil;
|
||
}
|
||
|
||
+ (NSString *)stringForEmojiHashOfData:(NSData *)data count:(NSInteger)count positionExtractor:(int32_t (^)(uint8_t *, int32_t, int32_t))positionExtractor
|
||
{
|
||
if (data.length != 32)
|
||
return @"";
|
||
|
||
NSArray *emojis = @[ @"😉", @"😍", @"😛", @"😭", @"😱", @"😡", @"😎", @"😴", @"😵", @"😈", @"😬", @"😇", @"😏", @"👮", @"👷", @"💂", @"👶", @"👨", @"👩", @"👴", @"👵", @"😻", @"😽", @"🙀", @"👺", @"🙈", @"🙉", @"🙊", @"💀", @"👽", @"💩", @"🔥", @"💥", @"💤", @"👂", @"👀", @"👃", @"👅", @"👄", @"👍", @"👎", @"👌", @"👊", @"✌️", @"✋️", @"👐", @"👆", @"👇", @"👉", @"👈", @"🙏", @"👏", @"💪", @"🚶", @"🏃", @"💃", @"👫", @"👪", @"👬", @"👭", @"💅", @"🎩", @"👑", @"👒", @"👟", @"👞", @"👠", @"👕", @"👗", @"👖", @"👙", @"👜", @"👓", @"🎀", @"💄", @"💛", @"💙", @"💜", @"💚", @"💍", @"💎", @"🐶", @"🐺", @"🐱", @"🐭", @"🐹", @"🐰", @"🐸", @"🐯", @"🐨", @"🐻", @"🐷", @"🐮", @"🐗", @"🐴", @"🐑", @"🐘", @"🐼", @"🐧", @"🐥", @"🐔", @"🐍", @"🐢", @"🐛", @"🐝", @"🐜", @"🐞", @"🐌", @"🐙", @"🐚", @"🐟", @"🐬", @"🐋", @"🐐", @"🐊", @"🐫", @"🍀", @"🌹", @"🌻", @"🍁", @"🌾", @"🍄", @"🌵", @"🌴", @"🌳", @"🌞", @"🌚", @"🌙", @"🌎", @"🌋", @"⚡️", @"☔️", @"❄️", @"⛄️", @"🌀", @"🌈", @"🌊", @"🎓", @"🎆", @"🎃", @"👻", @"🎅", @"🎄", @"🎁", @"🎈", @"🔮", @"🎥", @"📷", @"💿", @"💻", @"☎️", @"📡", @"📺", @"📻", @"🔉", @"🔔", @"⏳", @"⏰", @"⌚️", @"🔒", @"🔑", @"🔎", @"💡", @"🔦", @"🔌", @"🔋", @"🚿", @"🚽", @"🔧", @"🔨", @"🚪", @"🚬", @"💣", @"🔫", @"🔪", @"💊", @"💉", @"💰", @"💵", @"💳", @"✉️", @"📫", @"📦", @"📅", @"📁", @"✂️", @"📌", @"📎", @"✒️", @"✏️", @"📐", @"📚", @"🔬", @"🔭", @"🎨", @"🎬", @"🎤", @"🎧", @"🎵", @"🎹", @"🎻", @"🎺", @"🎸", @"👾", @"🎮", @"🃏", @"🎲", @"🎯", @"🏈", @"🏀", @"⚽️", @"⚾️", @"🎾", @"🎱", @"🏉", @"🎳", @"🏁", @"🏇", @"🏆", @"🏊", @"🏄", @"☕️", @"🍼", @"🍺", @"🍷", @"🍴", @"🍕", @"🍔", @"🍟", @"🍗", @"🍱", @"🍚", @"🍜", @"🍡", @"🍳", @"🍞", @"🍩", @"🍦", @"🎂", @"🍰", @"🍪", @"🍫", @"🍭", @"🍯", @"🍎", @"🍏", @"🍊", @"🍋", @"🍒", @"🍇", @"🍉", @"🍓", @"🍑", @"🍌", @"🍐", @"🍍", @"🍆", @"🍅", @"🌽", @"🏡", @"🏥", @"🏦", @"⛪️", @"🏰", @"⛺️", @"🏭", @"🗻", @"🗽", @"🎠", @"🎡", @"⛲️", @"🎢", @"🚢", @"🚤", @"⚓️", @"🚀", @"✈️", @"🚁", @"🚂", @"🚋", @"🚎", @"🚌", @"🚙", @"🚗", @"🚕", @"🚛", @"🚨", @"🚔", @"🚒", @"🚑", @"🚲", @"🚠", @"🚜", @"🚦", @"⚠️", @"🚧", @"⛽️", @"🎰", @"🗿", @"🎪", @"🎭", @"🇯🇵", @"🇰🇷", @"🇩🇪", @"🇨🇳", @"🇺🇸", @"🇫🇷", @"🇪🇸", @"🇮🇹", @"🇷🇺", @"🇬🇧", @"1️⃣", @"2️⃣", @"3️⃣", @"4️⃣", @"5️⃣", @"6️⃣", @"7️⃣", @"8️⃣", @"9️⃣", @"0️⃣", @"🔟", @"❗️", @"❓", @"♥️", @"♦️", @"💯", @"🔗", @"🔱", @"🔴", @"🔵", @"🔶", @"🔷" ];
|
||
|
||
uint8_t bytes[32];
|
||
[data getBytes:bytes length:32];
|
||
|
||
NSString *result = @"";
|
||
for (int32_t i = 0; i < count; i++)
|
||
{
|
||
int32_t position = positionExtractor(bytes, i, (int32_t)emojis.count);
|
||
NSString *emoji = emojis[position];
|
||
result = [result stringByAppendingString:emoji];
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
@end
|
||
|
||
#if defined(_MSC_VER)
|
||
|
||
#define FORCE_INLINE __forceinline
|
||
|
||
#include <stdlib.h>
|
||
|
||
#define ROTL32(x,y) _rotl(x,y)
|
||
#define ROTL64(x,y) _rotl64(x,y)
|
||
|
||
#define BIG_CONSTANT(x) (x)
|
||
|
||
// Other compilers
|
||
|
||
#else // defined(_MSC_VER)
|
||
|
||
#define FORCE_INLINE __attribute__((always_inline))
|
||
|
||
static inline uint32_t rotl32 ( uint32_t x, int8_t r )
|
||
{
|
||
return (x << r) | (x >> (32 - r));
|
||
}
|
||
|
||
#define ROTL32(x,y) rotl32(x,y)
|
||
#define ROTL64(x,y) rotl64(x,y)
|
||
|
||
#define BIG_CONSTANT(x) (x##LLU)
|
||
|
||
#endif // !defined(_MSC_VER)
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Block read - if your platform needs to do endian-swapping or can only
|
||
// handle aligned reads, do the conversion here
|
||
|
||
static FORCE_INLINE uint32_t getblock ( const uint32_t * p, int i )
|
||
{
|
||
return p[i];
|
||
}
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Finalization mix - force all bits of a hash block to avalanche
|
||
|
||
static FORCE_INLINE uint32_t fmix ( uint32_t h )
|
||
{
|
||
h ^= h >> 16;
|
||
h *= 0x85ebca6b;
|
||
h ^= h >> 13;
|
||
h *= 0xc2b2ae35;
|
||
h ^= h >> 16;
|
||
|
||
return h;
|
||
}
|
||
|
||
//----------
|
||
|
||
//-----------------------------------------------------------------------------
|
||
|
||
static void MurmurHash3_x86_32 ( const void * key, int len,
|
||
uint32_t seed, void * out )
|
||
{
|
||
const uint8_t * data = (const uint8_t*)key;
|
||
const int nblocks = len / 4;
|
||
|
||
uint32_t h1 = seed;
|
||
|
||
const uint32_t c1 = 0xcc9e2d51;
|
||
const uint32_t c2 = 0x1b873593;
|
||
|
||
//----------
|
||
// body
|
||
|
||
const uint32_t * blocks = (const uint32_t *)(data + nblocks*4);
|
||
|
||
for(int i = -nblocks; i; i++)
|
||
{
|
||
uint32_t k1 = getblock(blocks,i);
|
||
|
||
k1 *= c1;
|
||
k1 = ROTL32(k1,15);
|
||
k1 *= c2;
|
||
|
||
h1 ^= k1;
|
||
h1 = ROTL32(h1,13);
|
||
h1 = h1*5+0xe6546b64;
|
||
}
|
||
|
||
//----------
|
||
// tail
|
||
|
||
const uint8_t * tail = (const uint8_t*)(data + nblocks*4);
|
||
|
||
uint32_t k1 = 0;
|
||
|
||
switch(len & 3)
|
||
{
|
||
case 3: k1 ^= tail[2] << 16;
|
||
case 2: k1 ^= tail[1] << 8;
|
||
case 1: k1 ^= tail[0];
|
||
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
|
||
};
|
||
|
||
//----------
|
||
// finalization
|
||
|
||
h1 ^= len;
|
||
|
||
h1 = fmix(h1);
|
||
|
||
*(uint32_t*)out = h1;
|
||
}
|
||
|
||
int32_t legacy_murMurHash32(NSString *string)
|
||
{
|
||
const char *utf8 = string.UTF8String;
|
||
|
||
int32_t result = 0;
|
||
MurmurHash3_x86_32((uint8_t *)utf8, (int)strlen(utf8), -137723950, &result);
|
||
|
||
return result;
|
||
}
|
||
|
||
int32_t legacy_murMurHashBytes32(void *bytes, int length)
|
||
{
|
||
int32_t result = 0;
|
||
MurmurHash3_x86_32(bytes, length, -137723950, &result);
|
||
|
||
return result;
|
||
}
|
||
|
||
int32_t phoneMatchHash(NSString *phone)
|
||
{
|
||
int length = (int)phone.length;
|
||
char cleanString[length];
|
||
int cleanLength = 0;
|
||
|
||
for (int i = 0; i < length; i++)
|
||
{
|
||
unichar c = [phone characterAtIndex:i];
|
||
if (c >= '0' && c <= '9')
|
||
cleanString[cleanLength++] = (char)c;
|
||
}
|
||
|
||
int32_t result = 0;
|
||
if (cleanLength > 8)
|
||
MurmurHash3_x86_32((uint8_t *)cleanString + (cleanLength - 8), 8, -137723950, &result);
|
||
else
|
||
MurmurHash3_x86_32((uint8_t *)cleanString, cleanLength, -137723950, &result);
|
||
|
||
return result;
|
||
}
|
||
|
||
bool TGIsRTL()
|
||
{
|
||
static bool value = false;
|
||
static dispatch_once_t onceToken;
|
||
dispatch_once(&onceToken, ^
|
||
{
|
||
value = ([NSLocale characterDirectionForLanguage:[[NSLocale preferredLanguages] objectAtIndex:0]] == NSLocaleLanguageDirectionRightToLeft);
|
||
});
|
||
|
||
if (!value && iosMajorVersion() >= 9)
|
||
value = [UIView appearance].semanticContentAttribute == UISemanticContentAttributeForceRightToLeft;
|
||
|
||
return value;
|
||
}
|
||
|
||
bool TGIsArabic()
|
||
{
|
||
static bool value = false;
|
||
static dispatch_once_t onceToken;
|
||
dispatch_once(&onceToken, ^
|
||
{
|
||
NSString *language = [[NSLocale preferredLanguages] objectAtIndex:0];
|
||
value = [language isEqualToString:@"ar"] || [language hasPrefix:@"ar-"];
|
||
});
|
||
return value;
|
||
}
|
||
|
||
bool TGIsKorean()
|
||
{
|
||
static bool value = false;
|
||
static dispatch_once_t onceToken;
|
||
dispatch_once(&onceToken, ^
|
||
{
|
||
NSString *language = [[NSLocale preferredLanguages] objectAtIndex:0];
|
||
value = [language isEqualToString:@"ko"] || [language hasPrefix:@"ko-"];
|
||
});
|
||
return value;
|
||
}
|
||
|
||
bool TGIsLocaleArabic()
|
||
{
|
||
static bool value = false;
|
||
static dispatch_once_t onceToken;
|
||
dispatch_once(&onceToken, ^
|
||
{
|
||
NSString *identifier = [[NSLocale currentLocale] localeIdentifier];
|
||
value = [identifier isEqualToString:@"ar"] || [identifier hasPrefix:@"ar-"];
|
||
});
|
||
return value;
|
||
}
|
||
|
||
@implementation NSString (Telegraph)
|
||
|
||
- (int)lengthByComposedCharacterSequences
|
||
{
|
||
return [self lengthByComposedCharacterSequencesInRange:NSMakeRange(0, self.length)];
|
||
}
|
||
|
||
- (int)lengthByComposedCharacterSequencesInRange:(NSRange)range
|
||
{
|
||
__block NSInteger length = 0;
|
||
[self enumerateSubstringsInRange:range options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(__unused NSString *substring, __unused NSRange substringRange, __unused NSRange enclosingRange, __unused BOOL *stop)
|
||
{
|
||
if (substring.length != 0)
|
||
length++;
|
||
//TGLegacyLog(@"substringRange %@, enclosingRange %@, length %d", NSStringFromRange(substringRange), NSStringFromRange(enclosingRange), length);
|
||
}];
|
||
//TGLegacyLog(@"length %d", length);
|
||
|
||
return (int)length;
|
||
}
|
||
|
||
- (bool)hasNonWhitespaceCharacters
|
||
{
|
||
NSInteger textLength = self.length;
|
||
bool hasNonWhitespace = false;
|
||
for (int i = 0; i < textLength; i++)
|
||
{
|
||
unichar c = [self characterAtIndex:i];
|
||
if (c != ' ' && c != '\n' && c != '\t' && c != NSAttachmentCharacter)
|
||
{
|
||
hasNonWhitespace = true;
|
||
break;
|
||
}
|
||
}
|
||
return hasNonWhitespace;
|
||
}
|
||
|
||
- (NSAttributedString *)attributedFormattedStringWithRegularFont:(UIFont *)regularFont boldFont:(UIFont *)boldFont lineSpacing:(CGFloat)lineSpacing paragraphSpacing:(CGFloat)paragraphSpacing alignment:(NSTextAlignment)alignment
|
||
{
|
||
NSMutableArray *boldRanges = [[NSMutableArray alloc] init];
|
||
|
||
NSMutableString *cleanText = [[NSMutableString alloc] initWithString:self];
|
||
while (true)
|
||
{
|
||
NSRange startRange = [cleanText rangeOfString:@"**"];
|
||
if (startRange.location == NSNotFound)
|
||
break;
|
||
|
||
[cleanText deleteCharactersInRange:startRange];
|
||
|
||
NSRange endRange = [cleanText rangeOfString:@"**"];
|
||
if (endRange.location == NSNotFound)
|
||
break;
|
||
|
||
[cleanText deleteCharactersInRange:endRange];
|
||
|
||
[boldRanges addObject:[NSValue valueWithRange:NSMakeRange(startRange.location, endRange.location - startRange.location)]];
|
||
}
|
||
|
||
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
|
||
style.lineSpacing = lineSpacing;
|
||
style.lineBreakMode = NSLineBreakByWordWrapping;
|
||
style.alignment = alignment;
|
||
style.paragraphSpacing = paragraphSpacing;
|
||
|
||
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:cleanText attributes:@
|
||
{
|
||
}];
|
||
|
||
[attributedString addAttributes:@{NSParagraphStyleAttributeName: style, NSFontAttributeName: regularFont} range:NSMakeRange(0, attributedString.length)];
|
||
|
||
NSDictionary *boldAttributes = @{NSFontAttributeName: boldFont};
|
||
for (NSValue *nRange in boldRanges)
|
||
{
|
||
[attributedString addAttributes:boldAttributes range:[nRange rangeValue]];
|
||
}
|
||
|
||
return attributedString;
|
||
}
|
||
|
||
- (NSString *)urlAnchorPart {
|
||
NSURL *url = [[NSURL alloc] initWithString:self];
|
||
return [url fragment];
|
||
}
|
||
|
||
static unsigned char strToChar (char a, char b)
|
||
{
|
||
char encoder[3] = {'\0','\0','\0'};
|
||
encoder[0] = a;
|
||
encoder[1] = b;
|
||
return (char)strtol(encoder,NULL,16);
|
||
}
|
||
|
||
- (NSData *)dataByDecodingHexString
|
||
{
|
||
const char *bytes = [self cStringUsingEncoding:NSUTF8StringEncoding];
|
||
NSUInteger length = strlen(bytes);
|
||
unsigned char *r = (unsigned char *)malloc(length / 2);
|
||
unsigned char *index = r;
|
||
|
||
while ((*bytes) && (*(bytes + 1)))
|
||
{
|
||
*index = strToChar(*bytes, *(bytes +1));
|
||
index++;
|
||
bytes+=2;
|
||
}
|
||
|
||
return [[NSData alloc] initWithBytesNoCopy:r length:length / 2 freeWhenDone:true];
|
||
}
|
||
|
||
|
||
- (bool)containsSingleEmoji
|
||
{
|
||
if (self.length > 0 && self.length < 16)
|
||
{
|
||
NSArray *emojis = [self emojiArray:false];
|
||
return emojis.count == 1 && [emojis.firstObject isEqualToString:self];
|
||
}
|
||
return false;
|
||
}
|
||
|
||
- (bool)isEmoji
|
||
{
|
||
static dispatch_once_t onceToken;
|
||
static NSCharacterSet *variationSelectors;
|
||
dispatch_once(&onceToken, ^
|
||
{
|
||
variationSelectors = [NSCharacterSet characterSetWithRange:NSMakeRange(0xFE00, 16)];
|
||
});
|
||
|
||
if ([self rangeOfCharacterFromSet:variationSelectors].location != NSNotFound)
|
||
return true;
|
||
|
||
const unichar high = [self characterAtIndex:0];
|
||
if (0xd800 <= high && high <= 0xdbff)
|
||
{
|
||
if (self.length < 2)
|
||
return false;
|
||
|
||
const unichar low = [self characterAtIndex:1];
|
||
const int codepoint = ((high - 0xd800) * 0x400) + (low - 0xdc00) + 0x10000;
|
||
return (0x1d000 <= codepoint && codepoint <= 0x1f77f) || (0x1F900 <= codepoint && codepoint <= 0x1f9ff);
|
||
}
|
||
else
|
||
{
|
||
return (0x2100 <= high && high <= 0x27BF);
|
||
}
|
||
}
|
||
|
||
- (NSArray *)emojiArray:(bool)stripModifiers
|
||
{
|
||
__block NSMutableArray *emoji = [[NSMutableArray alloc] init];
|
||
[self enumerateSubstringsInRange: NSMakeRange(0, [self length]) options:NSStringEnumerationByComposedCharacterSequences usingBlock:
|
||
^(NSString *substring, __unused NSRange substringRange, __unused NSRange enclosingRange, __unused BOOL *stop)
|
||
{
|
||
if ([substring isEmoji])
|
||
{
|
||
if (substring.length > 2 && stripModifiers)
|
||
{
|
||
for (int i = 1; i < substring.length - 1; i++)
|
||
{
|
||
NSString *test = [substring substringToIndex:i];
|
||
if ([test isEmoji])
|
||
{
|
||
[emoji addObject:test];
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
[emoji addObject:substring];
|
||
}
|
||
}
|
||
}];
|
||
return emoji;
|
||
}
|
||
|
||
@end
|
||
|
||
@implementation NSData (Telegraph)
|
||
|
||
+ (NSData *)dataWithHexString:(NSString *)hex
|
||
{
|
||
char buf[3];
|
||
buf[2] = '\0';
|
||
NSAssert(0 == [hex length] % 2, @"Hex strings should have an even number of digits (%@)", hex);
|
||
uint8_t *bytes = (uint8_t *)malloc(hex.length / 2);
|
||
uint8_t *bp = bytes;
|
||
for (CFIndex i = 0; i < [hex length]; i += 2) {
|
||
buf[0] = [hex characterAtIndex:i];
|
||
buf[1] = [hex characterAtIndex:i+1];
|
||
char *b2 = NULL;
|
||
*bp++ = strtol(buf, &b2, 16);
|
||
NSAssert(b2 == buf + 2, @"String should be all hex digits: %@ (bad digit around %d)", hex, (int)i);
|
||
}
|
||
|
||
return [NSData dataWithBytesNoCopy:bytes length:[hex length]/2 freeWhenDone:YES];
|
||
}
|
||
|
||
- (NSString *)stringByEncodingInHex
|
||
{
|
||
const unsigned char *dataBuffer = (const unsigned char *)[self bytes];
|
||
if (dataBuffer == NULL)
|
||
return [NSString string];
|
||
|
||
NSUInteger dataLength = [self length];
|
||
NSMutableString *hexString = [NSMutableString stringWithCapacity:(dataLength * 2)];
|
||
|
||
for (int i = 0; i < (int)dataLength; ++i)
|
||
[hexString appendString:[NSString stringWithFormat:@"%02lx", (unsigned long)dataBuffer[i]]];
|
||
|
||
return hexString;
|
||
}
|
||
|
||
- (NSString *)stringByEncodingInHexSeparatedByString:(NSString *)string
|
||
{
|
||
const unsigned char *dataBuffer = (const unsigned char *)[self bytes];
|
||
if (dataBuffer == NULL)
|
||
return [NSString string];
|
||
|
||
NSUInteger dataLength = [self length];
|
||
NSMutableString *hexString = [NSMutableString stringWithCapacity:(dataLength * 2)];
|
||
|
||
NSString *divider = string;
|
||
for (int i = 0; i < (int)dataLength; ++i) {
|
||
if (i == (int)dataLength - 1)
|
||
divider = @"";
|
||
else if (i == (int)dataLength / 2 - 1)
|
||
divider = [divider stringByAppendingString:divider];
|
||
else
|
||
divider = string;
|
||
|
||
[hexString appendString:[NSString stringWithFormat:@"%02lx%@", (unsigned long)dataBuffer[i], divider]];
|
||
}
|
||
return hexString;
|
||
}
|
||
|
||
@end
|