X Tutup
Skip to content

Commit 3bbaec0

Browse files
barrbraingitster
authored andcommitted
Add stream helper library
This library provides thread-unsafe fgets()- and fread()-like functions where the caller does not have to supply a buffer. It maintains a couple of static buffers and provides an API to use them. [rr: allow input from files other than stdin] [jn: with tests, documentation, and error handling improvements] Signed-off-by: David Barr <david.barr@cordelta.com> Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com> Signed-off-by: Jonathan Nieder <jrnieder@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 1d73b52 commit 3bbaec0

File tree

7 files changed

+274
-2
lines changed

7 files changed

+274
-2
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@
166166
/test-dump-cache-tree
167167
/test-genrandom
168168
/test-index-version
169+
/test-line-buffer
169170
/test-match-trees
170171
/test-obj-pool
171172
/test-parse-options

Makefile

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,7 @@ TEST_PROGRAMS_NEED_X += test-date
408408
TEST_PROGRAMS_NEED_X += test-delta
409409
TEST_PROGRAMS_NEED_X += test-dump-cache-tree
410410
TEST_PROGRAMS_NEED_X += test-genrandom
411+
TEST_PROGRAMS_NEED_X += test-line-buffer
411412
TEST_PROGRAMS_NEED_X += test-match-trees
412413
TEST_PROGRAMS_NEED_X += test-obj-pool
413414
TEST_PROGRAMS_NEED_X += test-parse-options
@@ -1743,7 +1744,7 @@ ifndef NO_CURL
17431744
endif
17441745
XDIFF_OBJS = xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o \
17451746
xdiff/xmerge.o xdiff/xpatience.o
1746-
VCSSVN_OBJS = vcs-svn/string_pool.o
1747+
VCSSVN_OBJS = vcs-svn/string_pool.o vcs-svn/line_buffer.o
17471748
OBJECTS := $(GIT_OBJS) $(XDIFF_OBJS) $(VCSSVN_OBJS)
17481749

17491750
dep_files := $(foreach f,$(OBJECTS),$(dir $f).depend/$(notdir $f).d)
@@ -1868,7 +1869,8 @@ xdiff-interface.o $(XDIFF_OBJS): \
18681869
xdiff/xutils.h xdiff/xprepare.h xdiff/xdiffi.h xdiff/xemit.h
18691870

18701871
$(VCSSVN_OBJS): \
1871-
vcs-svn/obj_pool.h vcs-svn/trp.h vcs-svn/string_pool.h
1872+
vcs-svn/obj_pool.h vcs-svn/trp.h vcs-svn/string_pool.h \
1873+
vcs-svn/line_buffer.h
18721874
endif
18731875

18741876
exec_cmd.s exec_cmd.o: EXTRA_CPPFLAGS = \
@@ -2017,6 +2019,8 @@ test-date$X: date.o ctype.o
20172019

20182020
test-delta$X: diff-delta.o patch-delta.o
20192021

2022+
test-line-buffer$X: vcs-svn/lib.a
2023+
20202024
test-parse-options$X: parse-options.o
20212025

20222026
test-string-pool$X: vcs-svn/lib.a

t/t0080-vcs-svn.sh

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,60 @@ test_expect_success 'obj pool: high-water mark' '
7676
test_cmp expected actual
7777
'
7878

