Coverage Report

Created: 2026-03-04 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/libfreerdp/utils/ringbuffer.c
Line
Count
Source
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 *
4
 * Copyright 2014 Thincast Technologies GmbH
5
 * Copyright 2014 Hardening <contact@hardening-consulting.com>
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 * http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
20
#include <freerdp/config.h>
21
22
#include <freerdp/utils/ringbuffer.h>
23
24
#include <stdlib.h>
25
#include <string.h>
26
#include <winpr/assert.h>
27
28
#include <winpr/crt.h>
29
#include <freerdp/log.h>
30
31
#ifdef WITH_DEBUG_RINGBUFFER
32
#define TAG FREERDP_TAG("utils.ringbuffer")
33
34
#define DEBUG_RINGBUFFER(...) WLog_DBG(TAG, __VA_ARGS__)
35
#else
36
#define DEBUG_RINGBUFFER(...) \
37
0
  do                        \
38
0
  {                         \
39
0
  } while (0)
40
#endif
41
42
BOOL ringbuffer_init(RingBuffer* ringbuffer, size_t initialSize)
43
0
{
44
0
  WINPR_ASSERT(ringbuffer);
45
0
  ringbuffer->buffer = malloc(initialSize);
46
47
0
  if (!ringbuffer->buffer)
48
0
    return FALSE;
49
50
0
  ringbuffer->readPtr = ringbuffer->writePtr = 0;
51
0
  ringbuffer->initialSize = ringbuffer->size = ringbuffer->freeSize = initialSize;
52
0
  DEBUG_RINGBUFFER("ringbuffer_init(%p)", (void*)ringbuffer);
53
0
  return TRUE;
54
0
}
55
56
size_t ringbuffer_used(const RingBuffer* ringbuffer)
57
0
{
58
0
  WINPR_ASSERT(ringbuffer);
59
0
  return ringbuffer->size - ringbuffer->freeSize;
60
0
}
61
62
size_t ringbuffer_capacity(const RingBuffer* ringbuffer)
63
0
{
64
0
  WINPR_ASSERT(ringbuffer);
65
0
  return ringbuffer->size;
66
0
}
67
68
void ringbuffer_destroy(RingBuffer* ringbuffer)
69
0
{
70
0
  DEBUG_RINGBUFFER("ringbuffer_destroy(%p)", (void*)ringbuffer);
71
0
  if (!ringbuffer)
72
0
    return;
73
74
0
  free(ringbuffer->buffer);
75
0
  ringbuffer->buffer = nullptr;
76
0
}
77
78
static BOOL ringbuffer_realloc(RingBuffer* ringbuffer, size_t targetSize)
79
0
{
80
0
  WINPR_ASSERT(ringbuffer);
81
0
  BYTE* newData = nullptr;
82
0
  DEBUG_RINGBUFFER("ringbuffer_realloc(%p): targetSize: %" PRIdz "", (void*)ringbuffer,
83
0
                   targetSize);
84
85
0
  if (ringbuffer->writePtr == ringbuffer->readPtr)
86
0
  {
87
    /* when no size is used we can realloc() and set the heads at the
88
     * beginning of the buffer
89
     */
90
0
    newData = (BYTE*)realloc(ringbuffer->buffer, targetSize);
91
92
0
    if (!newData)
93
0
      return FALSE;
94
95
0
    ringbuffer->readPtr = ringbuffer->writePtr = 0;
96
0
    ringbuffer->buffer = newData;
97
0
  }
98
0
  else if ((ringbuffer->writePtr >= ringbuffer->readPtr) && (ringbuffer->writePtr < targetSize))
99
0
  {
100
    /* we reallocate only if we're in that case, realloc don't touch read
101
     * and write heads
102
     *
103
     *        readPtr              writePtr
104
     *              |              |
105
     *              v              v
106
     * [............|XXXXXXXXXXXXXX|..........]
107
     */
108
0
    newData = (BYTE*)realloc(ringbuffer->buffer, targetSize);
109
110
0
    if (!newData)
111
0
      return FALSE;
112
113
0
    ringbuffer->buffer = newData;
114
0
  }
115
0
  else
116
0
  {
117
    /* in case of malloc the read head is moved at the beginning of the new buffer
118
     * and the write head is set accordingly
119
     */
120
0
    newData = (BYTE*)malloc(targetSize);
121
122
0
    if (!newData)
123
0
      return FALSE;
124
125
0
    if (ringbuffer->readPtr < ringbuffer->writePtr)
126
0
    {
127
      /*        readPtr              writePtr
128
       *              |              |
129
       *              v              v
130
       * [............|XXXXXXXXXXXXXX|..........]
131
       */
132
0
      memcpy(newData, ringbuffer->buffer + ringbuffer->readPtr, ringbuffer_used(ringbuffer));
133
0
    }
134
0
    else
135
0
    {
136
      /*        writePtr             readPtr
137
       *              |              |
138
       *              v              v
139
       * [XXXXXXXXXXXX|..............|XXXXXXXXXX]
140
       */
141
0
      BYTE* dst = newData;
142
0
      memcpy(dst, ringbuffer->buffer + ringbuffer->readPtr,
143
0
             ringbuffer->size - ringbuffer->readPtr);
144
0
      dst += (ringbuffer->size - ringbuffer->readPtr);
145
146
0
      if (ringbuffer->writePtr)
147
0
        memcpy(dst, ringbuffer->buffer, ringbuffer->writePtr);
148
0
    }
149
150
0
    ringbuffer->writePtr = ringbuffer->size - ringbuffer->freeSize;
151
0
    ringbuffer->readPtr = 0;
152
0
    free(ringbuffer->buffer);
153
0
    ringbuffer->buffer = newData;
154
0
  }
155
156
0
  ringbuffer->freeSize += (targetSize - ringbuffer->size);
157
0
  ringbuffer->size = targetSize;
158
0
  return TRUE;
159
0
}
160
161
/**
162
 * Write to a ringbuffer
163
 *
164
 * @param ringbuffer A pointer to the ringbuffer
165
 * @param ptr A pointer to the data to write
166
 * @param sz The number of bytes to write
167
 *
168
 * @return \b TRUE for success, \b FALSE for failure
169
 */
