Skip to content

Commit

Permalink
Merge branch 'jk/argv-array' into maint
Browse files Browse the repository at this point in the history
* jk/argv-array:
  run_hook: use argv_array API
  checkout: use argv_array API
  bisect: use argv_array API
  quote: provide sq_dequote_to_argv_array
  refactor argv_array into generic code
  quote.h: fix bogus comment
  add sha1_array API docs
  • Loading branch information
gitster committed Oct 26, 2011
2 parents 7bb07f6 + 5d40a17 commit 60f60b4
Show file tree
Hide file tree
Showing 11 changed files with 261 additions and 107 deletions.
46 changes: 46 additions & 0 deletions Documentation/technical/api-argv-array.txt
@@ -0,0 +1,46 @@
argv-array API
==============

The argv-array API allows one to dynamically build and store
NULL-terminated lists. An argv-array maintains the invariant that the
`argv` member always points to a non-NULL array, and that the array is
always NULL-terminated at the element pointed to by `argv[argc]`. This
makes the result suitable for passing to functions expecting to receive
argv from main(), or the link:api-run-command.html[run-command API].

The link:api-string-list.html[string-list API] is similar, but cannot be
used for these purposes; instead of storing a straight string pointer,
it contains an item structure with a `util` field that is not compatible
with the traditional argv interface.

Each `argv_array` manages its own memory. Any strings pushed into the
array are duplicated, and all memory is freed by argv_array_clear().

Data Structures
---------------

`struct argv_array`::

A single array. This should be initialized by assignment from
`ARGV_ARRAY_INIT`, or by calling `argv_array_init`. The `argv`
member contains the actual array; the `argc` member contains the
number of elements in the array, not including the terminating
NULL.

Functions
---------

`argv_array_init`::
Initialize an array. This is no different than assigning from
`ARGV_ARRAY_INIT`.

`argv_array_push`::
Push a copy of a string onto the end of the array.

`argv_array_pushf`::
Format a string and push it onto the end of the array. This is a
convenience wrapper combining `strbuf_addf` and `argv_array_push`.

`argv_array_clear`::
Free all memory associated with the array and return it to the
initial, empty state.
79 changes: 79 additions & 0 deletions Documentation/technical/api-sha1-array.txt
@@ -0,0 +1,79 @@
sha1-array API
==============

The sha1-array API provides storage and manipulation of sets of SHA1
identifiers. The emphasis is on storage and processing efficiency,
making them suitable for large lists. Note that the ordering of items is
not preserved over some operations.

Data Structures
---------------

`struct sha1_array`::

A single array of SHA1 hashes. This should be initialized by
assignment from `SHA1_ARRAY_INIT`. The `sha1` member contains
the actual data. The `nr` member contains the number of items in
the set. The `alloc` and `sorted` members are used internally,
and should not be needed by API callers.

Functions
---------

`sha1_array_append`::
Add an item to the set. The sha1 will be placed at the end of
the array (but note that some operations below may lose this
ordering).

`sha1_array_sort`::
Sort the elements in the array.

`sha1_array_lookup`::
Perform a binary search of the array for a specific sha1.
If found, returns the offset (in number of elements) of the
sha1. If not found, returns a negative integer. If the array is
not sorted, this function has the side effect of sorting it.

`sha1_array_clear`::
Free all memory associated with the array and return it to the
initial, empty state.

`sha1_array_for_each_unique`::
Efficiently iterate over each unique element of the list,
executing the callback function for each one. If the array is
not sorted, this function has the side effect of sorting it.

Examples
--------

-----------------------------------------
void print_callback(const unsigned char sha1[20],
void *data)
{
printf("%s\n", sha1_to_hex(sha1));
}

