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