79+
test_expect_success 'line buffer' '
80+
echo HELLO >expected1 &&
81+
printf "%s\n" "" HELLO >expected2 &&
82+
echo >expected3 &&
83+
printf "%s\n" "" Q | q_to_nul >expected4 &&
84+
printf "%s\n" foo "" >expected5 &&
85+
printf "%s\n" "" foo >expected6 &&
86+
87+
test-line-buffer <<-\EOF >actual1 &&
88+
5
89+
HELLO
90+
EOF
91+
92+
test-line-buffer <<-\EOF >actual2 &&
93+
0
94+
95+
5
96+
HELLO
97+
EOF
98+
99+
q_to_nul <<-\EOF |
100+
1
101+
Q
102+
EOF
103+
test-line-buffer >actual3 &&
104+
105+
q_to_nul <<-\EOF |
106+
0
107+
108+
1
109+
Q
110+
EOF
111+
test-line-buffer >actual4 &&
112+
113+
test-line-buffer <<-\EOF >actual5 &&
114+
5
115+
foo
116+
EOF
117+
118+
test-line-buffer <<-\EOF >actual6 &&
119+
0
120+
121+
5
122+
foo
123+
EOF
124+
125+
test_cmp expected1 actual1 &&
126+
test_cmp expected2 actual2 &&
127+
test_cmp expected3 actual3 &&
128+
test_cmp expected4 actual4 &&
129+
test_cmp expected5 actual5 &&
130+
test_cmp expected6 actual6
131+
'
132+
79133
test_expect_success 'string pool' '
80134
echo a does not equal b >expected.differ &&
81135
echo a equals a >expected.match &&

test-line-buffer.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* test-line-buffer.c: code to exercise the svn importer's input helper
3+
*
4+
* Input format:
5+
* number NL
6+
* (number bytes) NL
7+
* number NL
8+
* ...
9+
*/
10+
11+
#include "git-compat-util.h"
12+
#include "vcs-svn/line_buffer.h"
13+
14+
static uint32_t strtouint32(const char *s)
15+
{
16+
char *end;
17+
uintmax_t n = strtoumax(s, &end, 10);
18+
if (*s == '\0' || *end != '\0')
19+
die("invalid count: %s", s);
20+
return (uint32_t) n;
21+
}
22+
23+
int main(int argc, char *argv[])
24+
{
25+
char *s;
26+
27+
if (argc != 1)
28+
usage("test-line-buffer < input.txt");
29+
if (buffer_init(NULL))
30+
die_errno("open error");
31+
while ((s = buffer_read_line())) {
32+
s = buffer_read_string(strtouint32(s));
33+
fputs(s, stdout);
34+
fputc('\n', stdout);
35+
buffer_skip_bytes(1);
36+
if (!(s = buffer_read_line()))
37+
break;
38+
buffer_copy_bytes(strtouint32(s) + 1);
39+
}
40+
if (buffer_deinit())
41+
die("input error");
42+
if (ferror(stdout))
43+
die("output error");
44+
buffer_reset();
45+
return 0;
46+
}

vcs-svn/line_buffer.c

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* Licensed under a two-clause BSD-style license.
3+
* See LICENSE for details.
4+
*/
5+
6+
#include "git-compat-util.h"
7+
#include "line_buffer.h"
8+
#include "obj_pool.h"
9+
10+
#define LINE_BUFFER_LEN 10000
11+
#define COPY_BUFFER_LEN 4096
12+
13+
/* Create memory pool for char sequence of known length */
14+
obj_pool_gen(blob, char, 4096)
15+
16+
static char line_buffer[LINE_BUFFER_LEN];
17+
static char byte_buffer[COPY_BUFFER_LEN];
18+
static FILE *infile;
19+
20+
int buffer_init(const char *filename)
21+
{
22+
infile = filename ? fopen(filename, "r") : stdin;
23+
if (!infile)
24+
return -1;
25+
return 0;
26+
}
27+
28+
int buffer_deinit(void)
29+
{
30+
int err;
31+
if (infile == stdin)
32+
return ferror(infile);
33+
err = ferror(infile);
34+
err |= fclose(infile);
35+
return err;
36+
}
37+
38+
/* Read a line without trailing newline. */
39+
char *buffer_read_line(void)
40+
{
41+
char *end;
42+
if (!fgets(line_buffer, sizeof(line_buffer), infile))
43+
/* Error or data exhausted. */
44+
return NULL;
45+
end = line_buffer + strlen(line_buffer);
46+
if (end[-1] == '\n')
47+
end[-1] = '\0';
48+
else if (feof(infile))
49+
; /* No newline at end of file. That's fine. */
50+
else
51+
/*
52+
* Line was too long.
53+
* There is probably a saner way to deal with this,
54+
* but for now let's return an error.
55+
*/
56+
return NULL;
57+
return line_buffer;
58+
}
59+
60+
char *buffer_read_string(uint32_t len)
61+
{
62+
char *s;
63+
blob_free(blob_pool.size);
64+
s = blob_pointer(blob_alloc(len + 1));
65+
s[fread(s, 1, len, infile)] = '\0';
66+
return ferror(infile) ? NULL : s;
67+
}
68+
69+
void buffer_copy_bytes(uint32_t len)
70+
{
71+
uint32_t in;
72+
while (len > 0 && !feof(infile) && !ferror(infile)) {
73+
in = len < COPY_BUFFER_LEN ? len : COPY_BUFFER_LEN;
74+
in = fread(byte_buffer, 1, in, infile);
75+
len -= in;
76+
fwrite(byte_buffer, 1, in, stdout);
77+
if (ferror(stdout)) {
78+
buffer_skip_bytes(len);
79+
return;
80+
}
81+
}
82+
}
83+
84+
void buffer_skip_bytes(uint32_t len)
85+
{
86+
uint32_t in;
87+
while (len > 0 && !feof(infile) && !ferror(infile)) {
88+
in = len < COPY_BUFFER_LEN ? len : COPY_BUFFER_LEN;
89+
in = fread(byte_buffer, 1, in, infile);
90+
len -= in;
91+
}
92+
}
93+
94+
void buffer_reset(void)
95+
{
96+
blob_reset();
97+
}

