xref: /openbmc/qemu/util/buffer.c (revision 623d7e3551a6fc5693c06ea938c60fe281b52e27)
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.1 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/osdep.h"
22  #include "qemu/host-utils.h"
23  #include "qemu/buffer.h"
24  #include "trace.h"
25  
26  #define BUFFER_MIN_INIT_SIZE     4096
27  #define BUFFER_MIN_SHRINK_SIZE  65536
28  
29  /* define the factor alpha for the exponential smoothing
30   * that is used in the average size calculation. a shift
31   * of 7 results in an alpha of 1/2^7. */
32  #define BUFFER_AVG_SIZE_SHIFT       7
33  
34  static size_t buffer_req_size(Buffer *buffer, size_t len)
35  {
36      return MAX(BUFFER_MIN_INIT_SIZE,
37                 pow2ceil(buffer->offset + len));
38  }
39  
40  static void buffer_adj_size(Buffer *buffer, size_t len)
41  {
42      size_t old = buffer->capacity;
43      buffer->capacity = buffer_req_size(buffer, len);
44      buffer->buffer = g_realloc(buffer->buffer, buffer->capacity);
45      trace_buffer_resize(buffer->name ?: "unnamed",
46                          old, buffer->capacity);
47  
48      /* make it even harder for the buffer to shrink, reset average size
49       * to current capacity if it is larger than the average. */
50      buffer->avg_size = MAX(buffer->avg_size,
51                             buffer->capacity << BUFFER_AVG_SIZE_SHIFT);
52  }
53  
54  void buffer_init(Buffer *buffer, const char *name, ...)
55  {
56      va_list ap;
57  
58      va_start(ap, name);
59      buffer->name = g_strdup_vprintf(name, ap);
60      va_end(ap);
61  }
62  
63  static uint64_t buffer_get_avg_size(Buffer *buffer)
64  {
65      return buffer->avg_size >> BUFFER_AVG_SIZE_SHIFT;
66  }
67  
68  void buffer_shrink(Buffer *buffer)
69  {
70      size_t new;
71  
72      /* Calculate the average size of the buffer as
73       * avg_size = avg_size * ( 1 - a ) + required_size * a
74       * where a is 1 / 2 ^ BUFFER_AVG_SIZE_SHIFT. */
75      buffer->avg_size *= (1 << BUFFER_AVG_SIZE_SHIFT) - 1;
76      buffer->avg_size >>= BUFFER_AVG_SIZE_SHIFT;
77      buffer->avg_size += buffer_req_size(buffer, 0);
78  
79      /* And then only shrink if the average size of the buffer is much
80       * too big, to avoid bumping up & down the buffers all the time.
81       * realloc() isn't exactly cheap ...  */
82      new = buffer_req_size(buffer, buffer_get_avg_size(buffer));
83      if (new < buffer->capacity >> 3 &&
84          new >= BUFFER_MIN_SHRINK_SIZE) {
85          buffer_adj_size(buffer, buffer_get_avg_size(buffer));
86      }
87  
88      buffer_adj_size(buffer, 0);
89  }
90  
91  void buffer_reserve(Buffer *buffer, size_t len)
92  {
93      if ((buffer->capacity - buffer->offset) < len) {
94          buffer_adj_size(buffer, len);
95      }
96  }
97  
98  gboolean buffer_empty(Buffer *buffer)
99  {
100      return buffer->offset == 0;
101  }
102  
103  uint8_t *buffer_end(Buffer *buffer)
104  {
105      return buffer->buffer + buffer->offset;
106  }
107  
108  void buffer_reset(Buffer *buffer)
109  {
110      buffer->offset = 0;
111      buffer_shrink(buffer);
112  }
113  
114  void buffer_free(Buffer *buffer)
115  {
116      trace_buffer_free(buffer->name ?: "unnamed", buffer->capacity);
117      g_free(buffer->buffer);
118      g_free(buffer->name);
119      buffer->offset = 0;
120      buffer->capacity = 0;
121      buffer->buffer = NULL;
122      buffer->name = NULL;
123  }
124  
125  void buffer_append(Buffer *buffer, const void *data, size_t len)
126  {
127      memcpy(buffer->buffer + buffer->offset, data, len);
128      buffer->offset += len;
129  }
130  
131  void buffer_advance(Buffer *buffer, size_t len)
132  {
133      memmove(buffer->buffer, buffer->buffer + len,
134              (buffer->offset - len));
135      buffer->offset -= len;
136      buffer_shrink(buffer);
137  }
138  
139  void buffer_move_empty(Buffer *to, Buffer *from)
140  {
141      trace_buffer_move_empty(to->name ?: "unnamed",
142                              from->offset,
143                              from->name ?: "unnamed");
144      assert(to->offset == 0);
145  
146      g_free(to->buffer);
147      to->offset = from->offset;
148      to->capacity = from->capacity;
149      to->buffer = from->buffer;
150  
151      from->offset = 0;
152      from->capacity = 0;
153      from->buffer = NULL;
154  }
155  
156  void buffer_move(Buffer *to, Buffer *from)
157  {
158      if (to->offset == 0) {
159          buffer_move_empty(to, from);
160          return;
161      }
162  
163      trace_buffer_move(to->name ?: "unnamed",
164                        from->offset,
165                        from->name ?: "unnamed");
166      buffer_reserve(to, from->offset);
167      buffer_append(to, from->buffer, from->offset);
168  
169      g_free(from->buffer);
170      from->offset = 0;
171      from->capacity = 0;
172      from->buffer = NULL;
173  }
174