xref: /openbmc/qemu/util/buffer.c (revision 683685e7)
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