|
13 | 13 |
|
14 | 14 | #import "git2/errors.h" |
15 | 15 | #import "git2/sys/filter.h" |
16 | | -#import "git2/deprecated.h" |
17 | 16 |
|
18 | 17 | NSString * const GTFilterErrorDomain = @"GTFilterErrorDomain"; |
19 | 18 |
|
@@ -57,7 +56,7 @@ - (instancetype)initWithName:(NSString *)name attributes:(NSString *)attributes |
57 | 56 |
|
58 | 57 | _filter.version = GIT_FILTER_VERSION; |
59 | 58 | _filter.attributes = attributes.UTF8String; |
60 | | - _filter.apply = >FilterApply; |
| 59 | + _filter.stream = >FilterStreamNew; |
61 | 60 |
|
62 | 61 | _name = [name copy]; |
63 | 62 | _applyBlock = [applyBlock copy]; |
@@ -103,14 +102,73 @@ static int GTFilterCheck(git_filter *filter, void **payload, const git_filter_so |
103 | 102 | return accept ? 0 : GIT_PASSTHROUGH; |
104 | 103 | } |
105 | 104 |
|
106 | | -static int GTFilterApply(git_filter *filter, void **payload, git_buf *to, const git_buf *from, const git_filter_source *src) { |
107 | | - GTFilter *self = GTFiltersGitFilterToRegisteredFilters[[NSValue valueWithPointer:filter]]; |
108 | | - NSData *fromData = [NSData dataWithBytesNoCopy:from->ptr length:from->size freeWhenDone:NO]; |
| 105 | +// Backing storage for the `git_writestream` handed to libgit2 by |
| 106 | +// `GTFilterStreamNew`. `git_writestream` structs are used with C-style |
| 107 | +// "inheritance": `parent` must be the first field so a `GTFilterWriteStream *` |
| 108 | +// can be reinterpreted as a `git_writestream *`. |
| 109 | +// |
| 110 | +// The apply block operates on a complete buffer (an `NSData`), not a stream, |
| 111 | +// so incoming chunks are accumulated in `bufferRef` and only handed to the |
| 112 | +// block once `close` is called (i.e. once all data has been written). |
| 113 | +typedef struct { |
| 114 | + git_writestream parent; |
| 115 | + void *filterRef; // (GTFilter *), retained via CFBridgingRetain |
| 116 | + void *sourceRef; // (GTFilterSource *), retained via CFBridgingRetain |
| 117 | + void *bufferRef; // (NSMutableData *), retained via CFBridgingRetain |
| 118 | + void **payload; |
| 119 | + git_writestream *next; |
| 120 | +} GTFilterWriteStream; |
| 121 | + |
| 122 | +static int GTFilterStreamWrite(git_writestream *s, const char *buffer, size_t len) { |
| 123 | + GTFilterWriteStream *stream = (GTFilterWriteStream *)s; |
| 124 | + NSMutableData *data = (__bridge NSMutableData *)stream->bufferRef; |
| 125 | + [data appendBytes:buffer length:len]; |
| 126 | + return 0; |
| 127 | +} |
| 128 | + |
| 129 | +static int GTFilterStreamClose(git_writestream *s) { |
| 130 | + GTFilterWriteStream *stream = (GTFilterWriteStream *)s; |
| 131 | + GTFilter *filter = (__bridge GTFilter *)stream->filterRef; |
| 132 | + GTFilterSource *source = (__bridge GTFilterSource *)stream->sourceRef; |
| 133 | + NSData *fromData = (__bridge NSData *)stream->bufferRef; |
| 134 | + |
109 | 135 | BOOL applied = YES; |
110 | | - NSData *toData = self.applyBlock(payload, fromData, [[GTFilterSource alloc] initWithGitFilterSource:src], &applied); |
111 | | - if (!applied) return GIT_PASSTHROUGH; |
| 136 | + NSData *toData = filter.applyBlock(stream->payload, fromData, source, &applied); |
| 137 | + NSData *outputData = applied ? toData : fromData; |
| 138 | + |
| 139 | + git_writestream *next = stream->next; |
| 140 | + int result = next->write(next, outputData.bytes, outputData.length); |
| 141 | + if (result < 0) return result; |
| 142 | + |
| 143 | + return next->close(next); |
| 144 | +} |
| 145 | + |
| 146 | +static void GTFilterStreamFree(git_writestream *s) { |
| 147 | + GTFilterWriteStream *stream = (GTFilterWriteStream *)s; |
| 148 | + CFBridgingRelease(stream->filterRef); |
| 149 | + CFBridgingRelease(stream->sourceRef); |
| 150 | + CFBridgingRelease(stream->bufferRef); |
| 151 | + free(stream); |
| 152 | +} |
| 153 | + |
| 154 | +static int GTFilterStreamNew(git_writestream **out, git_filter *filter, void **payload, const git_filter_source *src, git_writestream *next) { |
| 155 | + GTFilter *self = GTFiltersGitFilterToRegisteredFilters[[NSValue valueWithPointer:filter]]; |
| 156 | + GTFilterSource *source = [[GTFilterSource alloc] initWithGitFilterSource:src]; |
| 157 | + NSCAssert(source != nil, @"Unexpected nil filter source"); |
| 158 | + |
| 159 | + GTFilterWriteStream *stream = calloc(1, sizeof(GTFilterWriteStream)); |
| 160 | + if (stream == NULL) return GIT_ERROR; |
| 161 | + |
| 162 | + stream->parent.write = GTFilterStreamWrite; |
| 163 | + stream->parent.close = GTFilterStreamClose; |
| 164 | + stream->parent.free = GTFilterStreamFree; |
| 165 | + stream->filterRef = (void *)CFBridgingRetain(self); |
| 166 | + stream->sourceRef = (void *)CFBridgingRetain(source); |
| 167 | + stream->bufferRef = (void *)CFBridgingRetain([NSMutableData data]); |
| 168 | + stream->payload = payload; |
| 169 | + stream->next = next; |
112 | 170 |
|
113 | | - git_buf_set(to, toData.bytes, toData.length); |
| 171 | + *out = (git_writestream *)stream; |
114 | 172 | return 0; |
115 | 173 | } |
116 | 174 |
|
|
0 commit comments