xref: /openbmc/linux/tools/perf/util/strbuf.c (revision 6aa7de05)
1 #include "debug.h"
2 #include "util.h"
3 #include <linux/kernel.h>
4 #include <errno.h>
5 
6 /*
7  * Used as the default ->buf value, so that people can always assume
8  * buf is non NULL and ->buf is NUL terminated even for a freshly
9  * initialized strbuf.
10  */
11 char strbuf_slopbuf[1];
12 
13 int strbuf_init(struct strbuf *sb, ssize_t hint)
14 {
15 	sb->alloc = sb->len = 0;
16 	sb->buf = strbuf_slopbuf;
17 	if (hint)
18 		return strbuf_grow(sb, hint);
19 	return 0;
20 }
21 
22 void strbuf_release(struct strbuf *sb)
23 {
24 	if (sb->alloc) {
25 		zfree(&sb->buf);
26 		strbuf_init(sb, 0);
27 	}
28 }
29 
30 char *strbuf_detach(struct strbuf *sb, size_t *sz)
31 {
32 	char *res = sb->alloc ? sb->buf : NULL;
33 	if (sz)
34 		*sz = sb->len;
35 	strbuf_init(sb, 0);
36 	return res;
37 }
38 
39 int strbuf_grow(struct strbuf *sb, size_t extra)
40 {
41 	char *buf;
42 	size_t nr = sb->len + extra + 1;
43 
44 	if (nr < sb->alloc)
45 		return 0;
46 
47 	if (nr <= sb->len)
48 		return -E2BIG;
49 
50 	if (alloc_nr(sb->alloc) > nr)
51 		nr = alloc_nr(sb->alloc);
52 
53 	/*
54 	 * Note that sb->buf == strbuf_slopbuf if sb->alloc == 0, and it is
55 	 * a static variable. Thus we have to avoid passing it to realloc.
56 	 */
57 	buf = realloc(sb->alloc ? sb->buf : NULL, nr * sizeof(*buf));
58 	if (!buf)
59 		return -ENOMEM;
60 
61 	sb->buf = buf;
62 	sb->alloc = nr;
63 	return 0;
64 }
65 
66 int strbuf_addch(struct strbuf *sb, int c)
67 {
68 	int ret = strbuf_grow(sb, 1);
69 	if (ret)
70 		return ret;
71 
72 	sb->buf[sb->len++] = c;
73 	sb->buf[sb->len] = '\0';
74 	return 0;
75 }
76 
77 int strbuf_add(struct strbuf *sb, const void *data, size_t len)
78 {
79 	int ret = strbuf_grow(sb, len);
80 	if (ret)
81 		return ret;
82 
83 	memcpy(sb->buf + sb->len, data, len);
84 	return strbuf_setlen(sb, sb->len + len);
85 }
86 
87 static int strbuf_addv(struct strbuf *sb, const char *fmt, va_list ap)
88 {
89 	int len, ret;
90 	va_list ap_saved;
91 
92 	if (!strbuf_avail(sb)) {
93 		ret = strbuf_grow(sb, 64);
94 		if (ret)
95 			return ret;
96 	}
97 
98 	va_copy(ap_saved, ap);
99 	len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);
100 	if (len < 0)
101 		return len;
102 	if (len > strbuf_avail(sb)) {
103 		ret = strbuf_grow(sb, len);
104 		if (ret)
105 			return ret;
106 		len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap_saved);
107 		va_end(ap_saved);
108 		if (len > strbuf_avail(sb)) {
109 			pr_debug("this should not happen, your vsnprintf is broken");
110 			return -EINVAL;
111 		}
112 	}
113 	return strbuf_setlen(sb, sb->len + len);
114 }
115 
116 int strbuf_addf(struct strbuf *sb, const char *fmt, ...)
117 {
118 	va_list ap;
119 	int ret;
120 
121 	va_start(ap, fmt);
122 	ret = strbuf_addv(sb, fmt, ap);
123 	va_end(ap);
124 	return ret;
125 }
126 
127 ssize_t strbuf_read(struct strbuf *sb, int fd, ssize_t hint)
128 {
129 	size_t oldlen = sb->len;
130 	size_t oldalloc = sb->alloc;
131 	int ret;
132 
133 	ret = strbuf_grow(sb, hint ? hint : 8192);
134 	if (ret)
135 		return ret;
136 
137 	for (;;) {
138 		ssize_t cnt;
139 
140 		cnt = read(fd, sb->buf + sb->len, sb->alloc - sb->len - 1);
141 		if (cnt < 0) {
142 			if (oldalloc == 0)
143 				strbuf_release(sb);
144 			else
145 				strbuf_setlen(sb, oldlen);
146 			return cnt;
147 		}
148 		if (!cnt)
149 			break;
150 		sb->len += cnt;
151 		ret = strbuf_grow(sb, 8192);
152 		if (ret)
153 			return ret;
154 	}
155 
156 	sb->buf[sb->len] = '\0';
157 	return sb->len - oldlen;
158 }
159