Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/libfreerdp/utils/ringbuffer.c
Line
Count
Source (jump to first uncovered line)
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* rb, size_t initialSize)
43
0
{
44
0
  WINPR_ASSERT(rb);
45
0
  rb->buffer = malloc(initialSize);
46
47
0
  if (!rb->buffer)
48
0
    return FALSE;
49
50
0
  rb->readPtr = rb->writePtr = 0;
51
0
  rb->initialSize = rb->size = rb->freeSize = initialSize;
52
0
  DEBUG_RINGBUFFER("ringbuffer_init(%p)", (void*)rb);
53
0
  return TRUE;
54
0
}
55
56
size_t ringbuffer_used(const RingBuffer* rb)
57
0
{
58
0
  WINPR_ASSERT(rb);
59
0
  return rb->size - rb->freeSize;
60
0
}
61
62
size_t ringbuffer_capacity(const RingBuffer* rb)
63
0
{
64
0
  WINPR_ASSERT(rb);
65
0
  return rb->size;
66
0
}
67
68
void ringbuffer_destroy(RingBuffer* rb)
69
0
{
70
0
  DEBUG_RINGBUFFER("ringbuffer_destroy(%p)", (void*)rb);
71
0
  if (!rb)
72
0
    return;
73
74
0
  free(rb->buffer);
75
0
  rb->buffer = NULL;
76
0
}
77
78
static BOOL ringbuffer_realloc(RingBuffer* rb, size_t targetSize)
79
0
{
80
0
  WINPR_ASSERT(rb);
81
0
  BYTE* newData = NULL;
82
0
  DEBUG_RINGBUFFER("ringbuffer_realloc(%p): targetSize: %" PRIdz "", (void*)rb, targetSize);
83
84
0
  if (rb->writePtr == rb->readPtr)
85
0
  {
86
    /* when no size is used we can realloc() and set the heads at the
87
     * beginning of the buffer
88
     */
89
0
    newData = (BYTE*)realloc(rb->buffer, targetSize);
90
91
0
    if (!newData)
92
0
      return FALSE;
93
94
0
    rb->readPtr = rb->writePtr = 0;
95
0
    rb->buffer = newData;
96
0
  }
97
0
  else if ((rb->writePtr >= rb->readPtr) && (rb->writePtr < targetSize))
98
0
  {
99
    /* we reallocate only if we're in that case, realloc don't touch read
100
     * and write heads
101
     *
102
     *        readPtr              writePtr
103
     *              |              |
104
     *              v              v
105
     * [............|XXXXXXXXXXXXXX|..........]
106
     */
107
0
    newData = (BYTE*)realloc(rb->buffer, targetSize);
108
109
0
    if (!newData)
110
0
      return FALSE;
111
112
0
    rb->buffer = newData;
113
0
  }
114
0
  else
115
0
  {
116
    /* in case of malloc the read head is moved at the beginning of the new buffer
117
     * and the write head is set accordingly
118
     */
119
0
    newData = (BYTE*)malloc(targetSize);
120
121
0
    if (!newData)
122
0
      return FALSE;
123
124
0
    if (rb->readPtr < rb->writePtr)
125
0
    {
126
      /*        readPtr              writePtr
127
       *              |              |
128
       *              v              v
129
       * [............|XXXXXXXXXXXXXX|..........]
130
       */
131
0
      memcpy(newData, rb->buffer + rb->readPtr, ringbuffer_used(rb));
132
0
    }
133
0
    else
134
0
    {
135
      /*        writePtr             readPtr
136
       *              |              |
137
       *              v              v
138
       * [XXXXXXXXXXXX|..............|XXXXXXXXXX]
139
       */
140
0
      BYTE* dst = newData;
141
0
      memcpy(dst, rb->buffer + rb->readPtr, rb->size - rb->readPtr);
142
0
      dst += (rb->size - rb->readPtr);
143
144
0
      if (rb->writePtr)
145
0
        memcpy(dst, rb->buffer, rb->writePtr);
146
0
    }
147
148
0
    rb->writePtr = rb->size - rb->freeSize;
149
0
    rb->readPtr = 0;
150
0
    free(rb->buffer);
151
0
    rb->buffer = newData;
152
0
  }
153
154
0
  rb->freeSize += (targetSize - rb->size);
155
0
  rb->size = targetSize;
156
0
  return TRUE;
157
0
}
158
159
/**
160
 * Write to a ringbuffer
161
 *
162
 * @param rb A pointer to the ringbuffer
163
 * @param ptr A pointer to the data to write
164
 * @param sz The number of bytes to write
165
 *
166
 * @return \b TRUE for success, \b FALSE for failure
167
 */
