gcc 15 and C23 force some union trickery on buffer.h :-(

add a few buffer_init*_forread variants to pretend we have type safety
  make sure buffer_init_staticcontents handles flushing attempts
This commit is contained in:
leitner
2025-04-22 09:32:03 +00:00
parent 9bf6db9a02
commit b7b8a0efc1
37 changed files with 325 additions and 64 deletions

View File

@@ -4,6 +4,9 @@
some arm fixes (char is unsigned on arm)
str_diff* included byte.h instead of str.h (Mathias Gumz)
ownership warnings for clang (can diagnose double free if you free after iob_addbuf_free)
gcc 15 and C23 force some union trickery on buffer.h :-(
add a few buffer_init*_forread variants to pretend we have type safety
make sure buffer_init_staticcontents handles flushing attempts
0.34:
be more C99 compliant (Florian Weimer)

View File

@@ -15,33 +15,48 @@
extern "C" {
#endif
/* NOTE: unlike stdio a buffer can always only be for reading OR for
* writing, not both at the same time! Also, there is no linked flushing
* logic (reading from buffer_0 won't implicitly flush buffer_1 first */
typedef struct buffer {
char *x; /* actual buffer space */
size_t p; /* current position */
size_t n; /* current size of string in buffer */
size_t a; /* allocated buffer size */
ssize_t (*op)(); /* use read(2) or write(2) */
union {
ssize_t (*wop)(int fd,const void* buf,size_t len); // for write(2)
ssize_t (*rop)(int fd,void* buf,size_t len); // for read(2)
ssize_t (*wopc)(int fd,const char* buf,size_t len, void* cookie); // write(2) w/ cookie
ssize_t (*ropc)(int fd,char* buf,size_t len, void* cookie); // read(2) w/ cookie
} op;
void* cookie; /* used internally by the to-stralloc buffers, and for buffer chaining */
void (*deinit)(void*); /* called to munmap/free cleanup, with a pointer to the buffer as argument */
int fd; /* passed as first argument to op */
} buffer;
#define BUFFER_INIT(op,fd,buf,len) { (char*)(buf), 0, 0, (len), (op), NULL, NULL, (fd) }
#define BUFFER_INIT_FREE(op,fd,buf,len) { (buf), 0, 0, (len), (op), NULL, buffer_free, (fd) }
#define BUFFER_INIT_READ(op,fd,buf,len) BUFFER_INIT(op,fd,buf,len) /*obsolete*/
#define BUFFER_INIT(func,filedes,buf,len) { .x=(buf), .p=0, .n=0, .a=(len), .op.wop=(func), .cookie=NULL, .deinit=NULL, .fd=(filedes) }
#define BUFFER_INIT_FREE(func,filedes,buf,len) { .x=(buf), .p=0, .n=0, .a=(len), .op.wop=(func), .cookie=NULL, .deinit=buffer_free, .fd=(filedes) }
#define BUFFER_INIT_READ(func,filedes,buf,len) { .x=(buf), .p=0, .n=0, .a=(len), .op.rop=(func), .cookie=NULL, .deinit=NULL, .fd=(filedes) }
#define BUFFER_INSIZE 8192
#define BUFFER_OUTSIZE 8192
/* Initialize a buffer with an existing memory area, which the buffer
* will NOT take ownership of (i.e. won't free the memory when it's done) */
__bufout(4,5)
void buffer_init(buffer* b, ssize_t (*op)(), int fd, char* y, size_t ylen);
void buffer_init(buffer* b, ssize_t (*op)(int fd,const void* buf,size_t l), int fd, char* y, size_t ylen);
__bufout(4,5)
void buffer_init_forread(buffer* b, ssize_t (*op)(int fd,void* buf,size_t l), int fd, char* y, size_t ylen);
/* Initialize a buffer with an existing memory area, which the buffer
* WILL take ownership of (it will call free() on it when it's done) */
__bufout(4,5)
att_free(malloc,4)
void buffer_init_free(buffer* b,ssize_t (*op)(),int fd,char* y,size_t ylen);
void buffer_init_free(buffer* b,ssize_t (*op)(int fd,const void* buf,size_t l),int fd,char* y,size_t ylen);
__bufout(4,5)
att_free(malloc,4)
void buffer_init_free_forread(buffer* b,ssize_t (*op)(int fd,void* buf,size_t l),int fd,char* y,size_t ylen);
/* Call buffer_init with op=read(), return 0.
* If fd==-1, return -1 instead, leaving b untouched. */
@@ -57,7 +72,9 @@ int buffer_init_write(buffer* b, int fd, char* y, size_t ylen);
* buffer_init_free(b, op, fd, thebuffer, ylen) on it.
* Returns 0 on success, -1 on error (setting errno).
* Passing fd==-1 is treated as error (so you can pass open*() for fd). */
int buffer_init_allocbuf(buffer* b, ssize_t (*op)(), int fd, size_t ylen);
int buffer_init_allocbuf(buffer* b, ssize_t (*op)(int fd,const void* buf,size_t l), int fd, size_t ylen);
int buffer_init_allocbuf_forread(buffer* b, ssize_t (*op)(int fd,void* buf,size_t l), int fd, size_t ylen);
/* Call buffer_init_allocbuf with op=read */
int buffer_init_read_allocbuf(buffer* b, int fd, size_t ylen);
@@ -74,9 +91,11 @@ int buffer_init_write_allocbuf(buffer* b, int fd, size_t ylen);
* actually attempt to read from any actual file.
* Does not take ownership. Useful for testing. */
__bufin(2,3)
att_holds(malloc,2)
void buffer_init_staticcontents(buffer* b,char* y,size_t ylen);
__bufin(2,3)
void buffer_init_staticcontents_forread(buffer* b,const char* y,size_t ylen);
/* Same but the buffer takes ownership of the static buffer and frees it
* in buffer_close. */
__bufin(2,3)

View File

@@ -71,7 +71,7 @@ int main() {
/* second test: iobuf with no limit. Otherwise the same. */
struct buffer b;
buffer_init_staticcontents(&b, "fnord\nx", 6); // this will let us read 6 bytes
buffer_init_staticcontents_forread(&b, "fnord\nx", 6); // this will let us read 6 bytes
bs_init_iobuf(&bs, &b);
for (i=0; i<6; ++i) {
buf[i] = bs_get(&bs);
@@ -85,7 +85,7 @@ int main() {
assert(bs_err(&bs));
/* third test: iobuf with limit. Otherwise the same. */
buffer_init_staticcontents(&b, "fnord\nx", 7); // this will let us read 7 bytes
buffer_init_staticcontents_forread(&b, "fnord\nx", 7); // this will let us read 7 bytes
bs_init_iobuf_size(&bs, &b, 6); // but we tell bytestream the limit is 6
for (i=0; i<6; ++i) {
@@ -100,7 +100,7 @@ int main() {
assert(bs_err(&bs));
/* fourth test: iobuf with EOF */
buffer_init_staticcontents(&b, "fnord\nx", 6);
buffer_init_staticcontents_forread(&b, "fnord\nx", 6);
bs_init_iobuf(&bs, &b); // bytestream has no limit but will hit EOF in backing buffer
for (i=0; i<6; ++i) {
buf[i] = bs_get(&bs);

View File

@@ -66,7 +66,7 @@ int main() {
/* second test: iobuf with no limit. Otherwise the same. */
struct buffer b;
buffer_init_staticcontents(&b, "fx", 1);
buffer_init_staticcontents_forread(&b, "fx", 1);
bs_init_iobuf(&bs, &b);
assert(bs_peek(&bs) == 'f');
@@ -78,7 +78,7 @@ int main() {
assert(bs_err(&bs));
/* third test: iobuf with limit. Otherwise the same. */
buffer_init_staticcontents(&b, "fx", 2);
buffer_init_staticcontents_forread(&b, "fx", 2);
bs_init_iobuf_size(&bs, &b, 1);
assert(bs_peek(&bs) == 'f');
@@ -90,7 +90,7 @@ int main() {
assert(bs_err(&bs));
/* fourth test: iobuf with EOF */
buffer_init_staticcontents(&b, "fx", 1);
buffer_init_staticcontents_forread(&b, "fx", 1);
bs_init_iobuf(&bs, &b); // bytestream has no limit but will hit EOF in backing buffer
assert(bs_peek(&bs) == 'f');

View File

@@ -50,6 +50,7 @@ size_t bs_skip(struct bytestream* bs, size_t len) {
#include "buffer/bs_capacityassert.c"
#include "byte/byte_copy.c"
#include "buffer/buffer_init.c"
#include "buffer/buffer_init_read.c"
int main() {
struct bytestream bs;
@@ -71,7 +72,7 @@ int main() {
int fd = open_read("GNUmakefile");
assert(fd != -1);
unsigned char iobuf[42];
buffer b = BUFFER_INIT(read, fd, iobuf+32, 10);
buffer b = BUFFER_INIT_READ(read, fd, iobuf+32, 10);
bs_init_iobuf(&bs, &b);
assert(bs_skip(&bs, 100) == 100);
assert(prs_readblob(&bs, iobuf, 10) == 10);
@@ -91,7 +92,7 @@ int main() {
write(pipes[1], iobuf, 32);
}
}
buffer_init(&b, read, pipes[0], iobuf+32, 10);
buffer_init_read(&b, pipes[0], iobuf+32, 10);
bs_init_iobuf(&bs, &b);
assert(bs_skip(&bs, 20) == 20);

View File

@@ -1,7 +1,7 @@
#include <unistd.h>
#include "buffer.h"
static ssize_t b0read(int fd,char* buf, size_t len) {
static ssize_t b0read(int fd,void* buf, size_t len) {
if (buffer_flush(buffer_1)<0) return -1;
return read(fd,buf,len);
}

View File

@@ -1,7 +1,7 @@
#include <unistd.h>
#include "buffer.h"
static ssize_t b0read(int fd,char* buf, size_t len) {
static ssize_t b0read(int fd,void* buf, size_t len) {
if (buffer_flush(buffer_1small)<0) return -1;
return read(fd,buf,len);
}

View File

@@ -1,11 +1,11 @@
#include "buffer.h"
extern ssize_t buffer_stubborn_read(ssize_t (*op)(),int fd,const char* buf, size_t len,void* cookie);
extern ssize_t buffer_stubborn_read(ssize_t (*op)(int fd,char* buf,size_t l,void* c),int fd,char* buf, size_t len,void* cookie);
ssize_t buffer_feed(buffer* b) {
if (b->p==b->n) {
ssize_t w;
if ((w=buffer_stubborn_read(b->op,b->fd,b->x,b->a,b))<0)
if ((w=buffer_stubborn_read(b->op.ropc,b->fd,b->x,b->a,b))<0)
return -1;
b->n=(size_t)w;
b->p=0;

View File

@@ -1,13 +1,15 @@
#include "buffer.h"
extern ssize_t buffer_stubborn(ssize_t (*op)(),int fd,const char* buf, size_t len,void* cookie);
extern ssize_t buffer_stubborn(ssize_t (*op)(int fd,const char* buf,size_t len,void* cookie),int fd,const char* buf, size_t len,void* cookie);
extern int buffer_flush(buffer* b) {
register size_t p;
register ssize_t r;
if (!(p=b->p)) return 0; /* buffer already empty */
b->p=0;
r=buffer_stubborn(b->op,b->fd,b->x,p,b);
if (r>0) r=0;
r=buffer_stubborn(b->op.wopc,b->fd,b->x,p,b);
if (r>0) {
r=0;
b->p=0;
}
return (int)r;
}

View File

@@ -2,7 +2,10 @@
#include "array.h"
#include "buffer.h"
static ssize_t fail() {
static ssize_t fail(int fd,void* buf,size_t l) {
(void)fd;
(void)buf;
(void)l;
errno=EINVAL;
return -1;
}
@@ -10,7 +13,7 @@ static ssize_t fail() {
void buffer_fromarray(buffer* b,array* a) {
if (array_failed(a)) {
memset(b,0,sizeof *b);
b->op=fail;
b->op.rop=fail;
} else
buffer_frombuf(b,array_start(a),a->initialized);
}

View File

@@ -1,7 +1,7 @@
#include "stralloc.h"
#include "buffer.h"
static ssize_t dummyreadwrite(int fd,char* buf,size_t len) {
static ssize_t dummyreadwrite(int fd,void* buf,size_t len) {
(void)fd;
(void)buf;
(void)len;
@@ -14,6 +14,6 @@ void buffer_frombuf(buffer* b,const char* x,size_t l) {
b->n=l;
b->a=l;
b->fd=0;
b->op=dummyreadwrite;
b->op.rop=dummyreadwrite;
b->deinit=0;
}

View File

@@ -1,3 +1,6 @@
// goddamn glibc!
#define _XOPEN_SOURCE
#include <libowfat/byte.h>
#include <libowfat/buffer.h>

View File

@@ -1,7 +1,5 @@
#include "byte.h"
#include "stralloc.h"
#include "buffer.h"
#include <errno.h>
int buffer_get_token_sa_pred(buffer* b,stralloc* sa,sa_predicate p) {
for (;;) {

View File

@@ -1,4 +1,3 @@
#include "byte.h"
#include "buffer.h"
ssize_t buffer_getc(buffer* b,char* x) {

View File

@@ -1,4 +1,3 @@
#include "byte.h"
#include "buffer.h"
ssize_t buffer_getn(buffer* b,char* x,size_t len) {

View File

@@ -5,7 +5,11 @@ buffer_init \- initialize buffer structure
.B #include <libowfat/buffer.h>
void \fBbuffer_init\fR(buffer &\fIb\fR,
ssize_t (*\fIop\fR)(int,char*,size_t),
ssize_t (*\fIop\fR)(int,const void*,size_t),
int \fIfd\fR, char* \fIy\fR, size_t \fIylen\fR);
void \fBbuffer_init_forread\fR(buffer &\fIb\fR,
ssize_t (*\fIop\fR)(int,void*,size_t),
int \fIfd\fR, char* \fIy\fR, size_t \fIylen\fR);
.SH DESCRIPTION
buffer_init prepares \fIb\fR to store a string in \fIy\fR[0], \fIy\fR[1], ...,
@@ -18,11 +22,25 @@ You can use
buffer \fIb\fR = BUFFER_INIT(\fIop\fR,\fIfd\fR,\fIy\fR,\fIylen\fR);
or
buffer \fIb\fR = BUFFER_INIT_READ(\fIop\fR,\fIfd\fR,\fIy\fR,\fIylen\fR);
to initialize \fIb\fR statically if \fIop\fR, \fIfd\fR, \fIy\fR, and \fIylen\fR
are compile-time constants.
You can call buffer_init again at any time. Note that this discards the
currently buffered string.
The prototypes for \fIop\fR match write(2) and read(2).
If you want to actually pass write(2), use buffer_init_write() instead,
and for read(2) use buffer_init_read().
If you want to hand over ownership of \fIy\fR, so that buffer_close
also calls free() on the associated memory buffer, use
buffer_init_free(3) or buffer_init_free_forread(3).
.SH EXAMPLE
#include <libowfat/buffer.h>
#include <libowfat/open.h>
@@ -42,4 +60,5 @@ currently buffered string.
}
.SH "SEE ALSO"
buffer_flush(3), buffer(3)
buffer_flush(3), buffer(3), buffer_init_read(3), buffer_init_write(3),
buffer_init_free(3)

View File

@@ -1,8 +1,8 @@
#include <libowfat/buffer.h>
void buffer_init(buffer* b,ssize_t (*op)(),int fd,
void buffer_init(buffer* b,ssize_t (*op)(int fd,const void* buf,size_t l),int fd,
char* y,size_t ylen) {
b->op=op;
b->op.wop=op;
b->fd=fd;
b->x=y;
b->a=ylen;
@@ -11,3 +11,8 @@ void buffer_init(buffer* b,ssize_t (*op)(),int fd,
b->cookie=0;
b->deinit=0;
}
void buffer_init_forread(buffer* b,ssize_t (*op)(int fd,void* buf,size_t l),int fd,
char* y,size_t ylen) {
buffer_init(b,(void*)op,fd,y,ylen);
}

View File

@@ -2,7 +2,7 @@
#include <unistd.h>
#include <libowfat/buffer.h>
int buffer_init_allocbuf(buffer* b, ssize_t (*op)(), int fd, size_t ylen) {
int buffer_init_allocbuf(buffer* b, ssize_t (*op)(int fd,const void* buf,size_t l), int fd, size_t ylen) {
if (fd==-1) return -1;
char* thebuffer;
if (!(thebuffer=malloc(ylen)))
@@ -11,3 +11,6 @@ int buffer_init_allocbuf(buffer* b, ssize_t (*op)(), int fd, size_t ylen) {
return 0;
}
int buffer_init_allocbuf_forread(buffer* b, ssize_t (*op)(int fd,void* buf,size_t l), int fd, size_t ylen) {
return buffer_init_allocbuf(b,(void*)op,fd,ylen);
}

View File

@@ -0,0 +1,55 @@
.TH buffer_init_forread 3
.SH NAME
buffer_init_forread \- initialize buffer structure
.SH SYNTAX
.B #include <libowfat/buffer.h>
void \fBbuffer_init_forread\fR(buffer &\fIb\fR,
ssize_t (*\fIop\fR)(int,void*,size_t),
int \fIfd\fR, char* \fIy\fR, size_t \fIylen\fR);
.SH DESCRIPTION
buffer_init prepares \fIb\fR to store a string in \fIy\fR[0], \fIy\fR[1], ...,
\fIy\fR[\fIylen\fR-1]. Initially the string is empty.
buffer_init_forread also prepares \fIb\fR to use the read operation specified by
\fIop\fR and \fIfd\fR.
You can use
buffer \fIb\fR = BUFFER_INIT_READ(\fIop\fR,\fIfd\fR,\fIy\fR,\fIylen\fR);
to initialize \fIb\fR statically if \fIop\fR, \fIfd\fR, \fIy\fR, and \fIylen\fR
are compile-time constants.
You can call buffer_init again at any time. Note that this discards the
currently buffered string.
The prototype for \fIop\fR matches read(2).
If you want to actually pass read(2) as op, use buffer_init_read() instead.
If you want to hand over ownership of \fIy\fR, so that buffer_close
also calls free() on the associated memory buffer, use
buffer_init_free_forread(3).
.SH EXAMPLE
#include <libowfat/buffer.h>
#include <libowfat/open.h>
char buf[4096];
int fd=open_read("/etc/services");
buffer input;
if (fd>=0) {
char x;
buffer_init_forread(&input,read,fd,buf,sizeof buf);
while (buffer_get(&input,&x,1)==1) {
buffer_put(buffer_1,&x,1);
if (x=='\\n') break;
}
buffer_flush(buffer_1);
}
.SH "SEE ALSO"
buffer_flush(3), buffer(3), buffer_init_read(3), buffer_init_write(3),
buffer_init_free(3)

View File

@@ -7,8 +7,15 @@ buffer_init_free \- initialize buffer structure
void \fBbuffer_init_free\fR(buffer &\fIb\fR,
ssize_t (*\fIop\fR)(int,char*,size_t),
int \fIfd\fR, char* \fIy\fR, size_t \fIylen\fR);
void \fBbuffer_init_free_forread\fR(buffer &\fIb\fR,
ssize_t (*\fIop\fR)(int,char*,size_t),
int \fIfd\fR, char* \fIy\fR, size_t \fIylen\fR);
.SH DESCRIPTION
buffer_init_free is like buffer_init except that the memory (\fIy\fR is
marked to be freed by buffer_close().
marked to be freed by buffer_close).
buffer_init_free_forread is like buffer_init_forread except that the memory (\fIy\fR is
marked to be freed by buffer_close).
.SH "SEE ALSO"
buffer_init(3), buffer_mmap_read(3), buffer(3)

View File

@@ -1,7 +1,13 @@
#include <libowfat/buffer.h>
void buffer_init_free(buffer* b,ssize_t (*op)(),int fd,
void buffer_init_free(buffer* b,ssize_t (*op)(int fd,const void* buf,size_t l),int fd,
char* y,size_t ylen) {
buffer_init(b,op,fd,y,ylen);
b->deinit=buffer_free;
}
void buffer_init_free_forread(buffer* b,ssize_t (*op)(int fd,void* buf,size_t l),int fd,
char* y,size_t ylen) {
buffer_init_forread(b,op,fd,y,ylen);
b->deinit=buffer_free;
}

32
buffer/buffer_init_read.3 Normal file
View File

@@ -0,0 +1,32 @@
.TH buffer_init_read 3
.SH NAME
buffer_init_read \- initialize buffer structure
.SH SYNTAX
.B #include <libowfat/buffer.h>
void \fBbuffer_init_read\fR(buffer &\fIb\fR,
int \fIfd\fR, char* \fIy\fR, size_t \fIylen\fR);
.SH DESCRIPTION
buffer_init_read is equivalent to calling buffer_init_forread with read(2) as op.
.SH EXAMPLE
#include <libowfat/buffer.h>
#include <libowfat/open.h>
char buf[4096];
int fd=open_read("/etc/services");
buffer input;
if (fd>=0) {
char x;
buffer_init_read(&input,fd,buf,sizeof buf);
while (buffer_get(&input,&x,1)==1) {
buffer_put(buffer_1,&x,1);
if (x=='\\n') break;
}
buffer_flush(buffer_1);
}
.SH "SEE ALSO"
buffer_init(3), buffer(3), buffer_init_write(3)

View File

@@ -3,7 +3,7 @@
int buffer_init_read(buffer* b, int fd, char* y,size_t ylen) {
if (fd==-1) return -1;
buffer_init(b, read, fd, y, ylen);
buffer_init_forread(b, read, fd, y, ylen);
return 0;
}

View File

@@ -2,6 +2,6 @@
#include <libowfat/buffer.h>
int buffer_init_read_allocbuf(buffer* b, int fd, size_t ylen) {
return buffer_init_allocbuf(b, read, fd, ylen);
return buffer_init_allocbuf_forread(b, read, fd, ylen);
}

View File

@@ -0,0 +1,37 @@
.TH buffer_init_staticcontents 3
.SH NAME
buffer_init_staticcontents \- initialize buffer structure
.SH SYNTAX
.B #include <libowfat/buffer.h>
void \fBbuffer_init_staticcontents\fR(buffer &\fIb\fR,
char* \fIy\fR, size_t \fIylen\fR);
void \fBbuffer_init_staticcontents_forread\fR(buffer &\fIb\fR,
const char* \fIy\fR, size_t \fIylen\fR);
.SH DESCRIPTION
buffer_init_staticcontents sets up a buffer pointing to a fixed buffer.
No file is associated with the buffer. No resizing is done on the
buffer. Writing past capacity will return an error.
buffer_init_staticcontents_forread sets up a buffer for reading,
pointing to a pre-filled fixed buffer.
This is useful for writing unit tests.
.SH EXAMPLE
#include <libowfat/buffer.h>
#include <libowfat/byte.h>
#include <assert.h>
char buf[4096];
buffer output;
char x;
buffer_init_staticcontents(&output,buf,sizeof buf);
buffer_putsflush(&output,"foo\\nbar\\n");
assert(output.p==8 && byte_equal(buf,8,"foo\\nbar\\n"));
.SH "SEE ALSO"
buffer_init(3), buffer(3), buffer_init_write(3)

View File

@@ -1,14 +1,28 @@
#include <libowfat/buffer.h>
#include <errno.h>
static ssize_t op() {
static ssize_t op(int fd,const void* buf,size_t l) {
(void)fd;
(void)buf;
(void)l;
errno=ENOENT;
return 0;
}
void buffer_init_staticcontents(buffer* b, char* y, size_t len) {
b->x=y;
b->p=0; b->a=b->n=len;
b->p=0; b->a=len; b->n=0;
b->fd=-1;
b->op=op;
b->op.wop=op;
b->deinit=0;
}
void buffer_init_staticcontents_forread(buffer* b, const char* y, size_t len) {
b->x=(char*)y;
b->p=0;
b->a=b->n=len;
b->fd=-1;
b->op.wop=op;
b->deinit=0;
}
@@ -26,7 +40,7 @@ void buffer_init_staticcontents(buffer* b, char* y, size_t len) {
int main() {
buffer b;
buffer_init_staticcontents(&b, "fnord", 5);
buffer_init_staticcontents_forread(&b, "fnord", 5);
char tmp[6];
assert(buffer_get(&b, tmp, 6) == 5);
assert(!memcmp(tmp,"fnord",5));

View File

@@ -0,0 +1,32 @@
.TH buffer_init_staticcontents_forread 3
.SH NAME
buffer_init_staticcontents_forread \- initialize buffer structure
.SH SYNTAX
.B #include <libowfat/buffer.h>
void \fBbuffer_init_staticcontents_forread\fR(buffer &\fIb\fR,
const char* \fIy\fR, size_t \fIylen\fR);
.SH DESCRIPTION
buffer_init_staticcontents_forread sets up a buffer for reading,
pointing to a pre-filled fixed buffer.
This is useful for writing unit tests.
.SH EXAMPLE
#include <libowfat/buffer.h>
#include <libowfat/open.h>
char buf[]="foo\\nbar\\n";
buffer input;
char x;
buffer_init_staticcontents_forread(&input,buf,sizeof buf);
while (buffer_get(&input,&x,1)==1) {
buffer_put(buffer_1,&x,1);
if (x=='\\n') break;
}
buffer_flush(buffer_1);
.SH "SEE ALSO"
buffer_init(3), buffer(3), buffer_init_staticcontents(3)

View File

@@ -0,0 +1,14 @@
.TH buffer_init_write 3
.SH NAME
buffer_init_write \- initialize buffer structure
.SH SYNTAX
.B #include <libowfat/buffer.h>
void \fBbuffer_init_write\fR(buffer &\fIb\fR,
int \fIfd\fR, char* \fIy\fR, size_t \fIylen\fR);
.SH DESCRIPTION
buffer_init_read is equivalent to calling buffer_init with write(2) as op.
.SH "SEE ALSO"
buffer_init(3), buffer(3), buffer_init_read(3)

View File

@@ -1,7 +1,10 @@
#include <libowfat/buffer.h>
#include <libowfat/mmap.h>
static ssize_t op() {
static ssize_t op(int fd,void* buf,size_t l) {
(void)fd;
(void)buf;
(void)l;
return 0;
}
@@ -9,7 +12,7 @@ int buffer_mmapread(buffer* b,const char* filename) {
if (!(b->x=(char*)mmap_read(filename,&b->n))) return -1;
b->p=0; b->a=b->n;
b->fd=-1;
b->op=op;
b->op.rop=op;
b->deinit=buffer_munmap;
return 0;
}

View File

@@ -1,4 +1,3 @@
#include "byte.h"
#include "buffer.h"
ssize_t buffer_peekc(buffer* b,char* x) {

View File

@@ -5,7 +5,7 @@
#endif
#include "buffer.h"
extern ssize_t buffer_stubborn(ssize_t (*op)(),int fd,const char* buf, size_t len,void* cookie);
extern ssize_t buffer_stubborn(ssize_t (*op)(int fd,const char* buf,size_t len,void* cookie),int fd,const char* buf, size_t len,void* cookie);
#ifndef __unlikely
#ifdef __GNUC__
@@ -18,7 +18,7 @@ extern ssize_t buffer_stubborn(ssize_t (*op)(),int fd,const char* buf, size_t le
int buffer_put(buffer* b,const char* buf,size_t len) {
if (__unlikely(len>b->a-b->p)) { /* doesn't fit */
#ifndef __MINGW32__
if (b->op==write) {
if (b->op.wop==write) {
/* if it's write, we can substitute writev */
struct iovec v[2];
ssize_t r;
@@ -38,7 +38,7 @@ int buffer_put(buffer* b,const char* buf,size_t len) {
#endif
if (buffer_flush(b)==-1) return -1;
if (len>b->a) {
if (buffer_stubborn(b->op,b->fd,buf,len,b)<0) return -1;
if (buffer_stubborn(b->op.wopc,b->fd,buf,len,b)<0) return -1;
return 0;
}
}

View File

@@ -14,15 +14,25 @@
#endif
#endif
extern ssize_t buffer_stubborn(ssize_t (*op)(),int fd,const char* buf, size_t len,void* cookie);
extern ssize_t buffer_stubborn(ssize_t (*op)(int fd,const char* buf,size_t len,void* cookie),int fd,const char* buf, size_t len,void* cookie);
int buffer_putflush(buffer* b,const char* x,size_t len) {
/* Since we know we are going to flush anyway, let's see if we can
* optimize a bit */
if (!b->p) /* if the buffer is empty, just call buffer_stubborn directly */
return buffer_stubborn(b->op,b->fd,x,len,b);
if (!b->p) { /* if the buffer is empty, just call buffer_stubborn directly */
ssize_t r = buffer_stubborn(b->op.wopc,b->fd,x,len,b);
if (r == -1 && errno==ENOENT) {
// tried to flush on buffer_init_staticcontents, ignore
} else {
if (r <= 0 || (size_t)r == len) return r; // common case
// partial write
x+=r;
len-=r;
// fall through
}
}
#ifndef __MINGW32__
if (b->op==write && len>sizeof(struct iovec)*2) {
if (b->op.wop==write && len>sizeof(struct iovec)*2) {
struct iovec v[2];
ssize_t w;
size_t cl=b->p+len;
@@ -37,11 +47,11 @@ int buffer_putflush(buffer* b,const char* x,size_t len) {
if (__unlikely((size_t)w!=cl)) {
/* partial write. ugh. */
if ((size_t)w<v[0].iov_len) {
if (buffer_stubborn(b->op,b->fd,v[0].iov_base+w,v[0].iov_len-w,b) ||
buffer_stubborn(b->op,b->fd,v[1].iov_base,v[0].iov_len,b)) return -1;
if (buffer_stubborn(b->op.wopc,b->fd,v[0].iov_base+w,v[0].iov_len-w,b) ||
buffer_stubborn(b->op.wopc,b->fd,v[1].iov_base,v[0].iov_len,b)) return -1;
} else {
w-=v[0].iov_len;
return buffer_stubborn(b->op,b->fd,v[1].iov_base+w,v[1].iov_len-w,b);
return buffer_stubborn(b->op.wopc,b->fd,v[1].iov_base+w,v[1].iov_len-w,b);
}
}
b->p=0;

View File

@@ -1,4 +1,3 @@
#include "str.h"
#include "buffer.h"
int buffer_putnlflush(buffer* b) {

View File

@@ -1,4 +1,3 @@
#include "str.h"
#include "buffer.h"
int buffer_putspace(buffer* b) {

View File

@@ -1,7 +1,7 @@
#include <errno.h>
#include "buffer.h"
ssize_t buffer_stubborn_read(ssize_t (*op)(int fd,const char* buf,size_t len,void* cookie),int fd,const char* buf, size_t len,void* cookie) {
ssize_t buffer_stubborn_read(ssize_t (*op)(int fd,char* buf,size_t len,void* cookie),int fd,char* buf, size_t len,void* cookie) {
ssize_t w;
for (;;) {
if ((w=op(fd,buf,len,cookie))<0)

View File

@@ -1,7 +1,7 @@
#include "stralloc.h"
#include "buffer.h"
static ssize_t strallocwrite(int fd,char* buf,size_t len,void* myself) {
static ssize_t strallocwrite(int fd,const char* buf,size_t len,void* myself) {
buffer* b=myself;
stralloc* sa=b->cookie;
sa->len+=len;
@@ -21,7 +21,7 @@ int buffer_tosa(buffer* b,stralloc* sa) {
b->n=0;
b->a=1024;
b->fd=0;
b->op=strallocwrite;
b->op.wopc=strallocwrite;
b->cookie=sa;
b->deinit=0;
return 0;

View File

@@ -81,7 +81,7 @@ int main() {
int fd = open_read("Makefile");
assert(fd!=-1);
buffer b = BUFFER_INIT(read, fd, buf+32, 10);
buffer b = BUFFER_INIT_READ(read, fd, buf+32, 10);
bs_init_iobuf_size(&bs, &b, 100);
assert(prs_readblob(&bs, buf, 20) == 20); // make sure we can read a 20 byte blob at once even if the iobuf buffer size is smaller
// strace will show two read calls here