Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
XML parser without NSScanner
  • Loading branch information
djphoenix committed May 15, 2015
1 parent 125ceff commit 305e525
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 57 deletions.
4 changes: 2 additions & 2 deletions pxSVG/pxSVGImage.h
Expand Up @@ -9,8 +9,8 @@
#import <UIKit/UIKit.h>

@interface pxSVGImage : NSObject
+ (instancetype) svgImageWithXML:(NSString*)data;
- (instancetype) initWithXML:(NSString*)data;
+ (instancetype) svgImageWithXML:(NSData*)data;
- (instancetype) initWithXML:(NSData*)data;
@property (nonatomic,readonly) CGRect bounds;
- (CALayer*)makeLayer;
@end
6 changes: 3 additions & 3 deletions pxSVG/pxSVGImage.m
Expand Up @@ -15,14 +15,14 @@ @interface pxSVGImage ()
@end

@implementation pxSVGImage
+ (instancetype)svgImageWithXML:(NSString *)xml
+ (instancetype)svgImageWithXML:(NSData *)xml
{
return [[self alloc] initWithXML:xml];
}
- (instancetype)initWithXML:(NSString *)xml
- (instancetype)initWithXML:(NSData *)xml
{
pxXMLNode *xmlTree =
[[pxXMLNode parseTree:[[NSScanner alloc] initWithString:xml]]
[[pxXMLNode parseTree:xml]
filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"tagName=%@",@"svg"]]
.firstObject;
if (!xmlTree) return nil;
Expand Down
23 changes: 11 additions & 12 deletions pxSVG/pxSVGLayer.m
Expand Up @@ -72,38 +72,32 @@ - (void)loadURL:(NSURL *)url
err = [NSError errorWithDomain:@"pxSVGLoader.httpStatus" code:((NSHTTPURLResponse*)resp).statusCode userInfo:nil];
if (!err && !data)
err = [NSError errorWithDomain:@"pxSVGLoader.emptyData" code:0 userInfo:nil];
NSString *str = data?[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]:nil;
data = nil, resp = nil;
resp = nil;
if ([op isCancelled]) {
op = nil, str = nil, err = nil;
op = nil, data = nil, err = nil;
return;
}
op = nil;
if (err) {
str = nil;
data = nil;
[weakself performSelectorOnMainThread:@selector(loadError:) withObject:err waitUntilDone:NO modes:@[NSRunLoopCommonModes]];
err = nil;
return;
}
[weakself performSelectorOnMainThread:@selector(loadString:) withObject:str waitUntilDone:NO modes:@[NSRunLoopCommonModes]];
str = nil, err = nil;
[weakself performSelectorOnMainThread:@selector(loadData:) withObject:data waitUntilDone:NO modes:@[NSRunLoopCommonModes]];
data = nil, err = nil;
} }];
op.name = url.absoluteString;
op.threadPriority = 0.1f;
[[self.class loadQueue] addOperation:self.loadOperation=op];
}

- (void)loadData:(NSData *)data
{
[self loadString:[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]];
}

