Skip to content

Commit b16a36e

Browse files
authored
Merge pull request libgit2#6011 from libgit2/ethomson/filter_apply
filter: filter drivers stop taking git_buf as user input
2 parents 258115d + 5bcef52 commit b16a36e

File tree

7 files changed

+190
-86
lines changed

7 files changed

+190
-86
lines changed

include/git2/sys/filter.h

Lines changed: 49 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -167,17 +167,18 @@ typedef void GIT_CALLBACK(git_filter_shutdown_fn)(git_filter *self);
167167
*
168168
* The `payload` will be a pointer to a reference payload for the filter.
169169
* This will start as NULL, but `check` can assign to this pointer for
170-
* later use by the `apply` callback. Note that the value should be heap
171-
* allocated (not stack), so that it doesn't go away before the `apply`
170+
* later use by the `stream` callback. Note that the value should be heap
171+
* allocated (not stack), so that it doesn't go away before the `stream`
172172
* callback can use it. If a filter allocates and assigns a value to the
173173
* `payload`, it will need a `cleanup` callback to free the payload.
174174
*/
175175
typedef int GIT_CALLBACK(git_filter_check_fn)(
176-
git_filter *self,
177-
void **payload, /* points to NULL ptr on entry, may be set */
176+
git_filter *self,
177+
void **payload, /* NULL on entry, may be set */
178178
const git_filter_source *src,
179-
const char **attr_values);
179+
const char **attr_values);
180180

181+
#ifndef GIT_DEPRECATE_HARD
181182
/**
182183
* Callback to actually perform the data filtering
183184
*
@@ -189,32 +190,45 @@ typedef int GIT_CALLBACK(git_filter_check_fn)(
189190
*
190191
* The `payload` value will refer to any payload that was set by the
191192
* `check` callback. It may be read from or written to as needed.
193+
*
194+
* @deprecated use git_filter_stream_fn
192195
*/
193196
typedef int GIT_CALLBACK(git_filter_apply_fn)(
194-
git_filter *self,
195-
void **payload, /* may be read and/or set */
196-
git_buf *to,
197-
const git_buf *from,
197+
git_filter *self,
198+
void **payload, /* may be read and/or set */
199+
git_buf *to,
200+
const git_buf *from,
198201
const git_filter_source *src);
202+
#endif
199203

204+
/**
205+
* Callback to perform the data filtering.
206+
*
207+
* Specified as `filter.stream`, this is a callback that filters data
208+
* in a streaming manner. This function will provide a
209+
* `git_writestream` that will the original data will be written to;
210+
* with that data, the `git_writestream` will then perform the filter
211+
* translation and stream the filtered data out to the `next` location.
212+
*/
200213
typedef int GIT_CALLBACK(git_filter_stream_fn)(
201-
git_writestream **out,
202-
git_filter *self,
203-
void **payload,
214+
git_writestream **out,
215+
git_filter *self,
216+
void **payload,
204217
const git_filter_source *src,
205-
git_writestream *next);
218+
git_writestream *next);
206219

207220
/**
208221
* Callback to clean up after filtering has been applied
209222
*
210223
* Specified as `filter.cleanup`, this is an optional callback invoked
211-
* after the filter has been applied. If the `check` or `apply` callbacks
212-
* allocated a `payload` to keep per-source filter state, use this
213-
* callback to free that payload and release resources as required.
224+
* after the filter has been applied. If the `check`, `apply`, or
225+
* `stream` callbacks allocated a `payload` to keep per-source filter
226+
* state, use this callback to free that payload and release resources
227+
* as required.
214228
*/
215229
typedef void GIT_CALLBACK(git_filter_cleanup_fn)(
216-
git_filter *self,
217-
void *payload);
230+
git_filter *self,
231+
void *payload);
218232

219233
/**
220234
* Filter structure used to register custom filters.
@@ -248,21 +262,28 @@ struct git_filter {
248262
/**
249263
* Called to determine whether the filter should be invoked for a
250264
* given file. If this function returns `GIT_PASSTHROUGH` then the
251-
* `apply` function will not be invoked and the contents will be passed
252-
* through unmodified.
265+
* `stream` or `apply` functions will not be invoked and the
266+
* contents will be passed through unmodified.
253267
*/
254268
git_filter_check_fn check;
255269