vcs-svn/line_buffer.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#ifndef LINE_BUFFER_H_
2+
#define LINE_BUFFER_H_
3+
4+
int buffer_init(const char *filename);
5+
int buffer_deinit(void);
6+
char *buffer_read_line(void);
7+
char *buffer_read_string(uint32_t len);
8+
void buffer_copy_bytes(uint32_t len);
9+
void buffer_skip_bytes(uint32_t len);
10+
void buffer_reset(void);
11+
12+
#endif

vcs-svn/line_buffer.txt

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
line_buffer API
2+
===============
3+
4+
The line_buffer library provides a convenient interface for
5+
mostly-line-oriented input.
6+
7+
Each line is not permitted to exceed 10000 bytes. The provided
8+
functions are not thread-safe or async-signal-safe, and like
9+
`fgets()`, they generally do not function correctly if interrupted
10+
by a signal without SA_RESTART set.
11+
12+
Calling sequence
13+
----------------
14+
15+
The calling program:
16+
17+
- specifies a file to read with `buffer_init`
18+
- processes input with `buffer_read_line`, `buffer_read_string`,
19+
`buffer_skip_bytes`, and `buffer_copy_bytes`
20+
- closes the file with `buffer_deinit`, perhaps to start over and
21+
read another file.
22+
23+
Before exiting, the caller can use `buffer_reset` to deallocate
24+
resources for the benefit of profiling tools.
25+
26+
Functions
27+
---------
28+
29+
`buffer_init`::
30+
Open the named file for input. If filename is NULL,
31+
start reading from stdin. On failure, returns -1 (with
32+
errno indicating the nature of the failure).
33+
34+
`buffer_deinit`::
35+
Stop reading from the current file (closing it unless
36+
it was stdin). Returns nonzero if `fclose` fails or
37+
the error indicator was set.
38+
39+
`buffer_read_line`::
40+
Read a line and strip off the trailing newline.
41+
On failure or end of file, returns NULL.
42+
43+
`buffer_read_string`::
44+
Read `len` characters of input or up to the end of the
45+
file, whichever comes first. Returns NULL on error.
46+
Returns whatever characters were read (possibly "")
47+
for end of file.
48+
49+
`buffer_copy_bytes`::
50+
Read `len` bytes of input and dump them to the standard output
51+
stream. Returns early for error or end of file.
52+
53+
`buffer_skip_bytes`::
54+
Discards `len` bytes from the input stream (stopping early
55+
if necessary because of an error or eof).
56+
57+
`buffer_reset`::
58+
Deallocates non-static buffers.

0 commit comments

Comments
 (0)
X Tutup