- (void)loadString:(NSString *)string
{
[self clean];
__weak pxSVGLayer *weakself = self;
__block NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ @autoreleasepool {
pxSVGImage *img = [pxSVGImage svgImageWithXML:string];
pxSVGImage *img = [pxSVGImage svgImageWithXML:data];
if ([op isCancelled]) {
op = nil, img = nil;
return;
Expand All @@ -121,6 +115,11 @@ - (void)loadString:(NSString *)string
[[self.class parseQueue] addOperation:self.parseOperation=op];
}

- (void)loadString:(NSString *)string
{
[self loadData:[string dataUsingEncoding:NSUTF8StringEncoding]];
}

- (void)loadImage:(pxSVGImage*)image
{
[self clean];
Expand Down
3 changes: 1 addition & 2 deletions pxSVG/pxXMLNode.h
Expand Up @@ -9,8 +9,7 @@
#import <Foundation/Foundation.h>

@interface pxXMLNode : NSObject
+ (NSArray*) parseTree:(NSScanner*)scanner;
+ (instancetype) parseNode:(NSString*)string childScanner:(NSScanner*)childScanner;
+ (NSArray*) parseTree:(NSData*)data;
@property (readonly) NSString *tagName;
@property (readonly) NSDictionary *attributes;
@property (readonly) NSArray *childNodes;
Expand Down
99 changes: 61 additions & 38 deletions pxSVG/pxXMLNode.m
Expand Up @@ -16,47 +16,70 @@ @interface pxXMLNode ()

@implementation pxXMLNode

+ (NSArray *)parseTree:(NSScanner *)scanner
+ (NSArray *)parseTree:(NSData *)data
{
NSMutableArray *nodes = [NSMutableArray new];
NSString *tag;
pxXMLNode *node;
while (!scanner.isAtEnd) {
[scanner scanUpToString:@"<" intoString:nil];
[scanner scanUpToString:@">" intoString:&tag];
tag = [tag stringByAppendingString:@">"]; [scanner scanString:@">" intoString:nil];
if ([tag hasPrefix:@"</"]) break;
if ([tag characterAtIndex:1] == '?') continue;
if ([tag characterAtIndex:1] == '!') continue;
node = [self parseNode:tag childScanner:scanner];
[nodes addObject:node];
}
return [NSArray arrayWithArray:nodes];
}

+ (instancetype)parseNode:(NSString *)string childScanner:(NSScanner *)childScanner
{
NSCharacterSet *seps = [NSCharacterSet whitespaceAndNewlineCharacterSet];
NSScanner *scan = [[NSScanner alloc] initWithString:string];
[scan scanString:@"<" intoString:nil];
static NSCharacterSet *seps; if (!seps) seps = [NSCharacterSet whitespaceAndNewlineCharacterSet];
NSMutableDictionary *attrs = [[NSMutableDictionary alloc] initWithCapacity:100];
NSMutableArray *nodes = [[NSMutableArray alloc] initWithCapacity:100];
NSMutableArray *nodeStack = [[NSMutableArray alloc] initWithCapacity:100];
NSString *tagName, *attrName, *attrValue;
NSMutableDictionary *attrs = [NSMutableDictionary new];
[scan scanUpToCharactersFromSet:seps intoString:&tagName]; [scan scanCharactersFromSet:seps intoString:nil];

while ([scan scanUpToString:@"=" intoString:&attrName]) {
if(![scan scanString:@"=\"" intoString:nil]) break;
[scan scanUpToString:@"\"" intoString:&attrValue];
[scan scanString:@"\"" intoString:nil];
[scan scanCharactersFromSet:seps intoString:nil];
[attrs setObject:attrValue?:@"" forKey:attrName];
pxXMLNode *node;
NSUInteger idx = 0, tidx;
const char* bytes = data.bytes;
NSRange tagRange, attrRange;
while (idx < data.length) {
while ((idx < data.length) && (bytes[idx] != '<')) idx++;
tagRange.location = idx;
while ((idx < data.length) && (bytes[idx] != '>')) idx++;
tagRange.length = idx-tagRange.location;
idx++;
if (tagRange.length < 2) break;
if (bytes[tagRange.location+1] == '/') {
if (nodeStack.count == 0) break;
nodes = nodeStack.lastObject;
[nodeStack removeLastObject];
continue;
}
if (bytes[tagRange.location+1] == '?') continue;
if (bytes[tagRange.location+1] == '!') continue;
{
tidx = 1;
attrRange.location = 1;
[attrs removeAllObjects];
while ((tidx < tagRange.length) && ![seps characterIsMember:bytes[tagRange.location+tidx]]) tidx++;
attrRange.length = tidx-attrRange.location;
tagName = [[NSString alloc] initWithBytes:&bytes[tagRange.location+attrRange.location] length:attrRange.length encoding:NSUTF8StringEncoding];
while ((tidx < tagRange.length) && [seps characterIsMember:bytes[tagRange.location+tidx]]) tidx++;
while (tidx < tagRange.length) {
attrRange.location = tidx;
while ((tidx < tagRange.length) && (bytes[tagRange.location+tidx] != '=') && (bytes[tagRange.location+tidx] != '/')) tidx++;
attrRange.length = tidx-attrRange.location;
if (attrRange.length == 0) break;
attrName = [[NSString alloc] initWithBytes:&bytes[tagRange.location+attrRange.location] length:attrRange.length encoding:NSUTF8StringEncoding];
if (tagRange.length-idx < 2) break;
if ((bytes[tagRange.location+tidx] != '=') || (bytes[tagRange.location+tidx+1] != '"')) break;
tidx += 2;
attrRange.location = tidx;
while ((tidx < tagRange.length) && (bytes[tagRange.location+tidx] != '"')) tidx++;
attrRange.length = tidx-attrRange.location;
attrValue = [[NSString alloc] initWithBytes:&bytes[tagRange.location+attrRange.location] length:attrRange.length encoding:NSUTF8StringEncoding];
tidx++;
while ((tidx < tagRange.length) && [seps characterIsMember:bytes[tagRange.location+tidx]]) tidx++;
[attrs setObject:attrValue?:@"" forKey:attrName];
}
BOOL selfClose = bytes[tagRange.location+tagRange.length-1] == '/';
node = [self new];
node.tagName = tagName;
node.attributes = [NSDictionary dictionaryWithDictionary:attrs];
[nodes addObject:node];
if (selfClose) node.childNodes = @[];
else {
[nodeStack addObject:nodes];
node.childNodes = nodes = [[NSMutableArray alloc] initWithCapacity:100];
}
}
}
if ([tagName hasSuffix:@">"]) tagName = [tagName substringToIndex:tagName.length-1];
BOOL selfClose = [[attrName stringByTrimmingCharactersInSet:seps] hasPrefix:@"/"];
pxXMLNode *node = [self new];
node.tagName = tagName;
node.attributes = attrs;
node.childNodes = selfClose?@[]:[self parseTree:childScanner];
return node;
return (nodeStack.count>0)?nodeStack.firstObject:[NSArray arrayWithArray:nodes];
}

- (NSString *)description
Expand Down

0 comments on commit 305e525

Please sign in to comment.