// SPDX-License-Identifier: GPL-2.0+ /* * Handles a buffer that can be allocated and freed * * Copyright 2021 Google LLC * Written by Simon Glass */ #ifndef USE_HOSTCC #include #include #include #include #endif #include #include #include void abuf_set(struct abuf *abuf, void *data, size_t size) { abuf_uninit(abuf); abuf->data = data; abuf->size = size; } #ifndef USE_HOSTCC void abuf_map_sysmem(struct abuf *abuf, ulong addr, size_t size) { abuf_set(abuf, map_sysmem(addr, size), size); } ulong abuf_addr(const struct abuf *abuf) { return map_to_sysmem(abuf->data); } #else /* copied from lib/string.c for convenience */ static char *memdup(const void *src, size_t len) { char *p; p = malloc(len); if (!p) return NULL; memcpy(p, src, len); return p; } #endif bool abuf_realloc(struct abuf *abuf, size_t new_size) { void *ptr; if (!new_size) { /* easy case, just need to uninit, freeing any allocation */ abuf_uninit(abuf); return true; } else if (abuf->alloced) { /* currently allocated, so need to reallocate */ ptr = realloc(abuf->data, new_size); if (!ptr) return false; abuf->data = ptr; abuf->size = new_size; return true; } else if (new_size <= abuf->size) { /* * not currently alloced and new size is no larger. Just update * it. Data is lost off the end if new_size < abuf->size */ abuf->size = new_size; return true; } else { /* not currently allocated and new size is larger. Alloc and * copy in data. The new space is not inited. */ ptr = malloc(new_size); if (!ptr) return false; if (abuf->size) memcpy(ptr, abuf->data, abuf->size); abuf->data = ptr; abuf->size = new_size; abuf->alloced = true; return true; } } bool abuf_realloc_inc(struct abuf *abuf, size_t inc) { return abuf_realloc(abuf, abuf->size + inc); } void *abuf_uninit_move(struct abuf *abuf, size_t *sizep) { void *ptr; if (sizep) *sizep = abuf->size; if (!abuf->size) return NULL; if (abuf->alloced) { ptr = abuf->data; } else { ptr = memdup(abuf->data, abuf->size); if (!ptr) return NULL; } /* Clear everything out so there is no record of the data */ abuf_init(abuf); return ptr; } void abuf_init_set(struct abuf *abuf, void *data, size_t size) { abuf_init(abuf); abuf_set(abuf, data, size); } bool abuf_init_size(struct abuf *buf, size_t size) { abuf_init(buf); if (!abuf_realloc(buf, size)) return false; return true; } bool abuf_copy(const struct abuf *old, struct abuf *copy) { char *data; data = malloc(old->size); if (!data) return false; memcpy(data, old->data, old->size); abuf_init_set(copy, data, old->size); copy->alloced = true; return true; } int abuf_printf(struct abuf *buf, const char *fmt, ...) { int maxlen = buf->size; va_list args; int len; va_start(args, fmt); len = vsnprintf(buf->data, buf->size, fmt, args); va_end(args); /* add the terminator */ len++; if (len > 4096) return -E2BIG; if (len > maxlen) { /* make more space and try again */ maxlen = len; if (!abuf_realloc(buf, maxlen)) return -ENOMEM; va_start(args, fmt); len = vsnprintf(buf->data, maxlen, fmt, args); va_end(args); /* check there isn't anything strange going on */ if (len > maxlen) return -EFAULT; } return len; } void abuf_init_const(struct abuf *abuf, const void *data, size_t size) { /* for now there is no flag indicating that the abuf data is constant */ abuf_init_set(abuf, (void *)data, size); } void abuf_init_move(struct abuf *abuf, void *data, size_t size) { abuf_init_set(abuf, data, size); abuf->alloced = true; } void abuf_uninit(struct abuf *abuf) { if (abuf->alloced) free(abuf->data); abuf_init(abuf); } void abuf_init(struct abuf *abuf) { abuf->data = NULL; abuf->size = 0; abuf->alloced = false; }