270+
#ifdef GIT_DEPRECATE_HARD
271+
void *reserved;
272+
#else
256273
/**
257-
* Called to actually apply the filter to file contents. If this
258-
* function returns `GIT_PASSTHROUGH` then the contents will be passed
259-
* through unmodified.
274+
* Provided for backward compatibility; this will apply the
275+
* filter to the given contents in a `git_buf`. Callers should
276+
* provide a `stream` function instead.
260277
*/
261278
git_filter_apply_fn apply;
279+
#endif
262280

263281
/**
264-
* Called to apply the filter in a streaming manner. If this is not
265-
* specified then the system will call `apply` with the whole buffer.
282+
* Called to apply the filter, this function will provide a
283+
* `git_writestream` that will the original data will be
284+
* written to; with that data, the `git_writestream` will then
285+
* perform the filter translation and stream the filtered data
286+
* out to the `next` location.
266287
*/
267288
git_filter_stream_fn stream;
268289

@@ -289,9 +310,9 @@ GIT_EXTERN(int) git_filter_init(git_filter *filter, unsigned int version);
289310
* As mentioned elsewhere, the initialize callback will not be invoked
290311
* immediately. It is deferred until the filter is used in some way.
291312
*
292-
* A filter's attribute checks and `check` and `apply` callbacks will be
293-
* issued in order of `priority` on smudge (to workdir), and in reverse
294-
* order of `priority` on clean (to odb).
313+
* A filter's attribute checks and `check` and `stream` (or `apply`)
314+
* callbacks will be issued in order of `priority` on smudge (to
315+
* workdir), and in reverse order of `priority` on clean (to odb).
295316
*
296317
* Two filters are preregistered with libgit2:
297318
* - GIT_FILTER_CRLF with priority 0

src/crlf.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,17 @@ static int crlf_apply(
386386
return crlf_apply_to_odb(*payload, to, from, src);
387387
}
388388

389+
static int crlf_stream(
390+
git_writestream **out,
391+
git_filter *self,
392+
void **payload,
393+
const git_filter_source *src,
394+
git_writestream *next)
395+
{
396+
return git_filter_buffered_stream_new(out,
397+
self, crlf_apply, NULL, payload, src, next);
398+
}
399+
389400
static void crlf_cleanup(
390401
git_filter *self,
391402
void *payload)
@@ -405,7 +416,7 @@ git_filter *git_crlf_filter_new(void)
405416
f->f.initialize = NULL;
406417
f->f.shutdown = git_filter_free;
407418
f->f.check = crlf_check;
408-
f->f.apply = crlf_apply;
419+
f->f.stream = crlf_stream;
409420
f->f.cleanup = crlf_cleanup;
410421

411422
return (git_filter *)f;

src/filter.c

Lines changed: 71 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -839,9 +839,10 @@ int git_filter_list_apply_to_blob(
839839
return error;
840840
}
841841