170
BOOL ringbuffer_write(RingBuffer* ringbuffer, const BYTE* ptr, size_t sz)
171
0
{
172
0
  size_t toWrite = 0;
173
0
  size_t remaining = 0;
174
175
0
  WINPR_ASSERT(ringbuffer);
176
0
  DEBUG_RINGBUFFER("ringbuffer_write(%p): sz: %" PRIdz "", (void*)ringbuffer, sz);
177
178
0
  if ((ringbuffer->freeSize <= sz) && !ringbuffer_realloc(ringbuffer, ringbuffer->size + sz))
179
0
    return FALSE;
180
181
  /*  the write could be split in two
182
   *    readHead        writeHead
183
   *      |               |
184
   *      v               v
185
   * [    ################        ]
186
   */
187
0
  toWrite = sz;
188
0
  remaining = sz;
189
190
0
  if (ringbuffer->size - ringbuffer->writePtr < sz)
191
0
    toWrite = ringbuffer->size - ringbuffer->writePtr;
192
193
0
  if (toWrite)
194
0
  {
195
0
    memcpy(ringbuffer->buffer + ringbuffer->writePtr, ptr, toWrite);
196
0
    remaining -= toWrite;
197
0
    ptr += toWrite;
198
0
  }
199
200
0
  if (remaining)
201
0
    memcpy(ringbuffer->buffer, ptr, remaining);
202
203
0
  ringbuffer->writePtr = (ringbuffer->writePtr + sz) % ringbuffer->size;
204
0
  ringbuffer->freeSize -= sz;
205
0
  return TRUE;
206
0
}
207
208
BYTE* ringbuffer_ensure_linear_write(RingBuffer* ringbuffer, size_t sz)
209
0
{
210
0
  DEBUG_RINGBUFFER("ringbuffer_ensure_linear_write(%p): sz: %" PRIdz "", (void*)ringbuffer, sz);
211
212
0
  WINPR_ASSERT(ringbuffer);
213
0
  if (ringbuffer->freeSize < sz)
214
0
  {
215
0
    if (!ringbuffer_realloc(ringbuffer, ringbuffer->size + sz - ringbuffer->freeSize + 32))
216
0
      return nullptr;
217
0
  }
218
219
0
  if (ringbuffer->writePtr == ringbuffer->readPtr)
220
0
  {
221
0
    ringbuffer->writePtr = ringbuffer->readPtr = 0;
222
0
  }
223
224
0
  if (ringbuffer->writePtr + sz < ringbuffer->size)
225
0
    return ringbuffer->buffer + ringbuffer->writePtr;
226
227
  /*
228
   * to add:             .......
229
   * [          XXXXXXXXX  ]
230
   *
231
   * result:
232
   * [XXXXXXXXX.......     ]
233
   */
234
0
  memmove(ringbuffer->buffer, ringbuffer->buffer + ringbuffer->readPtr,
235
0
          ringbuffer->writePtr - ringbuffer->readPtr);
236
0
  ringbuffer->readPtr = 0;
237
0
  ringbuffer->writePtr = ringbuffer->size - ringbuffer->freeSize;
238
0
  return ringbuffer->buffer + ringbuffer->writePtr;
239
0
}
240
241
BOOL ringbuffer_commit_written_bytes(RingBuffer* ringbuffer, size_t sz)
242
0
{
243
0
  DEBUG_RINGBUFFER("ringbuffer_commit_written_bytes(%p): sz: %" PRIdz "", (void*)ringbuffer, sz);
244
245
0
  WINPR_ASSERT(ringbuffer);
246
0
  if (sz < 1)
247
0
    return TRUE;
248
249
0
  if (ringbuffer->writePtr + sz > ringbuffer->size)
250
0
    return FALSE;
251
252
0
  ringbuffer->writePtr = (ringbuffer->writePtr + sz) % ringbuffer->size;
253
0
  ringbuffer->freeSize -= sz;
254
0
  return TRUE;
255
0
}
256
257
int ringbuffer_peek(const RingBuffer* ringbuffer, DataChunk chunks[2], size_t sz)
258
0
{
259
0
  size_t remaining = sz;
260
0
  size_t toRead = 0;
261
0
  int chunkIndex = 0;
262
0
  int status = 0;
263
0
  DEBUG_RINGBUFFER("ringbuffer_peek(%p): sz: %" PRIdz "", (const void*)ringbuffer, sz);
264
265
0
  WINPR_ASSERT(ringbuffer);
266
0
  if (sz < 1)
267
0
    return 0;
268
269
0
  if ((ringbuffer->size - ringbuffer->freeSize) < sz)
270
0
    remaining = ringbuffer->size - ringbuffer->freeSize;
271
272
0
  toRead = remaining;
273
274
0
  if ((ringbuffer->readPtr + remaining) > ringbuffer->size)
275
0
    toRead = ringbuffer->size - ringbuffer->readPtr;
276
277
0
  if (toRead)
278
0
  {
279
0
    chunks[0].data = ringbuffer->buffer + ringbuffer->readPtr;
280
0
    chunks[0].size = toRead;
281
0
    remaining -= toRead;
282
0
    chunkIndex++;
283
0
    status++;
284
0
  }
285
286
0
  if (remaining)
287
0
  {
288
0
    chunks[chunkIndex].data = ringbuffer->buffer;
289
0
    chunks[chunkIndex].size = remaining;
290
0
    status++;
291
0
  }
292
293
0
  return status;
294
0
}
295
296
void ringbuffer_commit_read_bytes(RingBuffer* ringbuffer, size_t sz)
297
0
{
298
0
  DEBUG_RINGBUFFER("ringbuffer_commit_read_bytes(%p): sz: %" PRIdz "", (void*)ringbuffer, sz);
299
300
0
  WINPR_ASSERT(ringbuffer);
301
0
  if (sz < 1)
302
0
    return;
303
304
0
  WINPR_ASSERT(ringbuffer->size - ringbuffer->freeSize >= sz);
305
0
  ringbuffer->readPtr = (ringbuffer->readPtr + sz) % ringbuffer->size;
306
0
  ringbuffer->freeSize += sz;
307
308
  /* when we reach a reasonable free size, we can go back to the original size */
309
0
  if ((ringbuffer->size != ringbuffer->initialSize) &&
310
0
      (ringbuffer_used(ringbuffer) < ringbuffer->initialSize / 2))
311
0
    ringbuffer_realloc(ringbuffer, ringbuffer->initialSize);
312
0
}