adium 3106:f75c7bfd396b: Restructure AHHyperlinkScanner for bett...
commits at adium.im
commits at adium.im
Fri Feb 5 00:29:43 UTC 2010
details: http://hg.adium.im/adium/rev/f75c7bfd396b
revision: 3106:f75c7bfd396b
author: Stephen Holt <sholt at adium.im>
date: Thu Feb 04 18:54:56 2010 -0500
Restructure AHHyperlinkScanner for better synchronization to nextURI.
Subject: adium 3107:ad12d0d854b9: Make the current scan location a property. Less code is good. Atomicy is good.
details: http://hg.adium.im/adium/rev/ad12d0d854b9
revision: 3107:ad12d0d854b9
author: Stephen Holt <sholt at adium.im>
date: Thu Feb 04 19:28:56 2010 -0500
Make the current scan location a property. Less code is good. Atomicy is good.
Subject: adium 3108:7754de9d8f18: Create the linkified string once and cache it, also make it a r/o property.
details: http://hg.adium.im/adium/rev/7754de9d8f18
revision: 3108:7754de9d8f18
author: Stephen Holt <sholt at adium.im>
date: Thu Feb 04 19:28:58 2010 -0500
Create the linkified string once and cache it, also make it a r/o property.
It's probably rare that the linkified string will be requested more than once, but I'd rather not re-calculate it if it is.
diffs (488 lines):
diff -r e864e707fb51 -r 7754de9d8f18 Frameworks/AutoHyperlinks Framework/Source/AHHyperlinkScanner.h
--- a/Frameworks/AutoHyperlinks Framework/Source/AHHyperlinkScanner.h Wed Feb 03 22:06:53 2010 -0500
+++ b/Frameworks/AutoHyperlinks Framework/Source/AHHyperlinkScanner.h Thu Feb 04 19:28:58 2010 -0500
@@ -44,14 +44,17 @@
@interface AHHyperlinkScanner : NSObject <NSFastEnumeration>
{
- NSDictionary *m_urlSchemes;
- NSString *m_scanString;
- NSAttributedString *m_scanAttrString;
- BOOL m_strictChecking;
- unsigned long m_scanLocation;
- unsigned long m_scanStringLength;
+ NSDictionary *m_urlSchemes;
+ NSString *m_scanString;
+ NSAttributedString *m_scanAttrString;
+ NSAttributedString *m_linkifiedString;
+ BOOL m_strictChecking;
+ unsigned long m_scanLocation;
+ unsigned long m_scanStringLength;
}
+ at property (assign) unsigned long scanLocation;
+ at property (readonly) NSAttributedString *linkifiedString;
/*!
* @brief Allocs and inits a new lax AHHyperlinkScanner with the given NSString
@@ -146,7 +149,4 @@
*/
- (NSAttributedString *)linkifiedString;
-- (unsigned long)scanLocation;
-- (void)setScanLocation:(unsigned int)location;
-
@end
diff -r e864e707fb51 -r 7754de9d8f18 Frameworks/AutoHyperlinks Framework/Source/AHHyperlinkScanner.m
--- a/Frameworks/AutoHyperlinks Framework/Source/AHHyperlinkScanner.m Wed Feb 03 22:06:53 2010 -0500
+++ b/Frameworks/AutoHyperlinks Framework/Source/AHHyperlinkScanner.m Thu Feb 04 19:28:58 2010 -0500
@@ -28,12 +28,15 @@
#import "AHHyperlinkScanner.h"
#import "AHLinkLexer.h"
#import "AHMarkedHyperlink.h"
+#import <libkern/OSAtomic.h>
#define DEFAULT_URL_SCHEME @"http://"
#define ENC_INDEX_KEY @"encIndex"
#define ENC_CHAR_KEY @"encChar"
@interface AHHyperlinkScanner (PRIVATE)
+- (AHMarkedHyperlink *)nextURIFromLocation:(unsigned long *)_scanLocation;
+- (NSAttributedString *)_createLinkifiedString;
- (NSRange)_longestBalancedEnclosureInRange:(NSRange)inRange;
- (BOOL)_scanString:(NSString *)inString upToCharactersFromSet:(NSCharacterSet *)inCharSet intoRange:(NSRange *)outRangeRef fromIndex:(unsigned long *)idx;
- (BOOL)_scanString:(NSString *)inString charactersFromSet:(NSCharacterSet *)inCharSet intoRange:(NSRange *)outRangeRef fromIndex:(unsigned long *)idx;
@@ -50,7 +53,10 @@
static NSCharacterSet *enclosureSet = nil;
static NSArray *enclosureStopArray = nil;
static NSArray *encKeys = nil;
-
+
+ at synthesize scanLocation = m_scanLocation;
+ at dynamic linkifiedString;
+
#pragma mark Class Methods
+ (id)hyperlinkScannerWithString:(NSString *)inString
{
@@ -125,7 +131,14 @@
}
#pragma mark Init/Dealloc
-
+- (id)init
+{
+ if((self = [super init])){
+ self.scanLocation = 0;
+ m_linkifiedString = nil;
+ }
+ return self;
+}
- (id)initWithString:(NSString *)inString usingStrictChecking:(BOOL)flag
{
@@ -136,7 +149,6 @@
@"ftp://", @"ftp",
nil];
m_strictChecking = flag;
- m_scanLocation = 0;
m_scanStringLength = [m_scanString length];
}
return self;
@@ -151,7 +163,6 @@
@"ftp://", @"ftp",
nil];
m_strictChecking = flag;
- m_scanLocation = 0;
m_scanStringLength = [m_scanString length];
}
return self;
@@ -159,6 +170,8 @@
- (void)dealloc
{
+ self.scanLocation = 0;
+ [m_linkifiedString release];
[m_scanString release];
[m_urlSchemes release];
if(m_scanAttrString) [m_scanAttrString release];
@@ -174,84 +187,84 @@
+ (BOOL)isStringValidURI:(NSString *)inString usingStrict:(BOOL)useStrictChecking fromIndex:(unsigned long *)index withStatus:(AH_URI_VERIFICATION_STATUS *)validStatus
{
- AH_BUFFER_STATE buf; // buffer for flex to scan from
+ AH_BUFFER_STATE buf; // buffer for flex to scan from
yyscan_t scanner; // pointer to the flex scanner opaque type
const char *inStringEnc;
- unsigned long encodedLength;
-
+ unsigned long encodedLength;
+
if(!validStatus){
AH_URI_VERIFICATION_STATUS newStatus = AH_URL_INVALID;
validStatus = &newStatus;
}
*validStatus = AH_URL_INVALID; // assume the URL is invalid
-
+
// Find the fastest 8-bit wide encoding possible for the c string
NSStringEncoding stringEnc = [inString fastestEncoding];
if([@" " lengthOfBytesUsingEncoding:stringEnc] > 1U)
stringEnc = NSUTF8StringEncoding;
-
+
if (!(inStringEnc = [inString cStringUsingEncoding:stringEnc])) {
return NO;
}
encodedLength = strlen(inStringEnc); // length of the string in utf-8
-
+
// initialize the buffer (flex automatically switches to the buffer in this function)
AHlex_init(&scanner);
- buf = AH_scan_string(inStringEnc, scanner);
-
- // call flex to parse the input
- *validStatus = AHlex(scanner);
+ buf = AH_scan_string(inStringEnc, scanner);
+
+ // call flex to parse the input
+ *validStatus = AHlex(scanner);
if(index) *index += AHget_leng(scanner);
- // condition for valid URI's
- if(*validStatus == AH_URL_VALID || *validStatus == AH_MAILTO_VALID || *validStatus == AH_FILE_VALID){
- AH_delete_buffer(buf, scanner); //remove the buffer from flex.
- buf = NULL; //null the buffer pointer for safty's sake.
-
- // check that the whole string was matched by flex.
- // this prevents silly things like "blah...com" from being seen as links
- if(AHget_leng(scanner) == encodedLength){
+ // condition for valid URI's
+ if(*validStatus == AH_URL_VALID || *validStatus == AH_MAILTO_VALID || *validStatus == AH_FILE_VALID){
+ AH_delete_buffer(buf, scanner); //remove the buffer from flex.
+ buf = NULL; //null the buffer pointer for safty's sake.
+
+ // check that the whole string was matched by flex.
+ // this prevents silly things like "blah...com" from being seen as links
+ if(AHget_leng(scanner) == encodedLength){
AHlex_destroy(scanner);
- return YES;
- }
+ return YES;
+ }
// condition for degenerate URL's (A.K.A. URI's sans specifiers), requres strict checking to be NO.
- }else if((*validStatus == AH_URL_DEGENERATE || *validStatus == AH_MAILTO_DEGENERATE) && !useStrictChecking){
- AH_delete_buffer(buf, scanner);
- buf = NULL;
- if(AHget_leng(scanner) == encodedLength){
+ }else if((*validStatus == AH_URL_DEGENERATE || *validStatus == AH_MAILTO_DEGENERATE) && !useStrictChecking){
+ AH_delete_buffer(buf, scanner);
+ buf = NULL;
+ if(AHget_leng(scanner) == encodedLength){
AHlex_destroy(scanner);
- return YES;
- }
+ return YES;
+ }
// if it ain't vaild, and it ain't degenerate, then it's invalid.
- }else{
- AH_delete_buffer(buf, scanner);
- buf = NULL;
+ }else{
+ AH_delete_buffer(buf, scanner);
+ buf = NULL;
AHlex_destroy(scanner);
- return NO;
- }
- // default case, if the range checking above fails.
+ return NO;
+ }
+ // default case, if the range checking above fails.
AHlex_destroy(scanner);
- return NO;
+ return NO;
}
#pragma mark Accessors
-- (AHMarkedHyperlink *)nextURI
+- (AHMarkedHyperlink *)nextURIFromLocation:(unsigned long * const)_scanLocation
{
- NSRange scannedRange;
- unsigned long scannedLocation = m_scanLocation;
+ NSRange scannedRange = NSMakeRange(0, 0);
+ unsigned long scannedLocation = *_scanLocation;
- // scan upto the next whitespace char so that we don't unnecessarity confuse flex
- // otherwise we end up validating urls that look like this "http://www.adium.im/ <--cool"
+ // scan upto the next whitespace char so that we don't unnecessarity confuse flex
+ // otherwise we end up validating urls that look like this "http://www.adium.im/ <--cool"
[self _scanString:m_scanString charactersFromSet:startSet intoRange:nil fromIndex:&scannedLocation];
-
+
// main scanning loop
while([self _scanString:m_scanString upToCharactersFromSet:skipSet intoRange:&scannedRange fromIndex:&scannedLocation]) {
BOOL foundUnpairedEnclosureCharacter = NO;
-
+
// Check for and filter enclosures. We can't add (, [, etc. to the skipSet as they may be in a URI
if([enclosureSet characterIsMember:[m_scanString characterAtIndex:scannedRange.location]]){
unsigned long encIdx = [enclosureStartArray indexOfObject:[m_scanString substringWithRange:NSMakeRange(scannedRange.location, 1)]];
@@ -266,7 +279,7 @@
}
}
if(!scannedRange.length) break;
-
+
// Find balanced enclosure chars
NSRange longestEnclosure = [self _longestBalancedEnclosureInRange:scannedRange];
while (scannedRange.length > 2 && [endSet characterIsMember:[m_scanString characterAtIndex:(scannedRange.location + scannedRange.length - 1)]]) {
@@ -276,102 +289,105 @@
}else break;
}
- // if we have a valid URL then save the scanned string, and make a SHMarkedHyperlink out of it.
- // this way, we can preserve things like the matched string (to be converted to a NSURL),
- // parent string, its validation status (valid, file, degenerate, etc), and its range in the parent string
+ // if we have a valid URL then save the scanned string, and make a SHMarkedHyperlink out of it.
+ // this way, we can preserve things like the matched string (to be converted to a NSURL),
+ // parent string, its validation status (valid, file, degenerate, etc), and its range in the parent string
AH_URI_VERIFICATION_STATUS validStatus;
NSString *_scanString = nil;
if(3 < scannedRange.length) _scanString = [m_scanString substringWithRange:scannedRange];
-
- if((3 < scannedRange.length) && [[self class] isStringValidURI:_scanString usingStrict:m_strictChecking fromIndex:&m_scanLocation withStatus:&validStatus]){
- AHMarkedHyperlink *markedLink;
+
+ if((3 < scannedRange.length) && [[self class] isStringValidURI:_scanString usingStrict:m_strictChecking fromIndex:_scanLocation withStatus:&validStatus]){
+ AHMarkedHyperlink *markedLink;
- //insert typical specifiers if the URL is degenerate
- switch(validStatus){
- case AH_URL_DEGENERATE:
- {
- NSString *scheme = DEFAULT_URL_SCHEME;
- unsigned long i = 0;
-
- NSRange firstComponent;
- [self _scanString:_scanString
- upToCharactersFromSet:hostnameComponentSeparatorSet
- intoRange:&firstComponent
- fromIndex:&i];
-
- if(NSNotFound != firstComponent.location) {
- NSString *hostnameScheme = [m_urlSchemes objectForKey:[_scanString substringWithRange:firstComponent]];
- if(hostnameScheme) scheme = hostnameScheme;
- }
-
- _scanString = [scheme stringByAppendingString:_scanString];
-
- break;
- }
-
- case AH_MAILTO_DEGENERATE:
+ //insert typical specifiers if the URL is degenerate
+ switch(validStatus){
+ case AH_URL_DEGENERATE:
+ {
+ NSString *scheme = DEFAULT_URL_SCHEME;
+ unsigned long i = 0;
+
+ NSRange firstComponent;
+ [self _scanString:_scanString
+ upToCharactersFromSet:hostnameComponentSeparatorSet
+ intoRange:&firstComponent
+ fromIndex:&i];
+
+ if(NSNotFound != firstComponent.location) {
+ NSString *hostnameScheme = [m_urlSchemes objectForKey:[_scanString substringWithRange:firstComponent]];
+ if(hostnameScheme) scheme = hostnameScheme;
+ }
+
+ _scanString = [scheme stringByAppendingString:_scanString];
+
+ break;
+ }
+
+ case AH_MAILTO_DEGENERATE:
_scanString = [@"mailto:" stringByAppendingString:_scanString];
- break;
- default:
- break;
- }
-
- //make a marked link
- markedLink = [AHMarkedHyperlink hyperlinkWithString:_scanString
- withValidationStatus:validStatus
- parentString:m_scanString
- andRange:scannedRange];
- return [markedLink URL]? markedLink : nil;
- }
-
+ break;
+ default:
+ break;
+ }
+
+ //make a marked link
+ markedLink = [AHMarkedHyperlink hyperlinkWithString:_scanString
+ withValidationStatus:validStatus
+ parentString:m_scanString
+ andRange:scannedRange];
+ return [markedLink URL]? markedLink : nil;
+ }
+
//step location after scanning a string
if (foundUnpairedEnclosureCharacter){
- m_scanLocation++;
+ (*_scanLocation)++;
}else{
NSRange startRange = [m_scanString rangeOfCharacterFromSet:puncSet options:NSLiteralSearch range:scannedRange];
if (startRange.location != NSNotFound)
- m_scanLocation = startRange.location + startRange.length;
+ *_scanLocation = startRange.location + startRange.length;
else
- m_scanLocation += scannedRange.length;
+ *_scanLocation += scannedRange.length;
}
-
- scannedLocation = m_scanLocation;
- }
+
+ scannedLocation = *_scanLocation;
+ }
- // if we're here, then NSScanner hit the end of the string
- // set AHStringOffset to the string length here so we avoid potential infinite looping with many trailing spaces.
- m_scanLocation = m_scanStringLength;
- return nil;
+ // if we're here, then NSScanner hit the end of the string
+ // set AHStringOffset to the string length here so we avoid potential infinite looping with many trailing spaces.
+ *_scanLocation = m_scanStringLength;
+ return nil;
+}
+
+- (AHMarkedHyperlink *)nextURI
+{
+ @synchronized(self) {
+ return [self nextURIFromLocation:&m_scanLocation];
+ }
}
-(NSArray *)allURIs
{
- NSMutableArray *rangeArray = [NSMutableArray array];
- AHMarkedHyperlink *markedLink;
- unsigned long _holdOffset = m_scanLocation; // store location for later restoration;
- m_scanLocation = 0; //set the offset to 0.
-
- //build an array of marked links.
- while((markedLink = [self nextURI])){
+ NSMutableArray *rangeArray = [NSMutableArray array];
+ AHMarkedHyperlink *markedLink;
+ unsigned long offset = 0;
+
+ //build an array of marked links.
+ while((markedLink = [self nextURIFromLocation:&offset])){
[rangeArray addObject:markedLink];
}
- m_scanLocation = _holdOffset; // reset scanLocation
return rangeArray;
}
--(NSAttributedString *)linkifiedString
+-(NSAttributedString *)_createLinkifiedString
{
- NSMutableAttributedString *linkifiedString;
+ NSMutableAttributedString *_linkifiedString;
AHMarkedHyperlink *markedLink;
BOOL _didFindLinks = NO;
- unsigned long _holdOffset = m_scanLocation; // store location for later restoration;
+ unsigned long _scanLocationCache = self.scanLocation;
- m_scanLocation = 0;
-
if(m_scanAttrString) {
- linkifiedString = [[m_scanAttrString mutableCopy] autorelease];
+ _linkifiedString = [m_scanAttrString mutableCopy];
} else {
- linkifiedString = [[[NSMutableAttributedString alloc] initWithString:m_scanString] autorelease];
+ _linkifiedString = [[NSMutableAttributedString alloc] initWithString:m_scanString];
}
//for each SHMarkedHyperlink, add the proper URL to the proper range in the string.
@@ -379,26 +395,27 @@
NSURL *markedLinkURL;
_didFindLinks = YES;
if((markedLinkURL = [markedLink URL])) {
- [linkifiedString addAttribute:NSLinkAttributeName
+ [_linkifiedString addAttribute:NSLinkAttributeName
value:markedLinkURL
range:[markedLink range]];
}
}
-
- m_scanLocation = _holdOffset; // reset scanLocation
-
- return _didFindLinks? linkifiedString :
- m_scanAttrString ? [[m_scanAttrString retain] autorelease] : [[[NSMutableAttributedString alloc] initWithString:m_scanString] autorelease];
+
+ self.scanLocation = _scanLocationCache;
+ return _didFindLinks? _linkifiedString :
+ m_scanAttrString ? m_scanAttrString : [[NSMutableAttributedString alloc] initWithString:m_scanString];
}
--(unsigned long)scanLocation
+-(NSAttributedString *)linkifiedString
{
- return m_scanLocation;
-}
-
-- (void)setScanLocation:(unsigned int)location
-{
- m_scanLocation = location;
+ if(!m_linkifiedString){
+ NSAttributedString *newLinkifiedString = [self _createLinkifiedString];
+ // compare the old object to nil, and swap in the new value if they match.
+ // if the old object (m_linkifiedString) already has a value, release the duplicated new object
+ if(!OSAtomicCompareAndSwapPtrBarrier(nil, newLinkifiedString, (void *)&m_linkifiedString))
+ [newLinkifiedString release];
+ }
+ return [[[NSAttributedString alloc] initWithAttributedString:m_linkifiedString] autorelease];
}
#pragma mark NSFastEnumeration
@@ -430,15 +447,15 @@
while(encScanLocation < inRange.length + inRange.location) {
[self _scanString:m_scanString upToCharactersFromSet:enclosureSet intoRange:nil fromIndex:&encScanLocation];
-
+
if(encScanLocation >= (inRange.location + inRange.length)) break;
-
+
matchChar = [m_scanString substringWithRange:NSMakeRange(encScanLocation, 1)];
-
+
if([enclosureStartArray containsObject:matchChar]) {
encDict = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:[NSNumber numberWithUnsignedLong:encScanLocation], matchChar, nil]
- forKeys:encKeys];
- if(!enclosureStack) enclosureStack = [NSMutableArray arrayWithCapacity:1];
+ forKeys:encKeys];
+ if(!enclosureStack) enclosureStack = [NSMutableArray array];
[enclosureStack addObject:encDict];
}else if([enclosureStopArray containsObject:matchChar]) {
NSEnumerator *encEnumerator = [enclosureStack objectEnumerator];
@@ -447,8 +464,8 @@
unsigned long encStartIndex = [enclosureStartArray indexOfObjectIdenticalTo:[encDict objectForKey:ENC_CHAR_KEY]];
if([enclosureStopArray indexOfObjectIdenticalTo:matchChar] == encStartIndex) {
NSRange encRange = NSMakeRange(encTagIndex, encScanLocation - encTagIndex + 1);
- if(!enclosureStack) enclosureStack = [NSMutableArray arrayWithCapacity:1];
- if(!enclosureArray) enclosureArray = [NSMutableArray arrayWithCapacity:1];
+ if(!enclosureStack) enclosureStack = [NSMutableArray array];
+ if(!enclosureArray) enclosureArray = [NSMutableArray array];
[enclosureStack removeObject:encDict];
[enclosureArray addObject:NSStringFromRange(encRange)];
break;
More information about the commits
mailing list