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