2021-08-05 00:02:35 +02:00

1445 lines
48 KiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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
{ @"&quot;", 34 },
{ @"&amp;", 38 },
{ @"&apos;", 39 },
{ @"&lt;", 60 },
{ @"&gt;", 62 },
// A.2.1. Latin-1 characters
{ @"&nbsp;", 160 },
{ @"&iexcl;", 161 },
{ @"&cent;", 162 },
{ @"&pound;", 163 },
{ @"&curren;", 164 },
{ @"&yen;", 165 },
{ @"&brvbar;", 166 },
{ @"&sect;", 167 },
{ @"&uml;", 168 },
{ @"&copy;", 169 },
{ @"&ordf;", 170 },
{ @"&laquo;", 171 },
{ @"&not;", 172 },
{ @"&shy;", 173 },
{ @"&reg;", 174 },
{ @"&macr;", 175 },
{ @"&deg;", 176 },
{ @"&plusmn;", 177 },
{ @"&sup2;", 178 },
{ @"&sup3;", 179 },
{ @"&acute;", 180 },
{ @"&micro;", 181 },
{ @"&para;", 182 },
{ @"&middot;", 183 },
{ @"&cedil;", 184 },
{ @"&sup1;", 185 },
{ @"&ordm;", 186 },
{ @"&raquo;", 187 },
{ @"&frac14;", 188 },
{ @"&frac12;", 189 },
{ @"&frac34;", 190 },
{ @"&iquest;", 191 },
{ @"&Agrave;", 192 },
{ @"&Aacute;", 193 },
{ @"&Acirc;", 194 },
{ @"&Atilde;", 195 },
{ @"&Auml;", 196 },
{ @"&Aring;", 197 },
{ @"&AElig;", 198 },
{ @"&Ccedil;", 199 },
{ @"&Egrave;", 200 },
{ @"&Eacute;", 201 },
{ @"&Ecirc;", 202 },
{ @"&Euml;", 203 },
{ @"&Igrave;", 204 },
{ @"&Iacute;", 205 },
{ @"&Icirc;", 206 },
{ @"&Iuml;", 207 },
{ @"&ETH;", 208 },
{ @"&Ntilde;", 209 },
{ @"&Ograve;", 210 },
{ @"&Oacute;", 211 },
{ @"&Ocirc;", 212 },
{ @"&Otilde;", 213 },
{ @"&Ouml;", 214 },
{ @"&times;", 215 },
{ @"&Oslash;", 216 },
{ @"&Ugrave;", 217 },
{ @"&Uacute;", 218 },
{ @"&Ucirc;", 219 },
{ @"&Uuml;", 220 },
{ @"&Yacute;", 221 },
{ @"&THORN;", 222 },
{ @"&szlig;", 223 },
{ @"&agrave;", 224 },
{ @"&aacute;", 225 },
{ @"&acirc;", 226 },
{ @"&atilde;", 227 },
{ @"&auml;", 228 },
{ @"&aring;", 229 },
{ @"&aelig;", 230 },
{ @"&ccedil;", 231 },
{ @"&egrave;", 232 },
{ @"&eacute;", 233 },
{ @"&ecirc;", 234 },
{ @"&euml;", 235 },
{ @"&igrave;", 236 },
{ @"&iacute;", 237 },
{ @"&icirc;", 238 },
{ @"&iuml;", 239 },
{ @"&eth;", 240 },
{ @"&ntilde;", 241 },
{ @"&ograve;", 242 },
{ @"&oacute;", 243 },
{ @"&ocirc;", 244 },
{ @"&otilde;", 245 },
{ @"&ouml;", 246 },
{ @"&divide;", 247 },
{ @"&oslash;", 248 },
{ @"&ugrave;", 249 },
{ @"&uacute;", 250 },
{ @"&ucirc;", 251 },
{ @"&uuml;", 252 },
{ @"&yacute;", 253 },
{ @"&thorn;", 254 },
{ @"&yuml;", 255 },
// A.2.2. Special characters cont'd
{ @"&OElig;", 338 },
{ @"&oelig;", 339 },
{ @"&Scaron;", 352 },
{ @"&scaron;", 353 },
{ @"&Yuml;", 376 },
// A.2.3. Symbols
{ @"&fnof;", 402 },
// A.2.2. Special characters cont'd
{ @"&circ;", 710 },
{ @"&tilde;", 732 },
// A.2.3. Symbols cont'd
{ @"&Alpha;", 913 },
{ @"&Beta;", 914 },
{ @"&Gamma;", 915 },
{ @"&Delta;", 916 },
{ @"&Epsilon;", 917 },
{ @"&Zeta;", 918 },
{ @"&Eta;", 919 },
{ @"&Theta;", 920 },
{ @"&Iota;", 921 },
{ @"&Kappa;", 922 },
{ @"&Lambda;", 923 },
{ @"&Mu;", 924 },
{ @"&Nu;", 925 },
{ @"&Xi;", 926 },
{ @"&Omicron;", 927 },
{ @"&Pi;", 928 },
{ @"&Rho;", 929 },
{ @"&Sigma;", 931 },
{ @"&Tau;", 932 },
{ @"&Upsilon;", 933 },
{ @"&Phi;", 934 },
{ @"&Chi;", 935 },
{ @"&Psi;", 936 },
{ @"&Omega;", 937 },
{ @"&alpha;", 945 },
{ @"&beta;", 946 },
{ @"&gamma;", 947 },
{ @"&delta;", 948 },
{ @"&epsilon;", 949 },
{ @"&zeta;", 950 },
{ @"&eta;", 951 },
{ @"&theta;", 952 },
{ @"&iota;", 953 },
{ @"&kappa;", 954 },
{ @"&lambda;", 955 },
{ @"&mu;", 956 },
{ @"&nu;", 957 },
{ @"&xi;", 958 },
{ @"&omicron;", 959 },
{ @"&pi;", 960 },
{ @"&rho;", 961 },
{ @"&sigmaf;", 962 },
{ @"&sigma;", 963 },
{ @"&tau;", 964 },
{ @"&upsilon;", 965 },
{ @"&phi;", 966 },
{ @"&chi;", 967 },
{ @"&psi;", 968 },
{ @"&omega;", 969 },
{ @"&thetasym;", 977 },
{ @"&upsih;", 978 },
{ @"&piv;", 982 },
// A.2.2. Special characters cont'd
{ @"&ensp;", 8194 },
{ @"&emsp;", 8195 },
{ @"&thinsp;", 8201 },
{ @"&zwnj;", 8204 },
{ @"&zwj;", 8205 },
{ @"&lrm;", 8206 },
{ @"&rlm;", 8207 },
{ @"&ndash;", 8211 },
{ @"&mdash;", 8212 },
{ @"&lsquo;", 8216 },
{ @"&rsquo;", 8217 },
{ @"&sbquo;", 8218 },
{ @"&ldquo;", 8220 },
{ @"&rdquo;", 8221 },
{ @"&bdquo;", 8222 },
{ @"&dagger;", 8224 },
{ @"&Dagger;", 8225 },
// A.2.3. Symbols cont'd
{ @"&bull;", 8226 },
{ @"&hellip;", 8230 },
// A.2.2. Special characters cont'd
{ @"&permil;", 8240 },
// A.2.3. Symbols cont'd
{ @"&prime;", 8242 },
{ @"&Prime;", 8243 },
// A.2.2. Special characters cont'd
{ @"&lsaquo;", 8249 },
{ @"&rsaquo;", 8250 },
// A.2.3. Symbols cont'd
{ @"&oline;", 8254 },
{ @"&frasl;", 8260 },
// A.2.2. Special characters cont'd
{ @"&euro;", 8364 },
// A.2.3. Symbols cont'd
{ @"&image;", 8465 },
{ @"&weierp;", 8472 },
{ @"&real;", 8476 },
{ @"&trade;", 8482 },
{ @"&alefsym;", 8501 },
{ @"&larr;", 8592 },
{ @"&uarr;", 8593 },
{ @"&rarr;", 8594 },
{ @"&darr;", 8595 },
{ @"&harr;", 8596 },
{ @"&crarr;", 8629 },
{ @"&lArr;", 8656 },
{ @"&uArr;", 8657 },
{ @"&rArr;", 8658 },
{ @"&dArr;", 8659 },
{ @"&hArr;", 8660 },
{ @"&forall;", 8704 },
{ @"&part;", 8706 },
{ @"&exist;", 8707 },
{ @"&empty;", 8709 },
{ @"&nabla;", 8711 },
{ @"&isin;", 8712 },
{ @"&notin;", 8713 },
{ @"&ni;", 8715 },
{ @"&prod;", 8719 },
{ @"&sum;", 8721 },
{ @"&minus;", 8722 },
{ @"&lowast;", 8727 },
{ @"&radic;", 8730 },
{ @"&prop;", 8733 },
{ @"&infin;", 8734 },
{ @"&ang;", 8736 },
{ @"&and;", 8743 },
{ @"&or;", 8744 },
{ @"&cap;", 8745 },
{ @"&cup;", 8746 },
{ @"&int;", 8747 },
{ @"&there4;", 8756 },
{ @"&sim;", 8764 },
{ @"&cong;", 8773 },
{ @"&asymp;", 8776 },
{ @"&ne;", 8800 },
{ @"&equiv;", 8801 },
{ @"&le;", 8804 },
{ @"&ge;", 8805 },
{ @"&sub;", 8834 },
{ @"&sup;", 8835 },
{ @"&nsub;", 8836 },
{ @"&sube;", 8838 },
{ @"&supe;", 8839 },
{ @"&oplus;", 8853 },
{ @"&otimes;", 8855 },
{ @"&perp;", 8869 },
{ @"&sdot;", 8901 },
{ @"&lceil;", 8968 },
{ @"&rceil;", 8969 },
{ @"&lfloor;", 8970 },
{ @"&rfloor;", 8971 },
{ @"&lang;", 9001 },
{ @"&rang;", 9002 },
{ @"&loz;", 9674 },
{ @"&spades;", 9824 },
{ @"&clubs;", 9827 },
{ @"&hearts;", 9829 },
{ @"&diams;", 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 (&lt;) and less than 11 (&thetasym;)
if (length > 3 && length < 11)
{
if ([escapeString characterAtIndex:1] == '#')
{
unichar char2 = [escapeString characterAtIndex:2];
if (char2 == 'x' || char2 == 'X') {
// Hex escape squences &#xa3;
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 &#123;
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