]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blob - tools/perf/util/strbuf.c
Merge branch 'nvme-4.13' of git://git.infradead.org/nvme into for-linus
[mirror_ubuntu-artful-kernel.git] / tools / perf / util / strbuf.c
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 }