void some_func(void)
{
struct sha1_array hashes = SHA1_ARRAY_INIT;
unsigned char sha1[20];

/* Read objects into our set */
while (read_object_from_stdin(sha1))
sha1_array_append(&hashes, sha1);

/* Check if some objects are in our set */
while (read_object_from_stdin(sha1)) {
if (sha1_array_lookup(&hashes, sha1) >= 0)
printf("it's in there!\n");

/*
* Print the unique set of objects. We could also have
* avoided adding duplicate objects in the first place,
* but we would end up re-sorting the array repeatedly.
* Instead, this will sort once and then skip duplicates
* in linear time.
*/
sha1_array_for_each_unique(&hashes, print_callback, NULL);
}
-----------------------------------------
2 changes: 2 additions & 0 deletions Makefile
Expand Up @@ -505,6 +505,7 @@ VCSSVN_LIB=vcs-svn/lib.a


LIB_H += advice.h LIB_H += advice.h
LIB_H += archive.h LIB_H += archive.h
LIB_H += argv-array.h
LIB_H += attr.h LIB_H += attr.h
LIB_H += blob.h LIB_H += blob.h
LIB_H += builtin.h LIB_H += builtin.h
Expand Down Expand Up @@ -586,6 +587,7 @@ LIB_OBJS += alloc.o
LIB_OBJS += archive.o LIB_OBJS += archive.o
LIB_OBJS += archive-tar.o LIB_OBJS += archive-tar.o
LIB_OBJS += archive-zip.o LIB_OBJS += archive-zip.o
LIB_OBJS += argv-array.o
LIB_OBJS += attr.o LIB_OBJS += attr.o
LIB_OBJS += base85.o LIB_OBJS += base85.o
LIB_OBJS += bisect.o LIB_OBJS += bisect.o
Expand Down
51 changes: 51 additions & 0 deletions argv-array.c
@@ -0,0 +1,51 @@
#include "cache.h"
#include "argv-array.h"
#include "strbuf.h"

static const char *empty_argv_storage = NULL;
const char **empty_argv = &empty_argv_storage;

void argv_array_init(struct argv_array *array)
{
array->argv = empty_argv;
array->argc = 0;
array->alloc = 0;
}

static void argv_array_push_nodup(struct argv_array *array, const char *value)
{
if (array->argv == empty_argv)
array->argv = NULL;

ALLOC_GROW(array->argv, array->argc + 2, array->alloc);
array->argv[array->argc++] = value;
array->argv[array->argc] = NULL;
}

void argv_array_push(struct argv_array *array, const char *value)
{
argv_array_push_nodup(array, xstrdup(value));
}

void argv_array_pushf(struct argv_array *array, const char *fmt, ...)
{
va_list ap;
struct strbuf v = STRBUF_INIT;

va_start(ap, fmt);
strbuf_vaddf(&v, fmt, ap);
va_end(ap);

argv_array_push_nodup(array, strbuf_detach(&v, NULL));
}

void argv_array_clear(struct argv_array *array)
{
if (array->argv != empty_argv) {
int i;
for (i = 0; i < array->argc; i++)
free((char **)array->argv[i]);
free(array->argv);
}
argv_array_init(array);
}
20 changes: 20 additions & 0 deletions argv-array.h
@@ -0,0 +1,20 @@
#ifndef ARGV_ARRAY_H
#define ARGV_ARRAY_H

extern const char **empty_argv;

struct argv_array {
const char **argv;
int argc;
int alloc;
};

#define ARGV_ARRAY_INIT { empty_argv, 0, 0 }

void argv_array_init(struct argv_array *);
void argv_array_push(struct argv_array *, const char *);
__attribute__((format (printf,2,3)))
void argv_array_pushf(struct argv_array *, const char *fmt, ...);
void argv_array_clear(struct argv_array *);

#endif /* ARGV_ARRAY_H */
48 changes: 11 additions & 37 deletions bisect.c
Expand Up @@ -10,18 +10,13 @@
#include "log-tree.h" #include "log-tree.h"
#include "bisect.h" #include "bisect.h"
#include "sha1-array.h" #include "sha1-array.h"
#include "argv-array.h"


static struct sha1_array good_revs; static struct sha1_array good_revs;
static struct sha1_array skipped_revs; static struct sha1_array skipped_revs;


static const unsigned char *current_bad_sha1; static const unsigned char *current_bad_sha1;


struct argv_array {
const char **argv;
int argv_nr;
int argv_alloc;
};

static const char *argv_checkout[] = {"checkout", "-q", NULL, "--", NULL}; static const char *argv_checkout[] = {"checkout", "-q", NULL, "--", NULL};
static const char *argv_show_branch[] = {"show-branch", NULL, NULL}; static const char *argv_show_branch[] = {"show-branch", NULL, NULL};
static const char *argv_update_ref[] = {"update-ref", "--no-deref", "BISECT_HEAD", NULL, NULL}; static const char *argv_update_ref[] = {"update-ref", "--no-deref", "BISECT_HEAD", NULL, NULL};
Expand Down Expand Up @@ -405,21 +400,6 @@ struct commit_list *find_bisection(struct commit_list *list,
return best; return best;
} }


