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