Coverage Report

Created: 2024-05-20 06:11

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