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