1/* $NetBSD: vbuf.c,v 1.3 2020/03/18 19:05:22 christos Exp $ */ 2 3/*++ 4/* NAME 5/* vbuf 3 6/* SUMMARY 7/* generic buffer package 8/* SYNOPSIS 9/* #include <vbuf.h> 10/* 11/* int VBUF_GET(bp) 12/* VBUF *bp; 13/* 14/* int VBUF_PUT(bp, ch) 15/* VBUF *bp; 16/* int ch; 17/* 18/* int VBUF_SPACE(bp, len) 19/* VBUF *bp; 20/* ssize_t len; 21/* 22/* int vbuf_unget(bp, ch) 23/* VBUF *bp; 24/* int ch; 25/* 26/* ssize_t vbuf_read(bp, buf, len) 27/* VBUF *bp; 28/* void *buf; 29/* ssize_t len; 30/* 31/* ssize_t vbuf_write(bp, buf, len) 32/* VBUF *bp; 33/* const void *buf; 34/* ssize_t len; 35/* 36/* int vbuf_err(bp) 37/* VBUF *bp; 38/* 39/* int vbuf_eof(bp) 40/* VBUF *bp; 41/* 42/* int vbuf_timeout(bp) 43/* VBUF *bp; 44/* 45/* int vbuf_clearerr(bp) 46/* VBUF *bp; 47/* 48/* int vbuf_rd_err(bp) 49/* VBUF *bp; 50/* 51/* int vbuf_wr_err(bp) 52/* VBUF *bp; 53/* 54/* int vbuf_rd_timeout(bp) 55/* VBUF *bp; 56/* 57/* int vbuf_wr_timeout(bp) 58/* VBUF *bp; 59/* DESCRIPTION 60/* This module implements a buffer with read/write primitives that 61/* automatically handle buffer-empty or buffer-full conditions. 62/* The application is expected to provide callback routines that run 63/* when the read-write primitives detect a buffer-empty/full condition. 64/* 65/* VBUF buffers provide primitives to store and retrieve characters, 66/* and to look up buffer status information. 67/* By design, VBUF buffers provide no explicit primitives for buffer 68/* memory management. This is left to the application to avoid any bias 69/* toward specific management models. The application is free to use 70/* whatever strategy suits best: memory-resident buffer, memory mapped 71/* file, or stdio-like window to an open file. 72/* 73/* VBUF_GET() returns the next character from the specified buffer, 74/* or VBUF_EOF when none is available. VBUF_GET() is an unsafe macro 75/* that evaluates its argument more than once. 76/* 77/* VBUF_PUT() stores one character into the specified buffer. The result 78/* is the stored character, or VBUF_EOF in case of problems. VBUF_PUT() 79/* is an unsafe macro that evaluates its arguments more than once. 80/* 81/* VBUF_SPACE() requests that the requested amount of buffer space be 82/* made available, so that it can be accessed without using VBUF_PUT(). 83/* The result value is 0 for success, VBUF_EOF for problems. 84/* VBUF_SPACE() is an unsafe macro that evaluates its arguments more 85/* than once. VBUF_SPACE() does not support read-only streams. 86/* 87/* vbuf_unget() provides at least one character of pushback, and returns 88/* the pushed back character, or VBUF_EOF in case of problems. It is 89/* an error to call vbuf_unget() on a buffer before reading any data 90/* from it. vbuf_unget() clears the buffer's end-of-file indicator upon 91/* success, and sets the buffer's error indicator when an attempt is 92/* made to push back a non-character value. 93/* 94/* vbuf_read() and vbuf_write() do bulk I/O. The result value is the 95/* number of bytes transferred. A short count is returned in case of 96/* an error. 97/* 98/* vbuf_timeout() is a macro that returns non-zero if a timeout error 99/* condition was detected while reading or writing the buffer. The 100/* error status can be reset by calling vbuf_clearerr(). 101/* 102/* vbuf_err() is a macro that returns non-zero if a non-EOF error 103/* (including timeout) condition was detected while reading or writing 104/* the buffer. The error status can be reset by calling vbuf_clearerr(). 105/* 106/* The vbuf_rd_mumble() and vbuf_wr_mumble() macros report on 107/* read and write error conditions, respectively. 108/* 109/* vbuf_eof() is a macro that returns non-zero if an end-of-file 110/* condition was detected while reading or writing the buffer. The error 111/* status can be reset by calling vbuf_clearerr(). 112/* APPLICATION CALLBACK SYNOPSIS 113/* int get_ready(bp) 114/* VBUF *bp; 115/* 116/* int put_ready(bp) 117/* VBUF *bp; 118/* 119/* int space(bp, len) 120/* VBUF *bp; 121/* ssize_t len; 122/* APPLICATION CALLBACK DESCRIPTION 123/* .ad 124/* .fi 125/* get_ready() is called when VBUF_GET() detects a buffer-empty condition. 126/* The result is zero when more data could be read, VBUF_EOF otherwise. 127/* 128/* put_ready() is called when VBUF_PUT() detects a buffer-full condition. 129/* The result is zero when the buffer could be flushed, VBUF_EOF otherwise. 130/* 131/* space() performs whatever magic necessary to make at least \fIlen\fR 132/* bytes available for access without using VBUF_PUT(). The result is 0 133/* in case of success, VBUF_EOF otherwise. 134/* SEE ALSO 135/* vbuf(3h) layout of the VBUF data structure. 136/* LICENSE 137/* .ad 138/* .fi 139/* The Secure Mailer license must be distributed with this software. 140/* AUTHOR(S) 141/* Wietse Venema 142/* IBM T.J. Watson Research 143/* P.O. Box 704 144/* Yorktown Heights, NY 10598, USA 145/* 146/* Wietse Venema 147/* Google, Inc. 148/* 111 8th Avenue 149/* New York, NY 10011, USA 150/*--*/ 151 152/* System library. */ 153 154#include "sys_defs.h" 155#include <string.h> 156 157/* Utility library. */ 158 159#include "vbuf.h" 160 161/* vbuf_unget - implement at least one character pushback */ 162 163int vbuf_unget(VBUF *bp, int ch) 164{ 165 if ((ch & 0xff) != ch || -bp->cnt >= bp->len) { 166 bp->flags |= VBUF_FLAG_RD_ERR; /* This error affects reads! */ 167 return (VBUF_EOF); 168 } else { 169 bp->cnt--; 170 bp->flags &= ~VBUF_FLAG_EOF; 171 return (*--bp->ptr = ch); 172 } 173} 174 175/* vbuf_get - handle read buffer empty condition */ 176 177int vbuf_get(VBUF *bp) 178{ 179 return (bp->get_ready(bp) ? 180 ((bp->flags |= VBUF_FLAG_EOF), VBUF_EOF) : VBUF_GET(bp)); 181} 182 183/* vbuf_put - handle write buffer full condition */ 184 185int vbuf_put(VBUF *bp, int ch) 186{ 187 return (bp->put_ready(bp) ? VBUF_EOF : VBUF_PUT(bp, ch)); 188} 189 190/* vbuf_read - bulk read from buffer */ 191 192ssize_t vbuf_read(VBUF *bp, void *buf, ssize_t len) 193{ 194 ssize_t count; 195 void *cp; 196 ssize_t n; 197 198#if 0 199 for (count = 0; count < len; count++) 200 if ((buf[count] = VBUF_GET(bp)) < 0) 201 break; 202 return (count); 203#else 204 for (cp = buf, count = len; count > 0; cp += n, count -= n) { 205 if (bp->cnt >= 0 && bp->get_ready(bp)) 206 break; 207 n = (count < -bp->cnt ? count : -bp->cnt); 208 memcpy(cp, bp->ptr, n); 209 bp->ptr += n; 210 bp->cnt += n; 211 } 212 return (len - count); 213#endif 214} 215 216/* vbuf_write - bulk write to buffer */ 217 218ssize_t vbuf_write(VBUF *bp, const void *buf, ssize_t len) 219{ 220 ssize_t count; 221 const void *cp; 222 ssize_t n; 223 224#if 0 225 for (count = 0; count < len; count++) 226 if (VBUF_PUT(bp, buf[count]) < 0) 227 break; 228 return (count); 229#else 230 for (cp = buf, count = len; count > 0; cp += n, count -= n) { 231 if (bp->cnt <= 0 && bp->put_ready(bp) != 0) 232 break; 233 n = (count < bp->cnt ? count : bp->cnt); 234 memcpy(bp->ptr, cp, n); 235 bp->ptr += n; 236 bp->cnt -= n; 237 } 238 return (len - count); 239#endif 240} 241