Unverified Commit b16a36e1 by Edward Thomson Committed by GitHub

Merge pull request #6011 from libgit2/ethomson/filter_apply

filter: filter drivers stop taking git_buf as user input
parents 258115db 5bcef522
...@@ -167,17 +167,18 @@ typedef void GIT_CALLBACK(git_filter_shutdown_fn)(git_filter *self); ...@@ -167,17 +167,18 @@ typedef void GIT_CALLBACK(git_filter_shutdown_fn)(git_filter *self);
* *
* The `payload` will be a pointer to a reference payload for the filter. * The `payload` will be a pointer to a reference payload for the filter.
* This will start as NULL, but `check` can assign to this pointer for * This will start as NULL, but `check` can assign to this pointer for
* later use by the `apply` callback. Note that the value should be heap * later use by the `stream` callback. Note that the value should be heap
* allocated (not stack), so that it doesn't go away before the `apply` * allocated (not stack), so that it doesn't go away before the `stream`
* callback can use it. If a filter allocates and assigns a value to the * callback can use it. If a filter allocates and assigns a value to the
* `payload`, it will need a `cleanup` callback to free the payload. * `payload`, it will need a `cleanup` callback to free the payload.
*/ */
typedef int GIT_CALLBACK(git_filter_check_fn)( typedef int GIT_CALLBACK(git_filter_check_fn)(
git_filter *self, git_filter *self,
void **payload, /* points to NULL ptr on entry, may be set */ void **payload, /* NULL on entry, may be set */
const git_filter_source *src, const git_filter_source *src,
const char **attr_values); const char **attr_values);
#ifndef GIT_DEPRECATE_HARD
/** /**
* Callback to actually perform the data filtering * Callback to actually perform the data filtering
* *
...@@ -189,6 +190,8 @@ typedef int GIT_CALLBACK(git_filter_check_fn)( ...@@ -189,6 +190,8 @@ typedef int GIT_CALLBACK(git_filter_check_fn)(
* *
* The `payload` value will refer to any payload that was set by the * The `payload` value will refer to any payload that was set by the
* `check` callback. It may be read from or written to as needed. * `check` callback. It may be read from or written to as needed.
*
* @deprecated use git_filter_stream_fn
*/ */
typedef int GIT_CALLBACK(git_filter_apply_fn)( typedef int GIT_CALLBACK(git_filter_apply_fn)(
git_filter *self, git_filter *self,
...@@ -196,7 +199,17 @@ typedef int GIT_CALLBACK(git_filter_apply_fn)( ...@@ -196,7 +199,17 @@ typedef int GIT_CALLBACK(git_filter_apply_fn)(
git_buf *to, git_buf *to,
const git_buf *from, const git_buf *from,
const git_filter_source *src); const git_filter_source *src);
#endif
/**
* Callback to perform the data filtering.
*
* Specified as `filter.stream`, this is a callback that filters data
* in a streaming manner. This function will provide a
* `git_writestream` that will the original data will be written to;
* with that data, the `git_writestream` will then perform the filter
* translation and stream the filtered data out to the `next` location.
*/
typedef int GIT_CALLBACK(git_filter_stream_fn)( typedef int GIT_CALLBACK(git_filter_stream_fn)(
git_writestream **out, git_writestream **out,
git_filter *self, git_filter *self,
...@@ -208,9 +221,10 @@ typedef int GIT_CALLBACK(git_filter_stream_fn)( ...@@ -208,9 +221,10 @@ typedef int GIT_CALLBACK(git_filter_stream_fn)(
* Callback to clean up after filtering has been applied * Callback to clean up after filtering has been applied
* *
* Specified as `filter.cleanup`, this is an optional callback invoked * Specified as `filter.cleanup`, this is an optional callback invoked
* after the filter has been applied. If the `check` or `apply` callbacks * after the filter has been applied. If the `check`, `apply`, or
* allocated a `payload` to keep per-source filter state, use this * `stream` callbacks allocated a `payload` to keep per-source filter
* callback to free that payload and release resources as required. * state, use this callback to free that payload and release resources
* as required.
*/ */
typedef void GIT_CALLBACK(git_filter_cleanup_fn)( typedef void GIT_CALLBACK(git_filter_cleanup_fn)(
git_filter *self, git_filter *self,
...@@ -248,21 +262,28 @@ struct git_filter { ...@@ -248,21 +262,28 @@ struct git_filter {
/** /**
* Called to determine whether the filter should be invoked for a * Called to determine whether the filter should be invoked for a
* given file. If this function returns `GIT_PASSTHROUGH` then the * given file. If this function returns `GIT_PASSTHROUGH` then the
* `apply` function will not be invoked and the contents will be passed * `stream` or `apply` functions will not be invoked and the
* through unmodified. * contents will be passed through unmodified.
*/ */
git_filter_check_fn check; git_filter_check_fn check;
#ifdef GIT_DEPRECATE_HARD
void *reserved;
#else
/** /**
* Called to actually apply the filter to file contents. If this * Provided for backward compatibility; this will apply the
* function returns `GIT_PASSTHROUGH` then the contents will be passed * filter to the given contents in a `git_buf`. Callers should
* through unmodified. * provide a `stream` function instead.
*/ */
git_filter_apply_fn apply; git_filter_apply_fn apply;
#endif
/** /**
* Called to apply the filter in a streaming manner. If this is not * Called to apply the filter, this function will provide a
* specified then the system will call `apply` with the whole buffer. * `git_writestream` that will the original data will be
* written to; with that data, the `git_writestream` will then
* perform the filter translation and stream the filtered data
* out to the `next` location.
*/ */
git_filter_stream_fn stream; git_filter_stream_fn stream;
...@@ -289,9 +310,9 @@ GIT_EXTERN(int) git_filter_init(git_filter *filter, unsigned int version); ...@@ -289,9 +310,9 @@ GIT_EXTERN(int) git_filter_init(git_filter *filter, unsigned int version);
* As mentioned elsewhere, the initialize callback will not be invoked * As mentioned elsewhere, the initialize callback will not be invoked
* immediately. It is deferred until the filter is used in some way. * immediately. It is deferred until the filter is used in some way.
* *
* A filter's attribute checks and `check` and `apply` callbacks will be * A filter's attribute checks and `check` and `stream` (or `apply`)
* issued in order of `priority` on smudge (to workdir), and in reverse * callbacks will be issued in order of `priority` on smudge (to
* order of `priority` on clean (to odb). * workdir), and in reverse order of `priority` on clean (to odb).
* *
* Two filters are preregistered with libgit2: * Two filters are preregistered with libgit2:
* - GIT_FILTER_CRLF with priority 0 * - GIT_FILTER_CRLF with priority 0
......
...@@ -386,6 +386,17 @@ static int crlf_apply( ...@@ -386,6 +386,17 @@ static int crlf_apply(
return crlf_apply_to_odb(*payload, to, from, src); return crlf_apply_to_odb(*payload, to, from, src);
} }
static int crlf_stream(
git_writestream **out,
git_filter *self,
void **payload,
const git_filter_source *src,
git_writestream *next)
{
return git_filter_buffered_stream_new(out,
self, crlf_apply, NULL, payload, src, next);
}
static void crlf_cleanup( static void crlf_cleanup(
git_filter *self, git_filter *self,
void *payload) void *payload)
...@@ -405,7 +416,7 @@ git_filter *git_crlf_filter_new(void) ...@@ -405,7 +416,7 @@ git_filter *git_crlf_filter_new(void)
f->f.initialize = NULL; f->f.initialize = NULL;
f->f.shutdown = git_filter_free; f->f.shutdown = git_filter_free;
f->f.check = crlf_check; f->f.check = crlf_check;
f->f.apply = crlf_apply; f->f.stream = crlf_stream;
f->f.cleanup = crlf_cleanup; f->f.cleanup = crlf_cleanup;
return (git_filter *)f; return (git_filter *)f;
......
...@@ -839,9 +839,10 @@ int git_filter_list_apply_to_blob( ...@@ -839,9 +839,10 @@ int git_filter_list_apply_to_blob(
return error; return error;
} }
struct proxy_stream { struct buffered_stream {
git_writestream parent; git_writestream parent;
git_filter *filter; git_filter *filter;
int (*write_fn)(git_filter *, void **, git_buf *, const git_buf *, const git_filter_source *);
const git_filter_source *source; const git_filter_source *source;
void **payload; void **payload;
git_buf input; git_buf input;
...@@ -850,92 +851,120 @@ struct proxy_stream { ...@@ -850,92 +851,120 @@ struct proxy_stream {
git_writestream *target; git_writestream *target;
}; };
static int proxy_stream_write( static int buffered_stream_write(
git_writestream *s, const char *buffer, size_t len) git_writestream *s, const char *buffer, size_t len)
{ {
struct proxy_stream *proxy_stream = (struct proxy_stream *)s; struct buffered_stream *buffered_stream = (struct buffered_stream *)s;
GIT_ASSERT_ARG(proxy_stream); GIT_ASSERT_ARG(buffered_stream);
return git_buf_put(&proxy_stream->input, buffer, len); return git_buf_put(&buffered_stream->input, buffer, len);
} }
static int proxy_stream_close(git_writestream *s) static int buffered_stream_close(git_writestream *s)
{ {
struct proxy_stream *proxy_stream = (struct proxy_stream *)s; struct buffered_stream *buffered_stream = (struct buffered_stream *)s;
git_buf *writebuf; git_buf *writebuf;
git_error_state error_state = {0}; git_error_state error_state = {0};
int error; int error;
GIT_ASSERT_ARG(proxy_stream); GIT_ASSERT_ARG(buffered_stream);
error = proxy_stream->filter->apply( error = buffered_stream->write_fn(
proxy_stream->filter, buffered_stream->filter,
proxy_stream->payload, buffered_stream->payload,
proxy_stream->output, buffered_stream->output,
&proxy_stream->input, &buffered_stream->input,
proxy_stream->source); buffered_stream->source);
if (error == GIT_PASSTHROUGH) { if (error == GIT_PASSTHROUGH) {
writebuf = &proxy_stream->input; writebuf = &buffered_stream->input;
} else if (error == 0) { } else if (error == 0) {
if ((error = git_buf_sanitize(proxy_stream->output)) < 0) if ((error = git_buf_sanitize(buffered_stream->output)) < 0)
return error; return error;
writebuf = proxy_stream->output; writebuf = buffered_stream->output;
} else { } else {
/* close stream before erroring out taking care /* close stream before erroring out taking care
* to preserve the original error */ * to preserve the original error */
git_error_state_capture(&error_state, error); git_error_state_capture(&error_state, error);
proxy_stream->target->close(proxy_stream->target); buffered_stream->target->close(buffered_stream->target);
git_error_state_restore(&error_state); git_error_state_restore(&error_state);
return error; return error;
} }
if ((error = proxy_stream->target->write( if ((error = buffered_stream->target->write(
proxy_stream->target, writebuf->ptr, writebuf->size)) == 0) buffered_stream->target, writebuf->ptr, writebuf->size)) == 0)
error = proxy_stream->target->close(proxy_stream->target); error = buffered_stream->target->close(buffered_stream->target);
return error; return error;
} }
static void proxy_stream_free(git_writestream *s) static void buffered_stream_free(git_writestream *s)
{ {
struct proxy_stream *proxy_stream = (struct proxy_stream *)s; struct buffered_stream *buffered_stream = (struct buffered_stream *)s;
if (proxy_stream) { if (buffered_stream) {
git_buf_dispose(&proxy_stream->input); git_buf_dispose(&buffered_stream->input);
git_buf_dispose(&proxy_stream->temp_buf); git_buf_dispose(&buffered_stream->temp_buf);
git__free(proxy_stream); git__free(buffered_stream);
} }
} }
static int proxy_stream_init( int git_filter_buffered_stream_new(
git_writestream **out, git_writestream **out,
git_filter *filter, git_filter *filter,
int (*write_fn)(git_filter *, void **, git_buf *, const git_buf *, const git_filter_source *),
git_buf *temp_buf, git_buf *temp_buf,
void **payload, void **payload,
const git_filter_source *source, const git_filter_source *source,
git_writestream *target) git_writestream *target)
{ {
struct proxy_stream *proxy_stream = git__calloc(1, sizeof(struct proxy_stream)); struct buffered_stream *buffered_stream = git__calloc(1, sizeof(struct buffered_stream));
GIT_ERROR_CHECK_ALLOC(proxy_stream); GIT_ERROR_CHECK_ALLOC(buffered_stream);
proxy_stream->parent.write = proxy_stream_write; buffered_stream->parent.write = buffered_stream_write;
proxy_stream->parent.close = proxy_stream_close; buffered_stream->parent.close = buffered_stream_close;
proxy_stream->parent.free = proxy_stream_free; buffered_stream->parent.free = buffered_stream_free;
proxy_stream->filter = filter; buffered_stream->filter = filter;
proxy_stream->payload = payload; buffered_stream->write_fn = write_fn;
proxy_stream->source = source; buffered_stream->output = temp_buf ? temp_buf : &buffered_stream->temp_buf;
proxy_stream->target = target; buffered_stream->payload = payload;
proxy_stream->output = temp_buf ? temp_buf : &proxy_stream->temp_buf; buffered_stream->source = source;
buffered_stream->target = target;
if (temp_buf) if (temp_buf)
git_buf_clear(temp_buf); git_buf_clear(temp_buf);
*out = (git_writestream *)proxy_stream; *out = (git_writestream *)buffered_stream;
return 0; return 0;
} }
static int setup_stream(
git_writestream **out,
git_filter_entry *fe,
git_filter_list *filters,
git_writestream *last_stream)
{
#ifndef GIT_DEPRECATE_HARD
GIT_ASSERT(fe->filter->stream || fe->filter->apply);
/*
* If necessary, create a stream that proxies the traditional
* application.
*/
if (!fe->filter->stream) {
/* Create a stream that proxies the one-shot apply */
return git_filter_buffered_stream_new(out,
fe->filter, fe->filter->apply, filters->temp_buf,
&fe->payload, &filters->source, last_stream);
}
#endif
GIT_ASSERT(fe->filter->stream);
return fe->filter->stream(out, fe->filter,
&fe->payload, &filters->source, last_stream);
}
static int stream_list_init( static int stream_list_init(
git_writestream **out, git_writestream **out,
git_vector *streams, git_vector *streams,
...@@ -957,22 +986,11 @@ static int stream_list_init( ...@@ -957,22 +986,11 @@ static int stream_list_init(
for (i = 0; i < git_array_size(filters->filters); ++i) { for (i = 0; i < git_array_size(filters->filters); ++i) {
size_t filter_idx = (filters->source.mode == GIT_FILTER_TO_WORKTREE) ? size_t filter_idx = (filters->source.mode == GIT_FILTER_TO_WORKTREE) ?
git_array_size(filters->filters) - 1 - i : i; git_array_size(filters->filters) - 1 - i : i;
git_filter_entry *fe = git_array_get(filters->filters, filter_idx); git_filter_entry *fe = git_array_get(filters->filters, filter_idx);
git_writestream *filter_stream; git_writestream *filter_stream;
GIT_ASSERT(fe->filter->stream || fe->filter->apply); error = setup_stream(&filter_stream, fe, filters, last_stream);
/* If necessary, create a stream that proxies the traditional
* application.
*/
if (fe->filter->stream)
error = fe->filter->stream(&filter_stream, fe->filter,
&fe->payload, &filters->source, last_stream);
else
/* Create a stream that proxies the one-shot apply */
error = proxy_stream_init(&filter_stream, fe->filter,
filters->temp_buf, &fe->payload, &filters->source,
last_stream);
if (error < 0) if (error < 0)
goto out; goto out;
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "attr_file.h" #include "attr_file.h"
#include "git2/filter.h" #include "git2/filter.h"
#include "git2/sys/filter.h"
/* Amount of file to examine for NUL byte when checking binary-ness */ /* Amount of file to examine for NUL byte when checking binary-ness */
#define GIT_FILTER_BYTES_TO_CHECK_NUL 8000 #define GIT_FILTER_BYTES_TO_CHECK_NUL 8000
...@@ -51,4 +52,13 @@ extern int git_filter_list__convert_buf( ...@@ -51,4 +52,13 @@ extern int git_filter_list__convert_buf(
extern git_filter *git_crlf_filter_new(void); extern git_filter *git_crlf_filter_new(void);
extern git_filter *git_ident_filter_new(void); extern git_filter *git_ident_filter_new(void);
extern int git_filter_buffered_stream_new(
git_writestream **out,
git_filter *filter,
int (*write_fn)(git_filter *, void **, git_buf *, const git_buf *, const git_filter_source *),
git_buf *temp_buf,
void **payload,
const git_filter_source *source,
git_writestream *target);
#endif #endif
...@@ -113,6 +113,17 @@ static int ident_apply( ...@@ -113,6 +113,17 @@ static int ident_apply(
return ident_remove_id(to, from); return ident_remove_id(to, from);
} }
static int ident_stream(
git_writestream **out,
git_filter *self,
void **payload,
const git_filter_source *src,
git_writestream *next)
{
return git_filter_buffered_stream_new(out,
self, ident_apply, NULL, payload, src, next);
}
git_filter *git_ident_filter_new(void) git_filter *git_ident_filter_new(void)
{ {
git_filter *f = git__calloc(1, sizeof(git_filter)); git_filter *f = git__calloc(1, sizeof(git_filter));
...@@ -122,7 +133,7 @@ git_filter *git_ident_filter_new(void) ...@@ -122,7 +133,7 @@ git_filter *git_ident_filter_new(void)
f->version = GIT_FILTER_VERSION; f->version = GIT_FILTER_VERSION;
f->attributes = "+ident"; /* apply to files with ident attribute set */ f->attributes = "+ident"; /* apply to files with ident attribute set */
f->shutdown = git_filter_free; f->shutdown = git_filter_free;
f->apply = ident_apply; f->stream = ident_stream;
return f; return f;
} }
...@@ -37,6 +37,17 @@ int bitflip_filter_apply( ...@@ -37,6 +37,17 @@ int bitflip_filter_apply(
return 0; return 0;
} }
static int bitflip_filter_stream(
git_writestream **out,
git_filter *self,
void **payload,
const git_filter_source *src,
git_writestream *next)
{
return git_filter_buffered_stream_new(out,
self, bitflip_filter_apply, NULL, payload, src, next);
}
static void bitflip_filter_free(git_filter *f) static void bitflip_filter_free(git_filter *f)
{ {
git__free(f); git__free(f);
...@@ -50,7 +61,7 @@ git_filter *create_bitflip_filter(void) ...@@ -50,7 +61,7 @@ git_filter *create_bitflip_filter(void)
filter->version = GIT_FILTER_VERSION; filter->version = GIT_FILTER_VERSION;
filter->attributes = "+bitflip"; filter->attributes = "+bitflip";
filter->shutdown = bitflip_filter_free; filter->shutdown = bitflip_filter_free;
filter->apply = bitflip_filter_apply; filter->stream = bitflip_filter_stream;
return filter; return filter;
} }
...@@ -88,6 +99,17 @@ int reverse_filter_apply( ...@@ -88,6 +99,17 @@ int reverse_filter_apply(
return 0; return 0;
} }
static int reverse_filter_stream(
git_writestream **out,
git_filter *self,
void **payload,
const git_filter_source *src,
git_writestream *next)
{
return git_filter_buffered_stream_new(out,
self, reverse_filter_apply, NULL, payload, src, next);
}
static void reverse_filter_free(git_filter *f) static void reverse_filter_free(git_filter *f)
{ {
git__free(f); git__free(f);
...@@ -101,7 +123,7 @@ git_filter *create_reverse_filter(const char *attrs) ...@@ -101,7 +123,7 @@ git_filter *create_reverse_filter(const char *attrs)
filter->version = GIT_FILTER_VERSION; filter->version = GIT_FILTER_VERSION;
filter->attributes = attrs; filter->attributes = attrs;
filter->shutdown = reverse_filter_free; filter->shutdown = reverse_filter_free;
filter->apply = reverse_filter_apply; filter->stream = reverse_filter_stream;
return filter; return filter;
} }
......
...@@ -93,6 +93,17 @@ static int wildcard_filter_apply( ...@@ -93,6 +93,17 @@ static int wildcard_filter_apply(
return GIT_PASSTHROUGH; return GIT_PASSTHROUGH;
} }
static int wildcard_filter_stream(
git_writestream **out,
git_filter *self,
void **payload,
const git_filter_source *src,
git_writestream *next)
{
return git_filter_buffered_stream_new(out,
self, wildcard_filter_apply, NULL, payload, src, next);
}
static void wildcard_filter_cleanup(git_filter *self, void *payload) static void wildcard_filter_cleanup(git_filter *self, void *payload)
{ {
GIT_UNUSED(self); GIT_UNUSED(self);
...@@ -112,7 +123,7 @@ static git_filter *create_wildcard_filter(void) ...@@ -112,7 +123,7 @@ static git_filter *create_wildcard_filter(void)
filter->version = GIT_FILTER_VERSION; filter->version = GIT_FILTER_VERSION;
filter->attributes = "filter=*"; filter->attributes = "filter=*";
filter->check = wildcard_filter_check; filter->check = wildcard_filter_check;
filter->apply = wildcard_filter_apply; filter->stream = wildcard_filter_stream;
filter->cleanup = wildcard_filter_cleanup; filter->cleanup = wildcard_filter_cleanup;
filter->shutdown = wildcard_filter_free; filter->shutdown = wildcard_filter_free;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment