forked from nolanw/HTMLReader
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathHTMLTreeEnumerator.m
104 lines (86 loc) · 2.73 KB
/
HTMLTreeEnumerator.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
// HTMLTreeEnumerator.m
//
// Public domain. https://github.com/nolanw/HTMLReader
#import "HTMLTreeEnumerator.h"
#import "HTMLNode.h"
NS_ASSUME_NONNULL_BEGIN
// For performance we'll cache the number of nodes at each level of the tree.
typedef struct {
NSUInteger i;
NSUInteger count;
} Row;
typedef struct {
Row *path;
NSUInteger length;
NSUInteger capacity;
} IndexPath;
@implementation HTMLTreeEnumerator
{
HTMLNode *_nextNode;
BOOL _reversed;
IndexPath _indexPath;
}
- (void)dealloc
{
free(_indexPath.path);
}
- (instancetype)initWithNode:(HTMLNode *)node reversed:(BOOL)reversed
{
NSParameterAssert(node);
if ((self = [super init])) {
_nextNode = node;
_reversed = reversed;
}
return self;
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-pragmas"
#pragma clang diagnostic ignored "-Wobjc-designated-initializers"
- (instancetype)init
{
NSAssert(NO, @"send -initWithNode:reversed:");
return nil;
}
#pragma clang diagnostic pop
- (id __nullable)nextObject
{
// This enumerator works by storing the *next* node we intend to emit, and the index path that points to that next node.
HTMLNode *currentNode = _nextNode;
NSUInteger numberOfChildren = currentNode.numberOfChildren;
if (numberOfChildren > 0) {
// Depth-first means the next node we'll emit is the current node's first child.
if (_indexPath.length == _indexPath.capacity) {
_indexPath.capacity += 16;
_indexPath.path = reallocf(_indexPath.path, sizeof(_indexPath.path[0]) * _indexPath.capacity);
}
Row *row = _indexPath.path + _indexPath.length;
_indexPath.length++;
row->count = numberOfChildren;
row->i = _reversed ? numberOfChildren - 1 : 0;
_nextNode = [currentNode childAtIndex:row->i];
} else {
// We're out of children on this row, so walk back up the tree until we find a level with spare children.
HTMLNode *parentNode = currentNode.parentNode;
while (_indexPath.length > 0) {
Row *row = _indexPath.path + _indexPath.length - 1;
if (_reversed && row->i > 0) {
row->i--;
} else if (!_reversed && row->i + 1 < row->count) {
row->i++;
} else {
_indexPath.length--;
parentNode = parentNode.parentNode;
continue;
}
_nextNode = [parentNode childAtIndex:row->i];
break;
}
// No more spare children means we're done.
if (_indexPath.length == 0) {
_nextNode = nil;
}
}
return currentNode;
}
@end
NS_ASSUME_NONNULL_END