Browse Source
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>maint
David Barr
15 years ago
committed by
Junio C Hamano
7 changed files with 274 additions and 2 deletions
@ -0,0 +1,46 @@ |
|||||||
|
/* |
||||||
|
* test-line-buffer.c: code to exercise the svn importer's input helper |
||||||
|
* |
||||||
|
* Input format: |
||||||
|
* number NL |
||||||
|
* (number bytes) NL |
||||||
|
* number NL |
||||||
|
* ... |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "git-compat-util.h" |
||||||
|
#include "vcs-svn/line_buffer.h" |
||||||
|
|
||||||
|
static uint32_t strtouint32(const char *s) |
||||||
|
{ |
||||||
|
char *end; |
||||||
|
uintmax_t n = strtoumax(s, &end, 10); |
||||||
|
if (*s == '\0' || *end != '\0') |
||||||
|
die("invalid count: %s", s); |
||||||
|
return (uint32_t) n; |
||||||
|
} |
||||||
|
|
||||||
|
int main(int argc, char *argv[]) |
||||||
|
{ |
||||||
|
char *s; |
||||||
|
|
||||||
|
if (argc != 1) |
||||||
|
usage("test-line-buffer < input.txt"); |
||||||
|
if (buffer_init(NULL)) |
||||||
|
die_errno("open error"); |
||||||
|
while ((s = buffer_read_line())) { |
||||||
|
s = buffer_read_string(strtouint32(s)); |
||||||
|
fputs(s, stdout); |
||||||
|
fputc('\n', stdout); |
||||||
|
buffer_skip_bytes(1); |
||||||
|
if (!(s = buffer_read_line())) |
||||||
|
break; |
||||||
|
buffer_copy_bytes(strtouint32(s) + 1); |
||||||
|
} |
||||||
|
if (buffer_deinit()) |
||||||
|
die("input error"); |
||||||
|
if (ferror(stdout)) |
||||||
|
die("output error"); |
||||||
|
buffer_reset(); |
||||||
|
return 0; |
||||||
|
} |
@ -0,0 +1,97 @@ |
|||||||
|
/* |
||||||
|
* Licensed under a two-clause BSD-style license. |
||||||
|
* See LICENSE for details. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "git-compat-util.h" |
||||||
|
#include "line_buffer.h" |
||||||
|
#include "obj_pool.h" |
||||||
|
|
||||||
|
#define LINE_BUFFER_LEN 10000 |
||||||
|
#define COPY_BUFFER_LEN 4096 |
||||||
|
|
||||||
|
/* Create memory pool for char sequence of known length */ |
||||||
|
obj_pool_gen(blob, char, 4096) |
||||||
|
|
||||||
|
static char line_buffer[LINE_BUFFER_LEN]; |
||||||
|
static char byte_buffer[COPY_BUFFER_LEN]; |
||||||
|
static FILE *infile; |
||||||
|
|
||||||
|
int buffer_init(const char *filename) |
||||||
|
{ |
||||||
|
infile = filename ? fopen(filename, "r") : stdin; |
||||||
|
if (!infile) |
||||||
|
return -1; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
int buffer_deinit(void) |
||||||
|
{ |
||||||
|
int err; |
||||||
|
if (infile == stdin) |
||||||
|
return ferror(infile); |
||||||
|
err = ferror(infile); |
||||||
|
err |= fclose(infile); |
||||||
|
return err; |
||||||
|
} |
||||||
|
|
||||||
|
/* Read a line without trailing newline. */ |
||||||
|
char *buffer_read_line(void) |
||||||
|
{ |
||||||
|
char *end; |
||||||
|
if (!fgets(line_buffer, sizeof(line_buffer), infile)) |
||||||
|
/* Error or data exhausted. */ |
||||||
|
return NULL; |
||||||
|
end = line_buffer + strlen(line_buffer); |
||||||
|
if (end[-1] == '\n') |
||||||
|
end[-1] = '\0'; |
||||||
|
else if (feof(infile)) |
||||||
|
; /* No newline at end of file. That's fine. */ |
||||||
|
else |
||||||
|
/* |
||||||
|
* Line was too long. |
||||||
|
* There is probably a saner way to deal with this, |
||||||
|
* but for now let's return an error. |
||||||
|
*/ |
||||||
|
return NULL; |
||||||
|
return line_buffer; |
||||||
|
} |
||||||
|
|
||||||
|
char *buffer_read_string(uint32_t len) |
||||||
|
{ |
||||||
|
char *s; |
||||||
|
blob_free(blob_pool.size); |
||||||
|
s = blob_pointer(blob_alloc(len + 1)); |
||||||
|
s[fread(s, 1, len, infile)] = '\0'; |
||||||
|
return ferror(infile) ? NULL : s; |
||||||
|
} |
||||||
|
|
||||||
|
void buffer_copy_bytes(uint32_t len) |
||||||
|
{ |
||||||
|
uint32_t in; |
||||||
|
while (len > 0 && !feof(infile) && !ferror(infile)) { |
||||||
|
in = len < COPY_BUFFER_LEN ? len : COPY_BUFFER_LEN; |
||||||
|
in = fread(byte_buffer, 1, in, infile); |
||||||
|
len -= in; |
||||||
|
fwrite(byte_buffer, 1, in, stdout); |
||||||
|
if (ferror(stdout)) { |
||||||
|
buffer_skip_bytes(len); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void buffer_skip_bytes(uint32_t len) |
||||||
|
{ |
||||||
|
uint32_t in; |
||||||
|
while (len > 0 && !feof(infile) && !ferror(infile)) { |
||||||
|
in = len < COPY_BUFFER_LEN ? len : COPY_BUFFER_LEN; |
||||||
|
in = fread(byte_buffer, 1, in, infile); |
||||||
|
len -= in; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void buffer_reset(void) |
||||||
|
{ |
||||||
|
blob_reset(); |
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
#ifndef LINE_BUFFER_H_ |
||||||
|
#define LINE_BUFFER_H_ |
||||||
|
|
||||||
|
int buffer_init(const char *filename); |
||||||
|
int buffer_deinit(void); |
||||||
|
char *buffer_read_line(void); |
||||||
|
char *buffer_read_string(uint32_t len); |
||||||
|
void buffer_copy_bytes(uint32_t len); |
||||||
|
void buffer_skip_bytes(uint32_t len); |
||||||
|
void buffer_reset(void); |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,58 @@ |
|||||||
|
line_buffer API |
||||||
|
=============== |
||||||
|
|
||||||
|
The line_buffer library provides a convenient interface for |
||||||
|
mostly-line-oriented input. |
||||||
|
|
||||||
|
Each line is not permitted to exceed 10000 bytes. The provided |
||||||
|
functions are not thread-safe or async-signal-safe, and like |
||||||
|
`fgets()`, they generally do not function correctly if interrupted |
||||||
|
by a signal without SA_RESTART set. |
||||||
|
|
||||||
|
Calling sequence |
||||||
|
---------------- |
||||||
|
|
||||||
|
The calling program: |
||||||
|
|
||||||
|
- specifies a file to read with `buffer_init` |
||||||
|
- processes input with `buffer_read_line`, `buffer_read_string`, |
||||||
|
`buffer_skip_bytes`, and `buffer_copy_bytes` |
||||||
|
- closes the file with `buffer_deinit`, perhaps to start over and |
||||||
|
read another file. |
||||||
|
|
||||||
|
Before exiting, the caller can use `buffer_reset` to deallocate |
||||||
|
resources for the benefit of profiling tools. |
||||||
|
|
||||||
|
Functions |
||||||
|
--------- |
||||||
|
|
||||||
|
`buffer_init`:: |
||||||
|
Open the named file for input. If filename is NULL, |
||||||
|
start reading from stdin. On failure, returns -1 (with |
||||||
|
errno indicating the nature of the failure). |
||||||
|
|
||||||
|
`buffer_deinit`:: |
||||||
|
Stop reading from the current file (closing it unless |
||||||
|
it was stdin). Returns nonzero if `fclose` fails or |
||||||
|
the error indicator was set. |
||||||
|
|
||||||
|
`buffer_read_line`:: |
||||||
|
Read a line and strip off the trailing newline. |
||||||
|
On failure or end of file, returns NULL. |
||||||
|
|
||||||
|
`buffer_read_string`:: |
||||||
|
Read `len` characters of input or up to the end of the |
||||||
|
file, whichever comes first. Returns NULL on error. |
||||||
|
Returns whatever characters were read (possibly "") |
||||||
|
for end of file. |
||||||
|
|
||||||
|
`buffer_copy_bytes`:: |
||||||
|
Read `len` bytes of input and dump them to the standard output |
||||||
|
stream. Returns early for error or end of file. |
||||||
|
|
||||||
|
`buffer_skip_bytes`:: |
||||||
|
Discards `len` bytes from the input stream (stopping early |
||||||
|
if necessary because of an error or eof). |
||||||
|
|
||||||
|
`buffer_reset`:: |
||||||
|
Deallocates non-static buffers. |
Loading…
Reference in new issue