From d8a55db4dc919c465131f470106c4182f16de20b Mon Sep 17 00:00:00 2001 From: iska Date: Sun, 12 Apr 2015 18:23:27 +0200 Subject: [PATCH] Change the implementation of the character token and string methods to reduce initialization overhead All methods operate on the same NSString instance instead of creating a new character token, e.g. when trimming or splitting the leading whitespace. --- HTMLKit/HTMLCharacterToken.h | 10 +-- HTMLKit/HTMLCharacterToken.m | 43 +++++++----- HTMLKit/HTMLParser.m | 127 +++++++++++++++++++++-------------- HTMLKit/NSString+HTMLKit.h | 1 - HTMLKit/NSString+HTMLKit.m | 20 +++--- 5 files changed, 121 insertions(+), 80 deletions(-) diff --git a/HTMLKit/HTMLCharacterToken.h b/HTMLKit/HTMLCharacterToken.h index be3d4ee..0341991 100644 --- a/HTMLKit/HTMLCharacterToken.h +++ b/HTMLKit/HTMLCharacterToken.h @@ -16,10 +16,12 @@ - (instancetype)initWithString:(NSString *)string; - (void)appendString:(NSString *)string; - - (BOOL)isWhitespaceToken; -- (HTMLCharacterToken *)tokenByRetainingLeadingWhitespace; -- (HTMLCharacterToken *)tokenByTrimmingLeadingWhitespace; -- (HTMLCharacterToken *)tokenByTrimmingFormIndex:(NSUInteger)index; +- (BOOL)isEmpty; + +- (void)retainLeadingWhitespace; +- (void)trimLeadingWhitespace; +- (void)trimFormIndex:(NSUInteger)index; +- (HTMLCharacterToken *)tokenBySplitingLeadingWhiteSpace; @end diff --git a/HTMLKit/HTMLCharacterToken.m b/HTMLKit/HTMLCharacterToken.m index 2548558..1425778 100644 --- a/HTMLKit/HTMLCharacterToken.m +++ b/HTMLKit/HTMLCharacterToken.m @@ -39,31 +39,44 @@ return [_characters isHTMLWhitespaceString]; } -- (HTMLCharacterToken *)tokenByRetainingLeadingWhitespace +- (BOOL)isEmpty +{ + return _characters.length == 0; +} + +- (void)retainLeadingWhitespace +{ + NSUInteger index = _characters.leadingWhitespaceLength; + if (index > 0) { + [_characters setString:[_characters substringToIndex:index]]; + } +} + +- (void)trimLeadingWhitespace +{ + NSUInteger index = _characters.leadingWhitespaceLength; + if (index > 0) { + [_characters setString:[_characters substringFromIndex:index]]; + } +} + +- (void)trimFormIndex:(NSUInteger)index +{ + [_characters setString:[_characters substringFromIndex:index]]; +} + +- (HTMLCharacterToken *)tokenBySplitingLeadingWhiteSpace { NSUInteger index = _characters.leadingWhitespaceLength; if (index > 0) { NSString *leading = [_characters substringToIndex:index]; + [_characters setString:[_characters substringFromIndex:index]]; return [[HTMLCharacterToken alloc] initWithString:leading]; } - return nil; -} -- (HTMLCharacterToken *)tokenByTrimmingLeadingWhitespace -{ - NSUInteger index = _characters.leadingWhitespaceLength; - if (index < _characters.length) { - NSString *remaining = [_characters substringFromIndex:index]; - return [[HTMLCharacterToken alloc] initWithString:remaining]; - } return nil; } -- (HTMLCharacterToken *)tokenByTrimmingFormIndex:(NSUInteger)index -{ - return [[HTMLCharacterToken alloc] initWithString:[_characters substringFromIndex:index]]; -} - #pragma mark - NSObject - (BOOL)isEqual:(id)other diff --git a/HTMLKit/HTMLParser.m b/HTMLKit/HTMLParser.m index 4b9b8db..5a28b5d 100644 --- a/HTMLKit/HTMLParser.m +++ b/HTMLKit/HTMLParser.m @@ -253,7 +253,7 @@ if (characters.length <= 1) { return; } - token = [token.asCharacterToken tokenByTrimmingFormIndex:1]; + [token.asCharacterToken trimFormIndex:1]; } } } @@ -713,10 +713,14 @@ { switch (token.type) { case HTMLTokenTypeCharacter: - if ([token.asCharacterToken isWhitespaceToken]) { + { + [token.asCharacterToken trimLeadingWhitespace]; + + if (token.asCharacterToken.isEmpty) { return; } break; + } case HTMLTokenTypeComment: [self insertComment:token.asCommentToken asChildOfNode:_document]; return; @@ -761,10 +765,14 @@ [self insertComment:token.asCommentToken asChildOfNode:_document]; return; case HTMLTokenTypeCharacter: - if ([token.asCharacterToken isWhitespaceToken]) { + { + [token.asCharacterToken trimLeadingWhitespace]; + + if (token.asCharacterToken.isEmpty) { return; } break; + } case HTMLTokenTypeStartTag: if ([token.asStartTagToken.tagName isEqualToString:@"html"]) { HTMLElement *html = [self createElementForToken:token.asTagToken inNamespace:HTMLNamespaceHTML]; @@ -795,10 +803,14 @@ { switch (token.type) { case HTMLTokenTypeCharacter: - if ([token.asCharacterToken isWhitespaceToken]) { + { + [token.asCharacterToken trimLeadingWhitespace]; + + if (token.asCharacterToken.isEmpty) { return; } break; + } case HTMLTokenTypeComment: [self insertComment:token.asCommentToken]; return; @@ -838,11 +850,12 @@ switch (token.type) { case HTMLTokenTypeCharacter: { - HTMLCharacterToken *leadingWhiteSpace = [token.asCharacterToken tokenByRetainingLeadingWhitespace]; + HTMLCharacterToken *leadingWhiteSpace = [token.asCharacterToken tokenBySplitingLeadingWhiteSpace]; if (leadingWhiteSpace) { [self insertCharacters:leadingWhiteSpace.characters]; } - if ([token.asCharacterToken isWhitespaceToken]) { + + if (token.asCharacterToken.isEmpty) { return; } break; @@ -955,11 +968,12 @@ switch (token.type) { case HTMLTokenTypeCharacter: { - HTMLCharacterToken *leadingWhiteSpace = [token.asCharacterToken tokenByRetainingLeadingWhitespace]; + HTMLCharacterToken *leadingWhiteSpace = [token.asCharacterToken tokenBySplitingLeadingWhiteSpace]; if (leadingWhiteSpace) { [self insertCharacters:leadingWhiteSpace.characters]; } - if ([token.asCharacterToken isWhitespaceToken]) { + + if (token.asCharacterToken.isEmpty) { return; } break; @@ -1037,7 +1051,7 @@ if (charactes.length > 0) { [self reconstructActiveFormattingElements]; [self insertCharacters:charactes]; - if (![charactes containsHTMLWhitespace]) { + if (!charactes.isHTMLWhitespaceString) { _framesetOkFlag = NO; } } @@ -1740,11 +1754,12 @@ switch (token.type) { case HTMLTokenTypeCharacter: { - HTMLCharacterToken *leadingWhiteSpace = [token.asCharacterToken tokenByRetainingLeadingWhitespace]; + HTMLCharacterToken *leadingWhiteSpace = [token.asCharacterToken tokenBySplitingLeadingWhiteSpace]; if (leadingWhiteSpace) { [self insertCharacters:leadingWhiteSpace.characters]; } - if ([token.asCharacterToken isWhitespaceToken]) { + + if (token.asCharacterToken.isEmpty) { return; } break; @@ -2110,11 +2125,12 @@ switch (token.type) { case HTMLTokenTypeCharacter: { - HTMLCharacterToken *leadingWhiteSpace = [token.asCharacterToken tokenByRetainingLeadingWhitespace]; + HTMLCharacterToken *leadingWhiteSpace = [token.asCharacterToken tokenBySplitingLeadingWhiteSpace]; if (leadingWhiteSpace) { [self insertCharacters:leadingWhiteSpace.characters]; } - if ([token.asCharacterToken isWhitespaceToken]) { + + if (token.asCharacterToken.isEmpty) { return; } break; @@ -2158,14 +2174,19 @@ switch (token.type) { case HTMLTokenTypeCharacter: { - HTMLCharacterToken *leadingWhiteSpace = [token.asCharacterToken tokenByRetainingLeadingWhitespace]; - if (leadingWhiteSpace) { - [self insertCharacters:leadingWhiteSpace.characters]; - } - if ([token.asCharacterToken isWhitespaceToken]) { - return; - } - break; + NSString *characters = token.asCharacterToken.characters; + + [characters enumerateSubstringsInRange:NSMakeRange(0, characters.length) + options:NSStringEnumerationByComposedCharacterSequences + usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) { + if (substring.isHTMLWhitespaceString) { + [self insertCharacters:substring]; + } else { + [self emitParseError:@"Unexpected Character (%@) in ", substring]; + } + }]; + + return; } case HTMLTokenTypeComment: [self insertComment:token.asCommentToken]; @@ -2221,14 +2242,19 @@ switch (token.type) { case HTMLTokenTypeCharacter: { - HTMLCharacterToken *leadingWhiteSpace = [token.asCharacterToken tokenByRetainingLeadingWhitespace]; - if (leadingWhiteSpace) { - [self insertCharacters:leadingWhiteSpace.characters]; - } - if ([token.asCharacterToken isWhitespaceToken]) { - return; - } - break; + NSString *characters = token.asCharacterToken.characters; + + [characters enumerateSubstringsInRange:NSMakeRange(0, characters.length) + options:NSStringEnumerationByComposedCharacterSequences + usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) { + if (substring.isHTMLWhitespaceString) { + [self insertCharacters:substring]; + } else { + [self emitParseError:@"Unexpected Character (%@) after ", substring]; + } + }]; + + return; } case HTMLTokenTypeComment: [self insertComment:token.asCommentToken]; @@ -2269,11 +2295,12 @@ return; case HTMLTokenTypeCharacter: { - HTMLCharacterToken *leadingWhiteSpace = [token.asCharacterToken tokenByRetainingLeadingWhitespace]; + HTMLCharacterToken *leadingWhiteSpace = [token.asCharacterToken tokenBySplitingLeadingWhiteSpace]; if (leadingWhiteSpace) { - [self HTMLInsertionModeInBody:token]; + [self insertCharacters:leadingWhiteSpace.characters]; } - if ([token.asCharacterToken isWhitespaceToken]) { + + if (token.asCharacterToken.isEmpty) { return; } break; @@ -2306,11 +2333,12 @@ return; case HTMLTokenTypeCharacter: { - HTMLCharacterToken *leadingWhiteSpace = [token.asCharacterToken tokenByRetainingLeadingWhitespace]; + HTMLCharacterToken *leadingWhiteSpace = [token.asCharacterToken tokenBySplitingLeadingWhiteSpace]; if (leadingWhiteSpace) { - [self HTMLInsertionModeInBody:token]; + [self insertCharacters:leadingWhiteSpace.characters]; } - if ([token.asCharacterToken isWhitespaceToken]) { + + if (token.asCharacterToken.isEmpty) { return; } break; @@ -2342,22 +2370,23 @@ switch (token.type) { case HTMLTokenTypeCharacter: { - NSMutableString *charactes = [token.asCharacterToken.characters mutableCopy]; - NSUInteger nullCount = [charactes replaceOccurrencesOfString:@"\0" - withString:@"\uFFFD" - options:NSLiteralSearch - range:NSMakeRange(0, charactes.length)]; + NSMutableString *characters = [token.asCharacterToken.characters mutableCopy]; + [characters replaceOccurrencesOfString:@"\0" + withString:@"\uFFFD" + options:NSLiteralSearch + range:NSMakeRange(0, characters.length)]; - for (int i = 0; i < nullCount; i++) { - [self emitParseError:@"Unexpected Character (0x0000) in foreign content"]; - } + [characters enumerateSubstringsInRange:NSMakeRange(0, characters.length) + options:NSStringEnumerationByComposedCharacterSequences + usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) { + if ([substring isEqualToString:@"\uFFFD"]) { + [self emitParseError:@"Unexpected Character (0x0000) in foreign content"]; + } else if (!substring.isHTMLWhitespaceString) { + _framesetOkFlag = NO; + } + [self insertCharacters:substring]; + }]; - if (charactes.length > 0) { - [self insertCharacters:charactes]; - if (charactes.length > nullCount && ![charactes containsHTMLWhitespace]) { - _framesetOkFlag = NO; - } - } return; } case HTMLTokenTypeComment: diff --git a/HTMLKit/NSString+HTMLKit.h b/HTMLKit/NSString+HTMLKit.h index 5f2e9fd..53f963a 100644 --- a/HTMLKit/NSString+HTMLKit.h +++ b/HTMLKit/NSString+HTMLKit.h @@ -14,7 +14,6 @@ - (BOOL)isEqualToAny:(NSString *)first, ... NS_REQUIRES_NIL_TERMINATION; - (BOOL)hasPrefixIgnoringCase:(NSString *)aString; - (BOOL)isHTMLWhitespaceString; -- (BOOL)containsHTMLWhitespace; - (NSUInteger)leadingWhitespaceLength; @end diff --git a/HTMLKit/NSString+HTMLKit.m b/HTMLKit/NSString+HTMLKit.m index fe4d8b7..04870d2 100644 --- a/HTMLKit/NSString+HTMLKit.m +++ b/HTMLKit/NSString+HTMLKit.m @@ -8,7 +8,7 @@ #import "NSString+HTMLKit.h" -NS_INLINE BOOL isHtmlWhitespaceChar(char c) +NS_INLINE BOOL isHtmlWhitespaceChar(unichar c) { return c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r'; } @@ -42,21 +42,19 @@ NS_INLINE BOOL isHtmlWhitespaceChar(char c) - (BOOL)isHTMLWhitespaceString { - NSCharacterSet *set = [[NSCharacterSet characterSetWithCharactersInString:@" \t\n\f\r"] invertedSet]; - return [self rangeOfCharacterFromSet:set].location == NSNotFound; -} - -- (BOOL)containsHTMLWhitespace -{ - NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString:@" \t\n\f\r"]; - return [self rangeOfCharacterFromSet:set].location != NSNotFound; + return self.leadingWhitespaceLength == self.length; } - (NSUInteger)leadingWhitespaceLength { - const char *str = self.UTF8String; size_t idx = 0; - while (isHtmlWhitespaceChar(*str)) { str++; idx++; } + NSUInteger length = self.length; + while (idx < length) { + if (!isHtmlWhitespaceChar([self characterAtIndex:idx])) { + return idx; + } + idx++; + } return idx; }