842-
struct proxy_stream {
842+
struct buffered_stream {
843843
git_writestream parent;
844844
git_filter *filter;
845+
int (*write_fn)(git_filter *, void **, git_buf *, const git_buf *, const git_filter_source *);
845846
const git_filter_source *source;
846847
void **payload;
847848
git_buf input;
@@ -850,92 +851,120 @@ struct proxy_stream {
850851
git_writestream *target;
851852
};
852853

853-
static int proxy_stream_write(
854+
static int buffered_stream_write(
854855
git_writestream *s, const char *buffer, size_t len)
855856
{
856-
struct proxy_stream *proxy_stream = (struct proxy_stream *)s;
857-
GIT_ASSERT_ARG(proxy_stream);
857+
struct buffered_stream *buffered_stream = (struct buffered_stream *)s;
858+
GIT_ASSERT_ARG(buffered_stream);
858859

859-
return git_buf_put(&proxy_stream->input, buffer, len);
860+
return git_buf_put(&buffered_stream->input, buffer, len);
860861
}
861862

862-
static int proxy_stream_close(git_writestream *s)
863+
static int buffered_stream_close(git_writestream *s)
863864
{
864-
struct proxy_stream *proxy_stream = (struct proxy_stream *)s;
865+
struct buffered_stream *buffered_stream = (struct buffered_stream *)s;
865866
git_buf *writebuf;
866867
git_error_state error_state = {0};
867868
int error;
868869

869-
GIT_ASSERT_ARG(proxy_stream);
870+
GIT_ASSERT_ARG(buffered_stream);
870871

871-
error = proxy_stream->filter->apply(
872-
proxy_stream->filter,
873-
proxy_stream->payload,
874-
proxy_stream->output,
875-
&proxy_stream->input,
876-
proxy_stream->source);
872+
error = buffered_stream->write_fn(
873+
buffered_stream->filter,
874+
buffered_stream->payload,
875+
buffered_stream->output,
876+
&buffered_stream->input,
877+
buffered_stream->source);
877878

878879
if (error == GIT_PASSTHROUGH) {
879-
writebuf = &proxy_stream->input;
880+
writebuf = &buffered_stream->input;
880881
} else if (error == 0) {
881-
if ((error = git_buf_sanitize(proxy_stream->output)) < 0)
882+
if ((error = git_buf_sanitize(buffered_stream->output)) < 0)
882883
return error;
883884

884-
writebuf = proxy_stream->output;
885+
writebuf = buffered_stream->output;
885886
} else {
886887
/* close stream before erroring out taking care
887888
* to preserve the original error */
888889
git_error_state_capture(&error_state, error);
889-
proxy_stream->target->close(proxy_stream->target);
890+
buffered_stream->target->close(buffered_stream->target);
890891
git_error_state_restore(&error_state);
891892
return error;
892893
}
893894

894-
if ((error = proxy_stream->target->write(
895-
proxy_stream->target, writebuf->ptr, writebuf->size)) == 0)
896-
error = proxy_stream->target->close(proxy_stream->target);
895+
if ((error = buffered_stream->target->write(
896+
buffered_stream->target, writebuf->ptr, writebuf->size)) == 0)
897+
error = buffered_stream->target->close(buffered_stream->target);
897898

898899
return error;
899900
}
900901

901-
static void proxy_stream_free(git_writestream *s)
902+
static void buffered_stream_free(git_writestream *s)
902903
{
903-
struct proxy_stream *proxy_stream = (struct proxy_stream *)s;
904+
struct buffered_stream *buffered_stream = (struct buffered_stream *)s;
904905

905-
if (proxy_stream) {
906-
git_buf_dispose(&proxy_stream->input);
907-
git_buf_dispose(&proxy_stream->temp_buf);
908-
git__free(proxy_stream);
906+
if (buffered_stream) {
907+
git_buf_dispose(&buffered_stream->input);
908+
git_buf_dispose(&buffered_stream->temp_buf);
909+
git__free(buffered_stream);
909910
}
910911
}
911912

912-
static int proxy_stream_init(
913+
int git_filter_buffered_stream_new(
913914
git_writestream **out,
914915
git_filter *filter,
916+
int (*write_fn)(git_filter *, void **, git_buf *, const git_buf *, const git_filter_source *),
915917
git_buf *temp_buf,
916918
void **payload,
917919
const git_filter_source *source,
918920
git_writestream *target)
919921
{
920-
struct proxy_stream *proxy_stream = git__calloc(1, sizeof(struct proxy_stream));
921-
GIT_ERROR_CHECK_ALLOC(proxy_stream);
922-
923-
proxy_stream->parent.write = proxy_stream_write;
924-
proxy_stream->parent.close = proxy_stream_close;
925-
proxy_stream->parent.free = proxy_stream_free;
926-
proxy_stream->filter = filter;
927-
proxy_stream->payload = payload;
928-
proxy_stream->source = source;
929-
proxy_stream->target = target;
930-
proxy_stream->output = temp_buf ? temp_buf : &proxy_stream->temp_buf;
922+
struct buffered_stream *buffered_stream = git__calloc(1, sizeof(struct buffered_stream));
923+
GIT_ERROR_CHECK_ALLOC(buffered_stream);
924+
925+
buffered_stream->parent.write = buffered_stream_write;
926+
buffered_stream->parent.close = buffered_stream_close;
927+
buffered_stream->parent.free = buffered_stream_free;
928+
buffered_stream->filter = filter;
929+
buffered_stream->write_fn = write_fn;
930+
buffered_stream->output = temp_buf ? temp_buf : &buffered_stream->temp_buf;
931+
buffered_stream->payload = payload;
932+
buffered_stream->source = source;
933+
buffered_stream->target = target;
931934

932935
if (temp_buf)
933936
git_buf_clear(temp_buf);
934937

935-
*out = (git_writestream *)proxy_stream;
938+
*out = (git_writestream *)buffered_stream;
936939
return 0;
937940
}
938941

942+
static int setup_stream(
943+
git_writestream **out,
944+
git_filter_entry *fe,
945+
git_filter_list *filters,
946+
git_writestream *last_stream)
947+
{
948+
#ifndef GIT_DEPRECATE_HARD
949+
GIT_ASSERT(fe->filter->stream || fe->filter->apply);
950+
951+
/*
952+
* If necessary, create a stream that proxies the traditional
953+
* application.
954+
*/
955+
if (!fe->filter->stream) {
956+
/* Create a stream that proxies the one-shot apply */
957+
return git_filter_buffered_stream_new(out,
958+
fe->filter, fe->filter->apply, filters->temp_buf,
959+
&fe->payload, &filters->source, last_stream);
960+
}
961+
#endif
962+
963+
GIT_ASSERT(fe->filter->stream);
964+
return fe->filter->stream(out, fe->filter,
965+
&fe->payload, &filters->source, last_stream);
966+
}
967+
939968
static int stream_list_init(
940969
git_writestream **out,
941970
git_vector *streams,
@@ -957,22 +986,11 @@ static int stream_list_init(
957986
for (i = 0; i < git_array_size(filters->filters); ++i) {
958987
size_t filter_idx = (filters->source.mode == GIT_FILTER_TO_WORKTREE) ?
959988
git_array_size(filters->filters) - 1 - i : i;
989+
960990
git_filter_entry *fe = git_array_get(filters->filters, filter_idx);
961991
git_writestream *filter_stream;
962992

963-
GIT_ASSERT(fe->filter->stream || fe->filter->apply);
964-
965-
/* If necessary, create a stream that proxies the traditional
966-
* application.
967-
*/
968-
if (fe->filter->stream)
969-
error = fe->filter->stream(&filter_stream, fe->filter,
970-
&fe->payload, &filters->source, last_stream);
971-
else
972-
/* Create a stream that proxies the one-shot apply */
973-
error = proxy_stream_init(&filter_stream, fe->filter,
974-
filters->temp_buf, &fe->payload, &filters->source,
975-
last_stream);
993+
error = setup_stream(&filter_stream, fe, filters, last_stream);
976994

977995
if (error < 0)
978996
goto out;

src/filter.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include "attr_file.h"
1313
#include "git2/filter.h"
14+
#include "git2/sys/filter.h"
1415

1516
/* Amount of file to examine for NUL byte when checking binary-ness */
1617
#define GIT_FILTER_BYTES_TO_CHECK_NUL 8000
@@ -51,4 +52,13 @@ extern int git_filter_list__convert_buf(
5152
extern git_filter *git_crlf_filter_new(void);
5253
extern git_filter *git_ident_filter_new(void);
5354

55+
extern int git_filter_buffered_stream_new(
56+
git_writestream **out,
57+
git_filter *filter,
58+
int (*write_fn)(git_filter *, void **, git_buf *, const git_buf *, const git_filter_source *),
59+
git_buf *temp_buf,
60+
void **payload,
61+
const git_filter_source *source,
62+
git_writestream *target);
63+
5464
#endif

0 commit comments

Comments
 (0)