188c5f205SDaniel P. Berrange /*
288c5f205SDaniel P. Berrange * QEMU generic buffers
388c5f205SDaniel P. Berrange *
488c5f205SDaniel P. Berrange * Copyright (c) 2015 Red Hat, Inc.
588c5f205SDaniel P. Berrange *
688c5f205SDaniel P. Berrange * This library is free software; you can redistribute it and/or
788c5f205SDaniel P. Berrange * modify it under the terms of the GNU Lesser General Public
888c5f205SDaniel P. Berrange * License as published by the Free Software Foundation; either
9*61f3c91aSChetan Pant * version 2.1 of the License, or (at your option) any later version.
1088c5f205SDaniel P. Berrange *
1188c5f205SDaniel P. Berrange * This library is distributed in the hope that it will be useful,
1288c5f205SDaniel P. Berrange * but WITHOUT ANY WARRANTY; without even the implied warranty of
1388c5f205SDaniel P. Berrange * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1488c5f205SDaniel P. Berrange * Lesser General Public License for more details.
1588c5f205SDaniel P. Berrange *
1688c5f205SDaniel P. Berrange * You should have received a copy of the GNU Lesser General Public
1788c5f205SDaniel P. Berrange * License along with this library; if not, see <http://www.gnu.org/licenses/>.
1888c5f205SDaniel P. Berrange *
1988c5f205SDaniel P. Berrange */
2088c5f205SDaniel P. Berrange
21aafd7584SPeter Maydell #include "qemu/osdep.h"
2287776ab7SPaolo Bonzini #include "qemu/host-utils.h"
2388c5f205SDaniel P. Berrange #include "qemu/buffer.h"
24d2b90718SGerd Hoffmann #include "trace.h"
2588c5f205SDaniel P. Berrange
265c10dbb7SPeter Lieven #define BUFFER_MIN_INIT_SIZE 4096
271ff36b5dSGerd Hoffmann #define BUFFER_MIN_SHRINK_SIZE 65536
285c10dbb7SPeter Lieven
29d43eda3dSWei Jiangang /* define the factor alpha for the exponential smoothing
30f14c3d85SPeter Lieven * that is used in the average size calculation. a shift
31f14c3d85SPeter Lieven * of 7 results in an alpha of 1/2^7. */
32f14c3d85SPeter Lieven #define BUFFER_AVG_SIZE_SHIFT 7
33f14c3d85SPeter Lieven
buffer_req_size(Buffer * buffer,size_t len)34fd952433SPeter Lieven static size_t buffer_req_size(Buffer *buffer, size_t len)
35fd952433SPeter Lieven {
36fd952433SPeter Lieven return MAX(BUFFER_MIN_INIT_SIZE,
37fd952433SPeter Lieven pow2ceil(buffer->offset + len));
38fd952433SPeter Lieven }
39fd952433SPeter Lieven
buffer_adj_size(Buffer * buffer,size_t len)404ec5ba15SPeter Lieven static void buffer_adj_size(Buffer *buffer, size_t len)
414ec5ba15SPeter Lieven {
424ec5ba15SPeter Lieven size_t old = buffer->capacity;
434ec5ba15SPeter Lieven buffer->capacity = buffer_req_size(buffer, len);
444ec5ba15SPeter Lieven buffer->buffer = g_realloc(buffer->buffer, buffer->capacity);
454ec5ba15SPeter Lieven trace_buffer_resize(buffer->name ?: "unnamed",
464ec5ba15SPeter Lieven old, buffer->capacity);
47f14c3d85SPeter Lieven
48f14c3d85SPeter Lieven /* make it even harder for the buffer to shrink, reset average size
49d43eda3dSWei Jiangang * to current capacity if it is larger than the average. */
50f14c3d85SPeter Lieven buffer->avg_size = MAX(buffer->avg_size,
51f14c3d85SPeter Lieven buffer->capacity << BUFFER_AVG_SIZE_SHIFT);
524ec5ba15SPeter Lieven }
53fd952433SPeter Lieven
buffer_init(Buffer * buffer,const char * name,...)54810082d1SGerd Hoffmann void buffer_init(Buffer *buffer, const char *name, ...)
55810082d1SGerd Hoffmann {
56810082d1SGerd Hoffmann va_list ap;
57810082d1SGerd Hoffmann
58810082d1SGerd Hoffmann va_start(ap, name);
59810082d1SGerd Hoffmann buffer->name = g_strdup_vprintf(name, ap);
60810082d1SGerd Hoffmann va_end(ap);
61810082d1SGerd Hoffmann }
62810082d1SGerd Hoffmann
buffer_get_avg_size(Buffer * buffer)63f14c3d85SPeter Lieven static uint64_t buffer_get_avg_size(Buffer *buffer)
64f14c3d85SPeter Lieven {
65f14c3d85SPeter Lieven return buffer->avg_size >> BUFFER_AVG_SIZE_SHIFT;
66f14c3d85SPeter Lieven }
67f14c3d85SPeter Lieven
buffer_shrink(Buffer * buffer)681ff36b5dSGerd Hoffmann void buffer_shrink(Buffer *buffer)
691ff36b5dSGerd Hoffmann {
70f14c3d85SPeter Lieven size_t new;
71f14c3d85SPeter Lieven
72f14c3d85SPeter Lieven /* Calculate the average size of the buffer as
73f14c3d85SPeter Lieven * avg_size = avg_size * ( 1 - a ) + required_size * a
74f14c3d85SPeter Lieven * where a is 1 / 2 ^ BUFFER_AVG_SIZE_SHIFT. */
75f14c3d85SPeter Lieven buffer->avg_size *= (1 << BUFFER_AVG_SIZE_SHIFT) - 1;
76f14c3d85SPeter Lieven buffer->avg_size >>= BUFFER_AVG_SIZE_SHIFT;
77f14c3d85SPeter Lieven buffer->avg_size += buffer_req_size(buffer, 0);
78f14c3d85SPeter Lieven
79f14c3d85SPeter Lieven /* And then only shrink if the average size of the buffer is much
80f14c3d85SPeter Lieven * too big, to avoid bumping up & down the buffers all the time.
81f14c3d85SPeter Lieven * realloc() isn't exactly cheap ... */
82f14c3d85SPeter Lieven new = buffer_req_size(buffer, buffer_get_avg_size(buffer));
83f14c3d85SPeter Lieven if (new < buffer->capacity >> 3 &&
84f14c3d85SPeter Lieven new >= BUFFER_MIN_SHRINK_SIZE) {
85f14c3d85SPeter Lieven buffer_adj_size(buffer, buffer_get_avg_size(buffer));
861ff36b5dSGerd Hoffmann }
871ff36b5dSGerd Hoffmann
884ec5ba15SPeter Lieven buffer_adj_size(buffer, 0);
891ff36b5dSGerd Hoffmann }
901ff36b5dSGerd Hoffmann
buffer_reserve(Buffer * buffer,size_t len)9188c5f205SDaniel P. Berrange void buffer_reserve(Buffer *buffer, size_t len)
9288c5f205SDaniel P. Berrange {
9388c5f205SDaniel P. Berrange if ((buffer->capacity - buffer->offset) < len) {
944ec5ba15SPeter Lieven buffer_adj_size(buffer, len);
9588c5f205SDaniel P. Berrange }
9688c5f205SDaniel P. Berrange }
9788c5f205SDaniel P. Berrange
buffer_empty(Buffer * buffer)9888c5f205SDaniel P. Berrange gboolean buffer_empty(Buffer *buffer)
9988c5f205SDaniel P. Berrange {
10088c5f205SDaniel P. Berrange return buffer->offset == 0;
10188c5f205SDaniel P. Berrange }
10288c5f205SDaniel P. Berrange
buffer_end(Buffer * buffer)10388c5f205SDaniel P. Berrange uint8_t *buffer_end(Buffer *buffer)
10488c5f205SDaniel P. Berrange {
10588c5f205SDaniel P. Berrange return buffer->buffer + buffer->offset;
10688c5f205SDaniel P. Berrange }
10788c5f205SDaniel P. Berrange
buffer_reset(Buffer * buffer)10888c5f205SDaniel P. Berrange void buffer_reset(Buffer *buffer)
10988c5f205SDaniel P. Berrange {
11088c5f205SDaniel P. Berrange buffer->offset = 0;
111f14c3d85SPeter Lieven buffer_shrink(buffer);
11288c5f205SDaniel P. Berrange }
11388c5f205SDaniel P. Berrange
buffer_free(Buffer * buffer)11488c5f205SDaniel P. Berrange void buffer_free(Buffer *buffer)
11588c5f205SDaniel P. Berrange {
116d2b90718SGerd Hoffmann trace_buffer_free(buffer->name ?: "unnamed", buffer->capacity);
11788c5f205SDaniel P. Berrange g_free(buffer->buffer);
118810082d1SGerd Hoffmann g_free(buffer->name);
11988c5f205SDaniel P. Berrange buffer->offset = 0;
12088c5f205SDaniel P. Berrange buffer->capacity = 0;
12188c5f205SDaniel P. Berrange buffer->buffer = NULL;
122810082d1SGerd Hoffmann buffer->name = NULL;
12388c5f205SDaniel P. Berrange }
12488c5f205SDaniel P. Berrange
buffer_append(Buffer * buffer,const void * data,size_t len)12588c5f205SDaniel P. Berrange void buffer_append(Buffer *buffer, const void *data, size_t len)
12688c5f205SDaniel P. Berrange {
12788c5f205SDaniel P. Berrange memcpy(buffer->buffer + buffer->offset, data, len);
12888c5f205SDaniel P. Berrange buffer->offset += len;
12988c5f205SDaniel P. Berrange }
13088c5f205SDaniel P. Berrange
buffer_advance(Buffer * buffer,size_t len)13188c5f205SDaniel P. Berrange void buffer_advance(Buffer *buffer, size_t len)
13288c5f205SDaniel P. Berrange {
13388c5f205SDaniel P. Berrange memmove(buffer->buffer, buffer->buffer + len,
13488c5f205SDaniel P. Berrange (buffer->offset - len));
13588c5f205SDaniel P. Berrange buffer->offset -= len;
136f14c3d85SPeter Lieven buffer_shrink(buffer);
13788c5f205SDaniel P. Berrange }
1384d1eb5fdSGerd Hoffmann
buffer_move_empty(Buffer * to,Buffer * from)1394d1eb5fdSGerd Hoffmann void buffer_move_empty(Buffer *to, Buffer *from)
1404d1eb5fdSGerd Hoffmann {
141d2b90718SGerd Hoffmann trace_buffer_move_empty(to->name ?: "unnamed",
142d2b90718SGerd Hoffmann from->offset,
143d2b90718SGerd Hoffmann from->name ?: "unnamed");
1444d1eb5fdSGerd Hoffmann assert(to->offset == 0);
1454d1eb5fdSGerd Hoffmann
1464d1eb5fdSGerd Hoffmann g_free(to->buffer);
1474d1eb5fdSGerd Hoffmann to->offset = from->offset;
1484d1eb5fdSGerd Hoffmann to->capacity = from->capacity;
1494d1eb5fdSGerd Hoffmann to->buffer = from->buffer;
1504d1eb5fdSGerd Hoffmann
1514d1eb5fdSGerd Hoffmann from->offset = 0;
1524d1eb5fdSGerd Hoffmann from->capacity = 0;
1534d1eb5fdSGerd Hoffmann from->buffer = NULL;
1544d1eb5fdSGerd Hoffmann }
155830a9583SGerd Hoffmann
buffer_move(Buffer * to,Buffer * from)156830a9583SGerd Hoffmann void buffer_move(Buffer *to, Buffer *from)
157830a9583SGerd Hoffmann {
158830a9583SGerd Hoffmann if (to->offset == 0) {
159830a9583SGerd Hoffmann buffer_move_empty(to, from);
160830a9583SGerd Hoffmann return;
161830a9583SGerd Hoffmann }
162830a9583SGerd Hoffmann
163d2b90718SGerd Hoffmann trace_buffer_move(to->name ?: "unnamed",
164d2b90718SGerd Hoffmann from->offset,
165d2b90718SGerd Hoffmann from->name ?: "unnamed");
166830a9583SGerd Hoffmann buffer_reserve(to, from->offset);
167830a9583SGerd Hoffmann buffer_append(to, from->buffer, from->offset);
168830a9583SGerd Hoffmann
169830a9583SGerd Hoffmann g_free(from->buffer);
170830a9583SGerd Hoffmann from->offset = 0;
171830a9583SGerd Hoffmann from->capacity = 0;
172830a9583SGerd Hoffmann from->buffer = NULL;
173830a9583SGerd Hoffmann }
174