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