static void argv_array_push(struct argv_array *array, const char *string)
{
ALLOC_GROW(array->argv, array->argv_nr + 1, array->argv_alloc);
array->argv[array->argv_nr++] = string;
}

static void argv_array_push_sha1(struct argv_array *array,
const unsigned char *sha1,
const char *format)
{
struct strbuf buf = STRBUF_INIT;
strbuf_addf(&buf, format, sha1_to_hex(sha1));
argv_array_push(array, strbuf_detach(&buf, NULL));
}

static int register_ref(const char *refname, const unsigned char *sha1, static int register_ref(const char *refname, const unsigned char *sha1,
int flags, void *cb_data) int flags, void *cb_data)
{ {
Expand Down Expand Up @@ -449,16 +429,10 @@ static void read_bisect_paths(struct argv_array *array)
die_errno("Could not open file '%s'", filename); die_errno("Could not open file '%s'", filename);


while (strbuf_getline(&str, fp, '\n') != EOF) { while (strbuf_getline(&str, fp, '\n') != EOF) {
char *quoted;
int res;

strbuf_trim(&str); strbuf_trim(&str);
quoted = strbuf_detach(&str, NULL); if (sq_dequote_to_argv_array(str.buf, array))
res = sq_dequote_to_argv(quoted, &array->argv,
&array->argv_nr, &array->argv_alloc);
if (res)
die("Badly quoted content in file '%s': %s", die("Badly quoted content in file '%s': %s",
filename, quoted); filename, str.buf);
} }


strbuf_release(&str); strbuf_release(&str);
Expand Down Expand Up @@ -623,25 +597,25 @@ static void bisect_rev_setup(struct rev_info *revs, const char *prefix,
const char *bad_format, const char *good_format, const char *bad_format, const char *good_format,
int read_paths) int read_paths)
{ {
struct argv_array rev_argv = { NULL, 0, 0 }; struct argv_array rev_argv = ARGV_ARRAY_INIT;
int i; int i;


init_revisions(revs, prefix); init_revisions(revs, prefix);
revs->abbrev = 0; revs->abbrev = 0;
revs->commit_format = CMIT_FMT_UNSPECIFIED; revs->commit_format = CMIT_FMT_UNSPECIFIED;


/* rev_argv.argv[0] will be ignored by setup_revisions */ /* rev_argv.argv[0] will be ignored by setup_revisions */
argv_array_push(&rev_argv, xstrdup("bisect_rev_setup")); argv_array_push(&rev_argv, "bisect_rev_setup");
argv_array_push_sha1(&rev_argv, current_bad_sha1, bad_format); argv_array_pushf(&rev_argv, bad_format, sha1_to_hex(current_bad_sha1));
for (i = 0; i < good_revs.nr; i++) for (i = 0; i < good_revs.nr; i++)
argv_array_push_sha1(&rev_argv, good_revs.sha1[i], argv_array_pushf(&rev_argv, good_format,
good_format); sha1_to_hex(good_revs.sha1[i]));
argv_array_push(&rev_argv, xstrdup("--")); argv_array_push(&rev_argv, "--");
if (read_paths) if (read_paths)
read_bisect_paths(&rev_argv); read_bisect_paths(&rev_argv);
argv_array_push(&rev_argv, NULL);


setup_revisions(rev_argv.argv_nr, rev_argv.argv, revs, NULL); setup_revisions(rev_argv.argc, rev_argv.argv, revs, NULL);
/* XXX leak rev_argv, as "revs" may still be pointing to it */
} }


static void bisect_common(struct rev_info *revs) static void bisect_common(struct rev_info *revs)
Expand Down
27 changes: 8 additions & 19 deletions builtin/checkout.c
Expand Up @@ -19,6 +19,7 @@
#include "ll-merge.h" #include "ll-merge.h"
#include "resolve-undo.h" #include "resolve-undo.h"
#include "submodule.h" #include "submodule.h"
#include "argv-array.h"


static const char * const checkout_usage[] = { static const char * const checkout_usage[] = {
"git checkout [options] <branch>", "git checkout [options] <branch>",
Expand Down Expand Up @@ -592,24 +593,12 @@ static void update_refs_for_switch(struct checkout_opts *opts,
report_tracking(new); report_tracking(new);
} }


struct rev_list_args {
int argc;
int alloc;
const char **argv;
};

static void add_one_rev_list_arg(struct rev_list_args *args, const char *s)
{
ALLOC_GROW(args->argv, args->argc + 1, args->alloc);
args->argv[args->argc++] = s;
}

static int add_one_ref_to_rev_list_arg(const char *refname, static int add_one_ref_to_rev_list_arg(const char *refname,
const unsigned char *sha1, const unsigned char *sha1,
int flags, int flags,
void *cb_data) void *cb_data)
{ {
add_one_rev_list_arg(cb_data, refname); argv_array_push(cb_data, refname);
return 0; return 0;
} }


Expand Down Expand Up @@ -689,15 +678,14 @@ static void suggest_reattach(struct commit *commit, struct rev_info *revs)
*/ */
static void orphaned_commit_warning(struct commit *commit) static void orphaned_commit_warning(struct commit *commit)
{ {
struct rev_list_args args = { 0, 0, NULL }; struct argv_array args = ARGV_ARRAY_INIT;
struct rev_info revs; struct rev_info revs;


add_one_rev_list_arg(&args, "(internal)"); argv_array_push(&args, "(internal)");
add_one_rev_list_arg(&args, sha1_to_hex(commit->object.sha1)); argv_array_push(&args, sha1_to_hex(commit->object.sha1));
add_one_rev_list_arg(&args, "--not"); argv_array_push(&args, "--not");
for_each_ref(add_one_ref_to_rev_list_arg, &args); for_each_ref(add_one_ref_to_rev_list_arg, &args);
add_one_rev_list_arg(&args, "--"); argv_array_push(&args, "--");
add_one_rev_list_arg(&args, NULL);


init_revisions(&revs, NULL); init_revisions(&revs, NULL);
if (setup_revisions(args.argc - 1, args.argv, &revs, NULL) != 1) if (setup_revisions(args.argc - 1, args.argv, &revs, NULL) != 1)
Expand All @@ -709,6 +697,7 @@ static void orphaned_commit_warning(struct commit *commit)
else else
describe_detached_head(_("Previous HEAD position was"), commit); describe_detached_head(_("Previous HEAD position was"), commit);


argv_array_clear(&args);
clear_commit_marks(commit, -1); clear_commit_marks(commit, -1);
for_each_ref(clear_commit_marks_from_one_ref, NULL); for_each_ref(clear_commit_marks_from_one_ref, NULL);
} }
Expand Down

0 comments on commit 60f60b4

Please sign in to comment.