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