Skip to content

Commit 60f60b4

Browse files
committed
Merge branch 'jk/argv-array' into maint
* 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
2 parents 7bb07f6 + 5d40a17 commit 60f60b4

File tree

11 files changed

+261
-107
lines changed

11 files changed

+261
-107
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
argv-array API
2+
==============
3+
4+
The argv-array API allows one to dynamically build and store
5+
NULL-terminated lists. An argv-array maintains the invariant that the
6+
`argv` member always points to a non-NULL array, and that the array is
7+
always NULL-terminated at the element pointed to by `argv[argc]`. This
8+
makes the result suitable for passing to functions expecting to receive
9+
argv from main(), or the link:api-run-command.html[run-command API].
10+
11+
The link:api-string-list.html[string-list API] is similar, but cannot be
12+
used for these purposes; instead of storing a straight string pointer,
13+
it contains an item structure with a `util` field that is not compatible
14+
with the traditional argv interface.
15+
16+
Each `argv_array` manages its own memory. Any strings pushed into the
17+
array are duplicated, and all memory is freed by argv_array_clear().
18+
19+
Data Structures
20+
---------------
21+
22+
`struct argv_array`::
23+
24+
A single array. This should be initialized by assignment from
25+
`ARGV_ARRAY_INIT`, or by calling `argv_array_init`. The `argv`
26+
member contains the actual array; the `argc` member contains the
27+
number of elements in the array, not including the terminating
28+
NULL.
29+
30+
Functions
31+
---------
32+
33+
`argv_array_init`::
34+
Initialize an array. This is no different than assigning from
35+
`ARGV_ARRAY_INIT`.
36+
37+
`argv_array_push`::
38+
Push a copy of a string onto the end of the array.
39+
40+
`argv_array_pushf`::
41+
Format a string and push it onto the end of the array. This is a
42+
convenience wrapper combining `strbuf_addf` and `argv_array_push`.
43+
44+
`argv_array_clear`::
45+
Free all memory associated with the array and return it to the
46+
initial, empty state.
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
sha1-array API
2+
==============
3+
4+
The sha1-array API provides storage and manipulation of sets of SHA1
5+
identifiers. The emphasis is on storage and processing efficiency,
6+
making them suitable for large lists. Note that the ordering of items is
7+
not preserved over some operations.
8+
9+
Data Structures
10+
---------------
11+
12+
`struct sha1_array`::
13+
14+
A single array of SHA1 hashes. This should be initialized by
15+
assignment from `SHA1_ARRAY_INIT`. The `sha1` member contains
16+
the actual data. The `nr` member contains the number of items in
17+
the set. The `alloc` and `sorted` members are used internally,
18+
and should not be needed by API callers.
19+
20+
Functions
21+
---------
22+
23+
`sha1_array_append`::
24+
Add an item to the set. The sha1 will be placed at the end of
25+
the array (but note that some operations below may lose this
26+
ordering).
27+
28+
`sha1_array_sort`::
29+
Sort the elements in the array.
30+
31+
`sha1_array_lookup`::
32+
Perform a binary search of the array for a specific sha1.
33+
If found, returns the offset (in number of elements) of the
34+
sha1. If not found, returns a negative integer. If the array is
35+
not sorted, this function has the side effect of sorting it.
36+
37+
`sha1_array_clear`::
38+
Free all memory associated with the array and return it to the
39+
initial, empty state.
40+
41+
`sha1_array_for_each_unique`::
42+
Efficiently iterate over each unique element of the list,
43+
executing the callback function for each one. If the array is
44+
not sorted, this function has the side effect of sorting it.
45+
46+
Examples
47+
--------
48+
49+
-----------------------------------------
50+
void print_callback(const unsigned char sha1[20],
51+
void *data)
52+
{
53+
printf("%s\n", sha1_to_hex(sha1));
54+
}
55+
56+
void some_func(void)
57+
{
58+
struct sha1_array hashes = SHA1_ARRAY_INIT;
59+
unsigned char sha1[20];
60+
61+
/* Read objects into our set */
62+
while (read_object_from_stdin(sha1))
63+
sha1_array_append(&hashes, sha1);
64+
65+
/* Check if some objects are in our set */
66+
while (read_object_from_stdin(sha1)) {
67+
if (sha1_array_lookup(&hashes, sha1) >= 0)
68+
printf("it's in there!\n");
69+
70+
/*
71+
* Print the unique set of objects. We could also have
72+
* avoided adding duplicate objects in the first place,
73+
* but we would end up re-sorting the array repeatedly.
74+
* Instead, this will sort once and then skip duplicates
75+
* in linear time.
76+
*/
77+
sha1_array_for_each_unique(&hashes, print_callback, NULL);
78+
}
79+
-----------------------------------------

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,7 @@ VCSSVN_LIB=vcs-svn/lib.a
505505

506506
LIB_H += advice.h
507507
LIB_H += archive.h
508+
LIB_H += argv-array.h
508509
LIB_H += attr.h
509510
LIB_H += blob.h
510511
LIB_H += builtin.h
@@ -586,6 +587,7 @@ LIB_OBJS += alloc.o
586587
LIB_OBJS += archive.o
587588
LIB_OBJS += archive-tar.o
588589
LIB_OBJS += archive-zip.o
590+
LIB_OBJS += argv-array.o
589591
LIB_OBJS += attr.o
590592
LIB_OBJS += base85.o
591593
LIB_OBJS += bisect.o

argv-array.c

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#include "cache.h"
2+
#include "argv-array.h"
3+
#include "strbuf.h"
4+
5+
static const char *empty_argv_storage = NULL;
6+
const char **empty_argv = &empty_argv_storage;
7+
8+
void argv_array_init(struct argv_array *array)
9+
{
10+
array->argv = empty_argv;
11+
array->argc = 0;
12+
array->alloc = 0;
13+
}
14+
15+
static void argv_array_push_nodup(struct argv_array *array, const char *value)
16+
{
17+
if (array->argv == empty_argv)
18+
array->argv = NULL;
19+
20+
ALLOC_GROW(array->argv, array->argc + 2, array->alloc);
21+
array->argv[array->argc++] = value;
22+
array->argv[array->argc] = NULL;
23+
}
24+
25+
void argv_array_push(struct argv_array *array, const char *value)
26+
{
27+
argv_array_push_nodup(array, xstrdup(value));
28+
}
29+
30+
void argv_array_pushf(struct argv_array *array, const char *fmt, ...)
31+
{
32+
va_list ap;
33+
struct strbuf v = STRBUF_INIT;
34+
35+
va_start(ap, fmt);
36+
strbuf_vaddf(&v, fmt, ap);
37+
va_end(ap);
38+
39+
argv_array_push_nodup(array, strbuf_detach(&v, NULL));
40+
}
41+
42+
void argv_array_clear(struct argv_array *array)
43+
{
44+
if (array->argv != empty_argv) {
45+
int i;
46+
for (i = 0; i < array->argc; i++)
47+
free((char **)array->argv[i]);
48+
free(array->argv);
49+
}
50+
argv_array_init(array);
51+
}

argv-array.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#ifndef ARGV_ARRAY_H
2+
#define ARGV_ARRAY_H
3+
4+
extern const char **empty_argv;
5+
6+
struct argv_array {
7+
const char **argv;
8+
int argc;
9+
int alloc;
10+
};
11+
12+
#define ARGV_ARRAY_INIT { empty_argv, 0, 0 }
13+
14+
void argv_array_init(struct argv_array *);
15+
void argv_array_push(struct argv_array *, const char *);
16+
__attribute__((format (printf,2,3)))
17+
void argv_array_pushf(struct argv_array *, const char *fmt, ...);
18+
void argv_array_clear(struct argv_array *);
19+
20+
#endif /* ARGV_ARRAY_H */

bisect.c

Lines changed: 11 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,13 @@
1010
#include "log-tree.h"
1111
#include "bisect.h"
1212
#include "sha1-array.h"
13+
#include "argv-array.h"
1314

1415
static struct sha1_array good_revs;
1516
static struct sha1_array skipped_revs;
1617

1718
static const unsigned char *current_bad_sha1;
1819

19-
struct argv_array {
20-
const char **argv;
21-
int argv_nr;
22-
int argv_alloc;
23-
};
24-
2520
static const char *argv_checkout[] = {"checkout", "-q", NULL, "--", NULL};
2621
static const char *argv_show_branch[] = {"show-branch", NULL, NULL};
2722
static const char *argv_update_ref[] = {"update-ref", "--no-deref", "BISECT_HEAD", NULL, NULL};
@@ -405,21 +400,6 @@ struct commit_list *find_bisection(struct commit_list *list,
405400
return best;
406401
}
407402

408-
static void argv_array_push(struct argv_array *array, const char *string)
409-
{
410-
ALLOC_GROW(array->argv, array->argv_nr + 1, array->argv_alloc);
411-
array->argv[array->argv_nr++] = string;
412-
}
413-
414-
static void argv_array_push_sha1(struct argv_array *array,
415-
const unsigned char *sha1,
416-
const char *format)
417-
{
418-
struct strbuf buf = STRBUF_INIT;
419-
strbuf_addf(&buf, format, sha1_to_hex(sha1));
420-
argv_array_push(array, strbuf_detach(&buf, NULL));
421-
}
422-
423403
static int register_ref(const char *refname, const unsigned char *sha1,
424404
int flags, void *cb_data)
425405
{
@@ -449,16 +429,10 @@ static void read_bisect_paths(struct argv_array *array)
449429
die_errno("Could not open file '%s'", filename);
450430

451431
while (strbuf_getline(&str, fp, '\n') != EOF) {
452-
char *quoted;
453-
int res;
454-
455432
strbuf_trim(&str);
456-
quoted = strbuf_detach(&str, NULL);
457-
res = sq_dequote_to_argv(quoted, &array->argv,
458-
&array->argv_nr, &array->argv_alloc);
459-
if (res)
433+
if (sq_dequote_to_argv_array(str.buf, array))
460434
die("Badly quoted content in file '%s': %s",
461-
filename, quoted);
435+
filename, str.buf);
462436
}
463437

464438
strbuf_release(&str);
@@ -623,25 +597,25 @@ static void bisect_rev_setup(struct rev_info *revs, const char *prefix,
623597
const char *bad_format, const char *good_format,
624598
int read_paths)
625599
{
626-
struct argv_array rev_argv = { NULL, 0, 0 };
600+
struct argv_array rev_argv = ARGV_ARRAY_INIT;
627601
int i;
628602

629603
init_revisions(revs, prefix);
630604
revs->abbrev = 0;
631605
revs->commit_format = CMIT_FMT_UNSPECIFIED;
632606

633607
/* rev_argv.argv[0] will be ignored by setup_revisions */
634-
argv_array_push(&rev_argv, xstrdup("bisect_rev_setup"));
635-
argv_array_push_sha1(&rev_argv, current_bad_sha1, bad_format);
608+
argv_array_push(&rev_argv, "bisect_rev_setup");
609+
argv_array_pushf(&rev_argv, bad_format, sha1_to_hex(current_bad_sha1));
636610
for (i = 0; i < good_revs.nr; i++)
637-
argv_array_push_sha1(&rev_argv, good_revs.sha1[i],
638-
good_format);
639-
argv_array_push(&rev_argv, xstrdup("--"));
611+
argv_array_pushf(&rev_argv, good_format,
612+
sha1_to_hex(good_revs.sha1[i]));
613+
argv_array_push(&rev_argv, "--");
640614
if (read_paths)
641615
read_bisect_paths(&rev_argv);
642-
argv_array_push(&rev_argv, NULL);
643616

644-
setup_revisions(rev_argv.argv_nr, rev_argv.argv, revs, NULL);
617+
setup_revisions(rev_argv.argc, rev_argv.argv, revs, NULL);
618+
/* XXX leak rev_argv, as "revs" may still be pointing to it */
645619
}
646620

647621
static void bisect_common(struct rev_info *revs)

builtin/checkout.c

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "ll-merge.h"
2020
#include "resolve-undo.h"
2121
#include "submodule.h"
22+
#include "argv-array.h"
2223

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

595-
struct rev_list_args {
596-
int argc;
597-
int alloc;
598-
const char **argv;
599-
};
600-
601-
static void add_one_rev_list_arg(struct rev_list_args *args, const char *s)
602-
{
603-
ALLOC_GROW(args->argv, args->argc + 1, args->alloc);
604-
args->argv[args->argc++] = s;
605-
}
606-
607596
static int add_one_ref_to_rev_list_arg(const char *refname,
608597
const unsigned char *sha1,
609598
int flags,
610599
void *cb_data)
611600
{
612-
add_one_rev_list_arg(cb_data, refname);
601+
argv_array_push(cb_data, refname);
613602
return 0;
614603
}
615604

@@ -689,15 +678,14 @@ static void suggest_reattach(struct commit *commit, struct rev_info *revs)
689678
*/
690679
static void orphaned_commit_warning(struct commit *commit)
691680
{
692-
struct rev_list_args args = { 0, 0, NULL };
681+
struct argv_array args = ARGV_ARRAY_INIT;
693682
struct rev_info revs;
694683

695-
add_one_rev_list_arg(&args, "(internal)");
696-
add_one_rev_list_arg(&args, sha1_to_hex(commit->object.sha1));
697-
add_one_rev_list_arg(&args, "--not");
684+
argv_array_push(&args, "(internal)");
685+
argv_array_push(&args, sha1_to_hex(commit->object.sha1));
686+
argv_array_push(&args, "--not");
698687
for_each_ref(add_one_ref_to_rev_list_arg, &args);
699-
add_one_rev_list_arg(&args, "--");
700-
add_one_rev_list_arg(&args, NULL);
688+
argv_array_push(&args, "--");
701689

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

700+
argv_array_clear(&args);
712701
clear_commit_marks(commit, -1);
713702
for_each_ref(clear_commit_marks_from_one_ref, NULL);
714703
}

0 commit comments

Comments
 (0)