168
BOOL ringbuffer_write(RingBuffer* rb, const BYTE* ptr, size_t sz)
169
0
{
170
0
  size_t toWrite = 0;
171
0
  size_t remaining = 0;
172
173
0
  WINPR_ASSERT(rb);
174
0
  DEBUG_RINGBUFFER("ringbuffer_write(%p): sz: %" PRIdz "", (void*)rb, sz);
175
176
0
  if ((rb->freeSize <= sz) && !ringbuffer_realloc(rb, rb->size + sz))
177
0
    return FALSE;
178
179
  /*  the write could be split in two
180
   *    readHead        writeHead
181
   *      |               |
182
   *      v               v
183
   * [    ################        ]
184
   */
185
0
  toWrite = sz;
186
0
  remaining = sz;
187
188
0
  if (rb->size - rb->writePtr < sz)
189
0
    toWrite = rb->size - rb->writePtr;
190
191
0
  if (toWrite)
192
0
  {
193
0
    memcpy(rb->buffer + rb->writePtr, ptr, toWrite);
194
0
    remaining -= toWrite;
195
0
    ptr += toWrite;
196
0
  }
197
198
0
  if (remaining)
199
0
    memcpy(rb->buffer, ptr, remaining);
200
201
0
  rb->writePtr = (rb->writePtr + sz) % rb->size;
202
0
  rb->freeSize -= sz;
203
0
  return TRUE;
204
0
}
205
206
BYTE* ringbuffer_ensure_linear_write(RingBuffer* rb, size_t sz)
207
0
{
208
0
  DEBUG_RINGBUFFER("ringbuffer_ensure_linear_write(%p): sz: %" PRIdz "", (void*)rb, sz);
209
210
0
  WINPR_ASSERT(rb);
211
0
  if (rb->freeSize < sz)
212
0
  {
213
0
    if (!ringbuffer_realloc(rb, rb->size + sz - rb->freeSize + 32))
214
0
      return NULL;
215
0
  }
216
217
0
  if (rb->writePtr == rb->readPtr)
218
0
  {
219
0
    rb->writePtr = rb->readPtr = 0;
220
0
  }
221
222
0
  if (rb->writePtr + sz < rb->size)
223
0
    return rb->buffer + rb->writePtr;
224
225
  /*
226
   * to add:             .......
227
   * [          XXXXXXXXX  ]
228
   *
229
   * result:
230
   * [XXXXXXXXX.......     ]
231
   */
232
0
  memmove(rb->buffer, rb->buffer + rb->readPtr, rb->writePtr - rb->readPtr);
233
0
  rb->readPtr = 0;
234
0
  rb->writePtr = rb->size - rb->freeSize;
235
0
  return rb->buffer + rb->writePtr;
236
0
}
237
238
BOOL ringbuffer_commit_written_bytes(RingBuffer* rb, size_t sz)
239
0
{
240
0
  DEBUG_RINGBUFFER("ringbuffer_commit_written_bytes(%p): sz: %" PRIdz "", (void*)rb, sz);
241
242
0
  WINPR_ASSERT(rb);
243
0
  if (sz < 1)
244
0
    return TRUE;
245
246
0
  if (rb->writePtr + sz > rb->size)
247
0
    return FALSE;
248
249
0
  rb->writePtr = (rb->writePtr + sz) % rb->size;
250
0
  rb->freeSize -= sz;
251
0
  return TRUE;
252
0
}
253
254
int ringbuffer_peek(const RingBuffer* rb, DataChunk chunks[2], size_t sz)
255
0
{
256
0
  size_t remaining = sz;
257
0
  size_t toRead = 0;
258
0
  int chunkIndex = 0;
259
0
  int status = 0;
260
0
  DEBUG_RINGBUFFER("ringbuffer_peek(%p): sz: %" PRIdz "", (const void*)rb, sz);
261
262
0
  WINPR_ASSERT(rb);
263
0
  if (sz < 1)
264
0
    return 0;
265
266
0
  if ((rb->size - rb->freeSize) < sz)
267
0
    remaining = rb->size - rb->freeSize;
268
269
0
  toRead = remaining;
270
271
0
  if ((rb->readPtr + remaining) > rb->size)
272
0
    toRead = rb->size - rb->readPtr;
273
274
0
  if (toRead)
275
0
  {
276
0
    chunks[0].data = rb->buffer + rb->readPtr;
277
0
    chunks[0].size = toRead;
278
0
    remaining -= toRead;
279
0
    chunkIndex++;
280
0
    status++;
281
0
  }
282
283
0
  if (remaining)
284
0
  {
285
0
    chunks[chunkIndex].data = rb->buffer;
286
0
    chunks[chunkIndex].size = remaining;
287
0
    status++;
288
0
  }
289
290
0
  return status;
291
0
}
292
293
void ringbuffer_commit_read_bytes(RingBuffer* rb, size_t sz)
294
0
{
295
0
  DEBUG_RINGBUFFER("ringbuffer_commit_read_bytes(%p): sz: %" PRIdz "", (void*)rb, sz);
296
297
0
  WINPR_ASSERT(rb);
298
0
  if (sz < 1)
299
0
    return;
300
301
0
  WINPR_ASSERT(rb->size - rb->freeSize >= sz);
302
0
  rb->readPtr = (rb->readPtr + sz) % rb->size;
303
0
  rb->freeSize += sz;
304
305
  /* when we reach a reasonable free size, we can go back to the original size */
306
0
  if ((rb->size != rb->initialSize) && (ringbuffer_used(rb) < rb->initialSize / 2))
307
0
    ringbuffer_realloc(rb, rb->initialSize);
308
0
}