1 /* 2 * QEMU generic buffers 3 * 4 * Copyright (c) 2015 Red Hat, Inc. 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, see <http://www.gnu.org/licenses/>. 18 * 19 */ 20 21 #include "qemu/buffer.h" 22 #include "trace.h" 23 24 #define BUFFER_MIN_INIT_SIZE 4096 25 #define BUFFER_MIN_SHRINK_SIZE 65536 26 27 static size_t buffer_req_size(Buffer *buffer, size_t len) 28 { 29 return MAX(BUFFER_MIN_INIT_SIZE, 30 pow2ceil(buffer->offset + len)); 31 } 32 33 34 void buffer_init(Buffer *buffer, const char *name, ...) 35 { 36 va_list ap; 37 38 va_start(ap, name); 39 buffer->name = g_strdup_vprintf(name, ap); 40 va_end(ap); 41 } 42 43 void buffer_shrink(Buffer *buffer) 44 { 45 size_t old; 46 47 /* 48 * Only shrink in case the used size is *much* smaller than the 49 * capacity, to avoid bumping up & down the buffers all the time. 50 * realloc() isn't exactly cheap ... 51 */ 52 if (buffer->offset < (buffer->capacity >> 3) && 53 buffer->capacity > BUFFER_MIN_SHRINK_SIZE) { 54 return; 55 } 56 57 old = buffer->capacity; 58 buffer->capacity = pow2ceil(buffer->offset); 59 buffer->capacity = MAX(buffer->capacity, BUFFER_MIN_SHRINK_SIZE); 60 buffer->buffer = g_realloc(buffer->buffer, buffer->capacity); 61 trace_buffer_resize(buffer->name ?: "unnamed", 62 old, buffer->capacity); 63 } 64 65 void buffer_reserve(Buffer *buffer, size_t len) 66 { 67 size_t old; 68 69 if ((buffer->capacity - buffer->offset) < len) { 70 old = buffer->capacity; 71 buffer->capacity = buffer_req_size(buffer, len); 72 buffer->buffer = g_realloc(buffer->buffer, buffer->capacity); 73 trace_buffer_resize(buffer->name ?: "unnamed", 74 old, buffer->capacity); 75 } 76 } 77 78 gboolean buffer_empty(Buffer *buffer) 79 { 80 return buffer->offset == 0; 81 } 82 83 uint8_t *buffer_end(Buffer *buffer) 84 { 85 return buffer->buffer + buffer->offset; 86 } 87 88 void buffer_reset(Buffer *buffer) 89 { 90 buffer->offset = 0; 91 } 92 93 void buffer_free(Buffer *buffer) 94 { 95 trace_buffer_free(buffer->name ?: "unnamed", buffer->capacity); 96 g_free(buffer->buffer); 97 g_free(buffer->name); 98 buffer->offset = 0; 99 buffer->capacity = 0; 100 buffer->buffer = NULL; 101 buffer->name = NULL; 102 } 103 104 void buffer_append(Buffer *buffer, const void *data, size_t len) 105 { 106 memcpy(buffer->buffer + buffer->offset, data, len); 107 buffer->offset += len; 108 } 109 110 void buffer_advance(Buffer *buffer, size_t len) 111 { 112 memmove(buffer->buffer, buffer->buffer + len, 113 (buffer->offset - len)); 114 buffer->offset -= len; 115 } 116 117 void buffer_move_empty(Buffer *to, Buffer *from) 118 { 119 trace_buffer_move_empty(to->name ?: "unnamed", 120 from->offset, 121 from->name ?: "unnamed"); 122 assert(to->offset == 0); 123 124 g_free(to->buffer); 125 to->offset = from->offset; 126 to->capacity = from->capacity; 127 to->buffer = from->buffer; 128 129 from->offset = 0; 130 from->capacity = 0; 131 from->buffer = NULL; 132 } 133 134 void buffer_move(Buffer *to, Buffer *from) 135 { 136 if (to->offset == 0) { 137 buffer_move_empty(to, from); 138 return; 139 } 140 141 trace_buffer_move(to->name ?: "unnamed", 142 from->offset, 143 from->name ?: "unnamed"); 144 buffer_reserve(to, from->offset); 145 buffer_append(to, from->buffer, from->offset); 146 147 g_free(from->buffer); 148 from->offset = 0; 149 from->capacity = 0; 150 from->buffer = NULL; 151 } 152