1/*
2 * Copyright (c) 2015, Juniper Networks, Inc.
3 * All rights reserved.
4 * This SOFTWARE is licensed under the LICENSE provided in the
5 * ../Copyright file. By downloading, installing, copying, or otherwise
6 * using the SOFTWARE, you agree to be bound by the terms of that
7 * LICENSE.
8 * Phil Shafer, August 2015
9 */
10
11/*
12 * This file is an _internal_ part of the libxo plumbing, not suitable
13 * for external use.  It is not considered part of the libxo API and
14 * will not be a stable part of that API.  Mine, not your's, dude...
15 * The real hope is that something like this will become a standard part
16 * of libc and I can kill this off.
17 */
18
19#ifndef XO_BUF_H
20#define XO_BUF_H
21
22#define XO_BUFSIZ		(8*1024) /* Initial buffer size */
23#define XO_BUF_HIGH_WATER	(XO_BUFSIZ - 512) /* When to auto-flush */
24/*
25 * xo_buffer_t: a memory buffer that can be grown as needed.  We
26 * use them for building format strings and output data.
27 */
28typedef struct xo_buffer_s {
29    char *xb_bufp;		/* Buffer memory */
30    char *xb_curp;		/* Current insertion point */
31    ssize_t xb_size;		/* Size of buffer */
32} xo_buffer_t;
33
34/*
35 * Initialize the contents of an xo_buffer_t.
36 */
37static inline void
38xo_buf_init (xo_buffer_t *xbp)
39{
40    xbp->xb_size = XO_BUFSIZ;
41    xbp->xb_bufp = xo_realloc(NULL, xbp->xb_size);
42    xbp->xb_curp = xbp->xb_bufp;
43}
44
45/*
46 * Reset the buffer to empty
47 */
48static inline void
49xo_buf_reset (xo_buffer_t *xbp)
50{
51    xbp->xb_curp = xbp->xb_bufp;
52}
53
54/*
55 * Return the number of bytes left in the buffer
56 */
57static inline int
58xo_buf_left (xo_buffer_t *xbp)
59{
60    return xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
61}
62
63/*
64 * See if the buffer to empty
65 */
66static inline int
67xo_buf_is_empty (xo_buffer_t *xbp)
68{
69    return (xbp->xb_curp == xbp->xb_bufp);
70}
71
72/*
73 * Return the current offset
74 */
75static inline ssize_t
76xo_buf_offset (xo_buffer_t *xbp)
77{
78    return xbp ? (xbp->xb_curp - xbp->xb_bufp) : 0;
79}
80
81static inline char *
82xo_buf_data (xo_buffer_t *xbp, ssize_t offset)
83{
84    if (xbp == NULL)
85	return NULL;
86    return xbp->xb_bufp + offset;
87}
88
89static inline char *
90xo_buf_cur (xo_buffer_t *xbp)
91{
92    if (xbp == NULL)
93	return NULL;
94    return xbp->xb_curp;
95}
96
97/*
98 * Initialize the contents of an xo_buffer_t.
99 */
100static inline void
101xo_buf_cleanup (xo_buffer_t *xbp)
102{
103    if (xbp->xb_bufp)
104	xo_free(xbp->xb_bufp);
105    bzero(xbp, sizeof(*xbp));
106}
107
108/*
109 * Does the buffer have room for the given number of bytes of data?
110 * If not, realloc the buffer to make room.  If that fails, we
111 * return 0 to tell the caller they are in trouble.
112 */
113static inline int
114xo_buf_has_room (xo_buffer_t *xbp, ssize_t len)
115{
116    if (xbp->xb_curp + len >= xbp->xb_bufp + xbp->xb_size) {
117	/*
118	 * Find out how much new space we need, round it up to XO_BUFSIZ
119	 */
120	ssize_t sz = (xbp->xb_curp + len) - xbp->xb_bufp;
121	sz = (sz + XO_BUFSIZ - 1) & ~(XO_BUFSIZ - 1);
122
123	char *bp = xo_realloc(xbp->xb_bufp, sz);
124	if (bp == NULL)
125	    return 0;
126
127	xbp->xb_curp = bp + (xbp->xb_curp - xbp->xb_bufp);
128	xbp->xb_bufp = bp;
129	xbp->xb_size = sz;
130    }
131
132    return 1;
133}
134
135/*
136 * Append the given string to the given buffer
137 */
138static inline void
139xo_buf_append (xo_buffer_t *xbp, const char *str, ssize_t len)
140{
141    if (str == NULL || len == 0 || !xo_buf_has_room(xbp, len))
142	return;
143
144    memcpy(xbp->xb_curp, str, len);
145    xbp->xb_curp += len;
146}
147
148/*
149 * Append the given NUL-terminated string to the given buffer
150 */
151static inline void
152xo_buf_append_str (xo_buffer_t *xbp, const char *str)
153{
154    ssize_t len = strlen(str);
155
156    if (!xo_buf_has_room(xbp, len))
157	return;
158
159    memcpy(xbp->xb_curp, str, len);
160    xbp->xb_curp += len;
161}
162
163#endif /* XO_BUF_H */
164