uboot/lib/membuff.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (c) 2015 Google, Inc
   4 * Written by Simon Glass <sjg@chromium.org>
   5 *
   6 * Copyright (c) 1992 Simon Glass
   7 */
   8
   9#include <common.h>
  10#include <errno.h>
  11#include <log.h>
  12#include <malloc.h>
  13#include "membuff.h"
  14
  15void membuff_purge(struct membuff *mb)
  16{
  17        /* set mb->head and mb->tail so the buffers look empty */
  18        mb->head = mb->start;
  19        mb->tail = mb->start;
  20}
  21
  22static int membuff_putrawflex(struct membuff *mb, int maxlen, bool update,
  23                              char ***data, int *offsetp)
  24{
  25        int len;
  26
  27        /* always write to 'mb->head' */
  28        assert(data && offsetp);
  29        *data = &mb->start;
  30        *offsetp = mb->head - mb->start;
  31
  32        /* if there is no buffer, we can do nothing */
  33        if (!mb->start)
  34                return 0;
  35
  36        /*
  37         * if head is ahead of tail, we can write from head until the end of
  38         * the buffer
  39         */
  40        if (mb->head >= mb->tail) {
  41                /* work out how many bytes can fit here */
  42                len = mb->end - mb->head - 1;
  43                if (maxlen >= 0 && len > maxlen)
  44                        len = maxlen;
  45
  46                /* update the head pointer to mark these bytes as written */
  47                if (update)
  48                        mb->head += len;
  49
  50                /*
  51                 * if the tail isn't at start of the buffer, then we can
  52                 * write one more byte right at the end
  53                 */
  54                if ((maxlen < 0 || len < maxlen) && mb->tail != mb->start) {
  55                        len++;
  56                        if (update)
  57                                mb->head = mb->start;
  58                }
  59
  60        /* otherwise now we can write until head almost reaches tail */
  61        } else {
  62                /* work out how many bytes can fit here */
  63                len = mb->tail - mb->head - 1;
  64                if (maxlen >= 0 && len > maxlen)
  65                        len = maxlen;
  66
  67                /* update the head pointer to mark these bytes as written */
  68                if (update)
  69                        mb->head += len;
  70        }
  71
  72        /* return the number of bytes which can be/must be written */
  73        return len;
  74}
  75
  76int membuff_putraw(struct membuff *mb, int maxlen, bool update, char **data)
  77{
  78        char **datap;
  79        int offset;
  80        int size;
  81
  82        size = membuff_putrawflex(mb, maxlen, update, &datap, &offset);
  83        *data = *datap + offset;
  84
  85        return size;
  86}
  87
  88bool membuff_putbyte(struct membuff *mb, int ch)
  89{
  90        char *data;
  91
  92        if (membuff_putraw(mb, 1, true, &data) != 1)
  93                return false;
  94        *data = ch;
  95
  96        return true;
  97}
  98
  99int membuff_getraw(struct membuff *mb, int maxlen, bool update, char **data)
 100{
 101        int len;
 102
 103        /* assume for now there is no data to get */
 104        len = 0;
 105
 106        /*
 107         * in this case head is ahead of tail, so we must return data between
 108         *'tail' and 'head'
 109         */
 110        if (mb->head > mb->tail) {
 111                /* work out the amount of data */
 112                *data = mb->tail;
 113                len = mb->head - mb->tail;
 114
 115                /* check it isn't too much */
 116                if (maxlen >= 0 && len > maxlen)
 117                        len = maxlen;
 118
 119                /* & mark it as read from the buffer */
 120                if (update)
 121                        mb->tail += len;
 122        }
 123
 124        /*
 125         * if head is before tail, then we have data between 'tail' and 'end'
 126         * and some more data between 'start' and 'head'(which we can't
 127         * return this time
 128         */
 129        else if (mb->head < mb->tail) {
 130                /* work out the amount of data */
 131                *data = mb->tail;
 132                len = mb->end - mb->tail;
 133                if (maxlen >= 0 && len > maxlen)
 134                        len = maxlen;
 135                if (update) {
 136                        mb->tail += len;
 137                        if (mb->tail == mb->end)
 138                                mb->tail = mb->start;
 139                }
 140        }
 141
 142        debug("getraw: maxlen=%d, update=%d, head=%d, tail=%d, data=%d, len=%d",
 143              maxlen, update, (int)(mb->head - mb->start),
 144              (int)(mb->tail - mb->start), (int)(*data - mb->start), len);
 145
 146        /* return the number of bytes we found */
 147        return len;
 148}
 149
 150int membuff_getbyte(struct membuff *mb)
 151{
 152        char *data = 0;
 153
 154        return membuff_getraw(mb, 1, true, &data) != 1 ? -1 : *(uint8_t *)data;
 155}
 156
 157int membuff_peekbyte(struct membuff *mb)
 158{
 159        char *data = 0;
 160
 161        return membuff_getraw(mb, 1, false, &data) != 1 ? -1 : *(uint8_t *)data;
 162}
 163
 164int membuff_get(struct membuff *mb, char *buff, int maxlen)
 165{
 166        char *data = 0, *buffptr = buff;
 167        int len = 1, i;
 168
 169        /*
 170         * do this in up to two lots(see GetRaw for why) stopping when there
 171         * is no more data
 172         */
 173        for (i = 0; len && i < 2; i++) {
 174                /* get a pointer to the data available */
 175                len = membuff_getraw(mb, maxlen, true, &data);
 176
 177                /* copy it into the buffer */
 178                memcpy(buffptr, data, len);
 179                buffptr += len;
 180                maxlen -= len;
 181        }
 182
 183        /* return the number of bytes read */
 184        return buffptr - buff;
 185}
 186
 187int membuff_put(struct membuff *mb, const char *buff, int length)
 188{
 189        char *data;
 190        int towrite, i, written;
 191
 192        for (i = written = 0; i < 2; i++) {
 193                /* ask where some data can be written */
 194                towrite = membuff_putraw(mb, length, true, &data);
 195
 196                /* and write it, updating the bytes length */
 197                memcpy(data, buff, towrite);
 198                written += towrite;
 199                buff += towrite;
 200                length -= towrite;
 201        }
 202
 203        /* return the number of bytes written */
 204        return written;
 205}
 206
 207bool membuff_isempty(struct membuff *mb)
 208{
 209        return mb->head == mb->tail;
 210}
 211
 212int membuff_avail(struct membuff *mb)
 213{
 214        struct membuff copy;
 215        int i, avail;
 216        char *data = 0;
 217
 218        /* make a copy of this buffer's control data */
 219        copy = *mb;
 220
 221        /* now read everything out of the copied buffer */
 222        for (i = avail = 0; i < 2; i++)
 223                avail += membuff_getraw(&copy, -1, true, &data);
 224
 225        /* and return how much we read */
 226        return avail;
 227}
 228
 229int membuff_size(struct membuff *mb)
 230{
 231        return mb->end - mb->start;
 232}
 233
 234bool membuff_makecontig(struct membuff *mb)
 235{
 236        int topsize, botsize;
 237
 238        debug("makecontig: head=%d, tail=%d, size=%d",
 239              (int)(mb->head - mb->start), (int)(mb->tail - mb->start),
 240              (int)(mb->end - mb->start));
 241
 242        /*
 243         * first we move anything at the start of the buffer into the correct
 244         * place some way along
 245         */
 246        if (mb->tail > mb->head) {
 247                /*
 248                 * the data is split into two parts, from 0 to ->head and
 249                 * from ->tail to ->end. We move the stuff from 0 to ->head
 250                 * up to make space for the other data before it
 251                 */
 252                topsize = mb->end - mb->tail;
 253                botsize = mb->head - mb->start;
 254
 255                /*
 256                 * must move data at bottom up by 'topsize' bytes - check if
 257                 * there's room
 258                 */
 259                if (mb->head + topsize >= mb->tail)
 260                        return false;
 261                memmove(mb->start + topsize, mb->start, botsize);
 262                debug(" - memmove(%d, %d, %d)", topsize, 0, botsize);
 263
 264        /* nothing at the start, so skip that step */
 265        } else {
 266                topsize = mb->head - mb->tail;
 267                botsize = 0;
 268        }
 269
 270        /* now move data at top down to the bottom */
 271        memcpy(mb->start, mb->tail, topsize);
 272        debug(" - memcpy(%d, %d, %d)", 0, (int)(mb->tail - mb->start), topsize);
 273
 274        /* adjust pointers */
 275        mb->tail = mb->start;
 276        mb->head = mb->start + topsize + botsize;
 277
 278        debug(" - head=%d, tail=%d", (int)(mb->head - mb->start),
 279              (int)(mb->tail - mb->start));
 280
 281        /* all ok */
 282        return true;
 283}
 284
 285int membuff_free(struct membuff *mb)
 286{
 287        return mb->end == mb->start ? 0 :
 288                        (mb->end - mb->start) - 1 - membuff_avail(mb);
 289}
 290
 291int membuff_readline(struct membuff *mb, char *str, int maxlen, int minch)
 292{
 293        int len;  /* number of bytes read (!= string length) */
 294        char *s, *end;
 295        bool ok = false;
 296        char *orig = str;
 297
 298        end = mb->head >= mb->tail ? mb->head : mb->end;
 299        for (len = 0, s = mb->tail; s < end && len < maxlen - 1; str++) {
 300                *str = *s++;
 301                len++;
 302                if (*str == '\n' || *str < minch) {
 303                        ok = true;
 304                        break;
 305                }
 306                if (s == end && mb->tail > mb->head) {
 307                        s = mb->start;
 308                        end = mb->head;
 309                }
 310        }
 311
 312        /* couldn't get the whole string */
 313        if (!ok) {
 314                if (maxlen)
 315                        *orig = '\0';
 316                return 0;
 317        }
 318
 319        /* terminate the string, update the membuff and return success */
 320        *str = '\0';
 321        mb->tail = s == mb->end ? mb->start : s;
 322
 323        return len;
 324}
 325
 326int membuff_extend_by(struct membuff *mb, int by, int max)
 327{
 328        int oldhead, oldtail;
 329        int size, orig;
 330        char *ptr;
 331
 332        /* double the buffer size until it is big enough */
 333        assert(by >= 0);
 334        for (orig = mb->end - mb->start, size = orig; size < orig + by;)
 335                size *= 2;
 336        if (max != -1)
 337                size = min(size, max);
 338        by = size - orig;
 339
 340        /* if we're already at maximum, give up */
 341        if (by <= 0)
 342                return -E2BIG;
 343
 344        oldhead = mb->head - mb->start;
 345        oldtail = mb->tail - mb->start;
 346        ptr = realloc(mb->start, size);
 347        if (!ptr)
 348                return -ENOMEM;
 349        mb->start = ptr;
 350        mb->head = mb->start + oldhead;
 351        mb->tail = mb->start + oldtail;
 352
 353        if (mb->head < mb->tail) {
 354                memmove(mb->tail + by, mb->tail, orig - oldtail);
 355                mb->tail += by;
 356        }
 357        mb->end = mb->start + size;
 358
 359        return 0;
 360}
 361
 362void membuff_init(struct membuff *mb, char *buff, int size)
 363{
 364        mb->start = buff;
 365        mb->end = mb->start + size;
 366        membuff_purge(mb);
 367}
 368
 369int membuff_new(struct membuff *mb, int size)
 370{
 371        mb->start = malloc(size);
 372        if (!mb->start)
 373                return -ENOMEM;
 374
 375        membuff_init(mb, mb->start, size);
 376        return 0;
 377}
 378
 379void membuff_uninit(struct membuff *mb)
 380{
 381        mb->end = NULL;
 382        mb->start = NULL;
 383        membuff_purge(mb);
 384}
 385
 386void membuff_dispose(struct membuff *mb)
 387{
 388        free(&mb->start);
 389        membuff_uninit(mb);
 390}
 391