3 * Copyright (C) 2013-2023 Filipe Coelho <falktx@falktx.com>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of
8 * the License, or any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * For a full copy of the GNU General Public License see the doc/GPL.txt file.
18 #ifndef CARLA_RING_BUFFER_HPP_INCLUDED
19 #define CARLA_RING_BUFFER_HPP_INCLUDED
21 #include "CarlaMathUtils.hpp"
22 #include "CarlaMemUtils.hpp"
24 // --------------------------------------------------------------------------------------------------------------------
29 current writing position, headmost position of the buffer.
30 increments when writing.
33 current reading position, last used position of the buffer.
34 increments when reading.
35 head == tail means empty buffer
38 temporary position of head until a commitWrite() is called.
39 if buffer writing fails, wrtn will be back to head position thus ignoring the last operation(s).
40 if buffer writing succeeds, head will be set to this variable.
43 boolean used to check if a write operation failed.
44 this ensures we don't get incomplete writes.
49 uint32_t head
, tail
, wrtn
;
50 bool invalidateCommit
;
53 void copyDataFrom(const HeapBuffer
& rb
) noexcept
55 CARLA_SAFE_ASSERT_RETURN(size
== rb
.size
,);
60 invalidateCommit
= rb
.invalidateCommit
;
61 std::memcpy(buf
, rb
.buf
, size
);
65 struct SmallStackBuffer
{
66 static constexpr const uint32_t size
= 4096;
67 uint32_t head
, tail
, wrtn
;
68 bool invalidateCommit
;
72 struct BigStackBuffer
{
73 static constexpr const uint32_t size
= 16384;
74 uint32_t head
, tail
, wrtn
;
75 bool invalidateCommit
;
79 struct HugeStackBuffer
{
80 static constexpr const uint32_t size
= 65536;
81 uint32_t head
, tail
, wrtn
;
82 bool invalidateCommit
;
86 // --------------------------------------------------------------------------------------------------------------------
87 // CarlaRingBufferControl templated class
89 template <class BufferStruct
>
90 class CarlaRingBufferControl
93 CarlaRingBufferControl() noexcept
96 fErrorWriting(false) {}
98 virtual ~CarlaRingBufferControl() noexcept
{}
100 // ----------------------------------------------------------------------------------------------------------------
102 void clearData() noexcept
104 CARLA_SAFE_ASSERT_RETURN(fBuffer
!= nullptr,);
106 fBuffer
->head
= fBuffer
->tail
= fBuffer
->wrtn
= 0;
107 fBuffer
->invalidateCommit
= false;
109 carla_zeroBytes(fBuffer
->buf
, fBuffer
->size
);
111 fErrorReading
= fErrorWriting
= false;
114 void flush() noexcept
116 CARLA_SAFE_ASSERT_RETURN(fBuffer
!= nullptr,);
118 fBuffer
->head
= fBuffer
->tail
= fBuffer
->wrtn
= 0;
119 fBuffer
->invalidateCommit
= false;
121 fErrorWriting
= false;
124 // ----------------------------------------------------------------------------------------------------------------
126 bool commitWrite() noexcept
128 CARLA_SAFE_ASSERT_RETURN(fBuffer
!= nullptr, false);
130 if (fBuffer
->invalidateCommit
)
132 fBuffer
->wrtn
= fBuffer
->head
;
133 fBuffer
->invalidateCommit
= false;
137 // nothing to commit?
138 CARLA_SAFE_ASSERT_RETURN(fBuffer
->head
!= fBuffer
->wrtn
, false);
141 fBuffer
->head
= fBuffer
->wrtn
;
142 fErrorWriting
= false;
146 bool isDataAvailableForReading() const noexcept
;
148 bool isEmpty() const noexcept
150 CARLA_SAFE_ASSERT_RETURN(fBuffer
!= nullptr, false);
152 return fBuffer
->buf
== nullptr || fBuffer
->head
== fBuffer
->tail
;
155 uint32_t getSize() const noexcept
157 return fBuffer
!= nullptr ? fBuffer
->size
: 0;
160 uint32_t getReadableDataSize() const noexcept
162 CARLA_SAFE_ASSERT_RETURN(fBuffer
!= nullptr, 0);
164 const uint32_t wrap
= fBuffer
->head
>= fBuffer
->tail
? 0 : fBuffer
->size
;
166 return wrap
+ fBuffer
->head
- fBuffer
->tail
;
169 uint32_t getWritableDataSize() const noexcept
171 CARLA_SAFE_ASSERT_RETURN(fBuffer
!= nullptr, 0);
173 const uint32_t wrap
= fBuffer
->tail
> fBuffer
->wrtn
? 0 : fBuffer
->size
;
175 return wrap
+ fBuffer
->tail
- fBuffer
->wrtn
- 1;
178 // ----------------------------------------------------------------------------------------------------------------
180 bool readBool() noexcept
183 return tryRead(&b
, sizeof(bool)) ? b
: false;
186 uint8_t readByte() noexcept
189 return tryRead(&B
, sizeof(uint8_t)) ? B
: 0;
192 int16_t readShort() noexcept
195 return tryRead(&s
, sizeof(int16_t)) ? s
: 0;
198 uint16_t readUShort() noexcept
201 return tryRead(&us
, sizeof(uint16_t)) ? us
: 0;
204 int32_t readInt() noexcept
207 return tryRead(&i
, sizeof(int32_t)) ? i
: 0;
210 uint32_t readUInt() noexcept
213 return tryRead(&ui
, sizeof(int32_t)) ? ui
: 0;
216 int64_t readLong() noexcept
219 return tryRead(&l
, sizeof(int64_t)) ? l
: 0;
222 uint64_t readULong() noexcept
225 return tryRead(&ul
, sizeof(int64_t)) ? ul
: 0;
228 float readFloat() noexcept
231 return tryRead(&f
, sizeof(float)) ? f
: 0.0f
;
234 double readDouble() noexcept
237 return tryRead(&d
, sizeof(double)) ? d
: 0.0;
240 void readCustomData(void* const data
, const uint32_t size
) noexcept
242 CARLA_SAFE_ASSERT_RETURN(data
!= nullptr,);
243 CARLA_SAFE_ASSERT_RETURN(size
> 0,);
245 if (! tryRead(data
, size
))
246 std::memset(data
, 0, size
);
249 template <typename T
>
250 void readCustomType(T
& type
) noexcept
252 if (! tryRead(&type
, sizeof(T
)))
253 std::memset(&type
, 0, sizeof(T
));
256 // ----------------------------------------------------------------------------------------------------------------
258 void skipRead(const uint32_t size
) noexcept
260 CARLA_SAFE_ASSERT_RETURN(fBuffer
!= nullptr,);
261 CARLA_SAFE_ASSERT_RETURN(size
> 0,);
262 CARLA_SAFE_ASSERT_RETURN(size
< fBuffer
->size
,);
265 if (fBuffer
->head
== fBuffer
->tail
)
268 const uint32_t head
= fBuffer
->head
;
269 const uint32_t tail
= fBuffer
->tail
;
270 const uint32_t wrap
= head
> tail
? 0 : fBuffer
->size
;
272 if (size
> wrap
+ head
- tail
)
276 fErrorReading
= true;
277 carla_stderr2("CarlaRingBuffer::skipRead(%u): failed, not enough space", size
);
282 uint32_t readto
= tail
+ size
;
284 if (readto
>= fBuffer
->size
)
285 readto
-= fBuffer
->size
;
287 fBuffer
->tail
= readto
;
288 fErrorReading
= false;
292 // ----------------------------------------------------------------------------------------------------------------
294 bool writeBool(const bool value
) noexcept
296 return tryWrite(&value
, sizeof(bool));
299 bool writeByte(const uint8_t value
) noexcept
301 return tryWrite(&value
, sizeof(uint8_t));
304 bool writeShort(const int16_t value
) noexcept
306 return tryWrite(&value
, sizeof(int16_t));
309 bool writeUShort(const uint16_t value
) noexcept
311 return tryWrite(&value
, sizeof(uint16_t));
314 bool writeInt(const int32_t value
) noexcept
316 return tryWrite(&value
, sizeof(int32_t));
319 bool writeUInt(const uint32_t value
) noexcept
321 return tryWrite(&value
, sizeof(uint32_t));
324 bool writeLong(const int64_t value
) noexcept
326 return tryWrite(&value
, sizeof(int64_t));
329 bool writeULong(const uint64_t value
) noexcept
331 return tryWrite(&value
, sizeof(uint64_t));
334 bool writeFloat(const float value
) noexcept
336 return tryWrite(&value
, sizeof(float));
339 bool writeDouble(const double value
) noexcept
341 return tryWrite(&value
, sizeof(double));
344 bool writeCustomData(const void* const data
, const uint32_t size
) noexcept
346 CARLA_SAFE_ASSERT_RETURN(data
!= nullptr, false);
347 CARLA_SAFE_ASSERT_RETURN(size
> 0, false);
349 return tryWrite(data
, size
);
352 template <typename T
>
353 bool writeCustomType(const T
& type
) noexcept
355 return tryWrite(&type
, sizeof(T
));
358 // ----------------------------------------------------------------------------------------------------------------
361 void setRingBuffer(BufferStruct
* const ringBuf
, const bool resetBuffer
) noexcept
363 CARLA_SAFE_ASSERT_RETURN(fBuffer
!= ringBuf
,);
367 if (resetBuffer
&& ringBuf
!= nullptr)
371 // ----------------------------------------------------------------------------------------------------------------
373 bool tryRead(void* const buf
, const uint32_t size
) noexcept
375 CARLA_SAFE_ASSERT_RETURN(fBuffer
!= nullptr, false);
376 #if defined(__clang__)
377 #pragma clang diagnostic push
378 #pragma clang diagnostic ignored "-Wtautological-pointer-compare"
380 CARLA_SAFE_ASSERT_RETURN(fBuffer
->buf
!= nullptr, false);
381 #if defined(__clang__)
382 #pragma clang diagnostic pop
384 CARLA_SAFE_ASSERT_RETURN(buf
!= nullptr, false);
385 CARLA_SAFE_ASSERT_RETURN(size
> 0, false);
386 CARLA_SAFE_ASSERT_RETURN(size
< fBuffer
->size
, false);
389 if (fBuffer
->head
== fBuffer
->tail
)
392 uint8_t* const bytebuf
= static_cast<uint8_t*>(buf
);
394 const uint32_t head
= fBuffer
->head
;
395 const uint32_t tail
= fBuffer
->tail
;
396 const uint32_t wrap
= head
> tail
? 0 : fBuffer
->size
;
398 if (size
> wrap
+ head
- tail
)
402 fErrorReading
= true;
403 carla_stderr2("CarlaRingBuffer::tryRead(%p, %u): failed, not enough space", buf
, size
);
408 uint32_t readto
= tail
+ size
;
410 if (readto
> fBuffer
->size
)
412 readto
-= fBuffer
->size
;
416 std::memcpy(bytebuf
, fBuffer
->buf
+ tail
, 1);
420 const uint32_t firstpart
= fBuffer
->size
- tail
;
421 std::memcpy(bytebuf
, fBuffer
->buf
+ tail
, firstpart
);
422 std::memcpy(bytebuf
+ firstpart
, fBuffer
->buf
, readto
);
427 std::memcpy(bytebuf
, fBuffer
->buf
+ tail
, size
);
429 if (readto
== fBuffer
->size
)
433 fBuffer
->tail
= readto
;
434 fErrorReading
= false;
438 bool tryWrite(const void* const buf
, const uint32_t size
) noexcept
440 CARLA_SAFE_ASSERT_RETURN(fBuffer
!= nullptr, false);
441 CARLA_SAFE_ASSERT_RETURN(buf
!= nullptr, false);
442 CARLA_SAFE_ASSERT_RETURN(size
> 0, false);
443 CARLA_SAFE_ASSERT_UINT2_RETURN(size
< fBuffer
->size
, size
, fBuffer
->size
, false);
445 const uint8_t* const bytebuf
= static_cast<const uint8_t*>(buf
);
447 const uint32_t tail
= fBuffer
->tail
;
448 const uint32_t wrtn
= fBuffer
->wrtn
;
449 const uint32_t wrap
= tail
> wrtn
? 0 : fBuffer
->size
;
451 if (size
>= wrap
+ tail
- wrtn
)
455 fErrorWriting
= true;
456 carla_stderr2("CarlaRingBuffer::tryWrite(%p, %u): failed, not enough space", buf
, size
);
458 fBuffer
->invalidateCommit
= true;
462 uint32_t writeto
= wrtn
+ size
;
464 if (writeto
> fBuffer
->size
)
466 writeto
-= fBuffer
->size
;
470 std::memcpy(fBuffer
->buf
, bytebuf
, 1);
474 const uint32_t firstpart
= fBuffer
->size
- wrtn
;
475 std::memcpy(fBuffer
->buf
+ wrtn
, bytebuf
, firstpart
);
476 std::memcpy(fBuffer
->buf
, bytebuf
+ firstpart
, writeto
);
481 std::memcpy(fBuffer
->buf
+ wrtn
, bytebuf
, size
);
483 if (writeto
== fBuffer
->size
)
487 fBuffer
->wrtn
= writeto
;
492 BufferStruct
* fBuffer
;
494 // wherever read/write errors have been printed to terminal
498 CARLA_PREVENT_VIRTUAL_HEAP_ALLOCATION
499 CARLA_DECLARE_NON_COPYABLE(CarlaRingBufferControl
)
502 template <class BufferStruct
>
503 inline bool CarlaRingBufferControl
<BufferStruct
>::isDataAvailableForReading() const noexcept
505 return (fBuffer
!= nullptr && fBuffer
->head
!= fBuffer
->tail
);
509 inline bool CarlaRingBufferControl
<HeapBuffer
>::isDataAvailableForReading() const noexcept
511 return (fBuffer
!= nullptr && fBuffer
->buf
!= nullptr && fBuffer
->head
!= fBuffer
->tail
);
514 // --------------------------------------------------------------------------------------------------------------------
515 // CarlaRingBuffer using heap space
517 class CarlaHeapRingBuffer
: public CarlaRingBufferControl
<HeapBuffer
>
520 CarlaHeapRingBuffer() noexcept
521 : fHeapBuffer
{0, 0, 0, 0, false, nullptr} {}
523 ~CarlaHeapRingBuffer() noexcept override
525 if (fHeapBuffer
.buf
== nullptr)
528 delete[] fHeapBuffer
.buf
;
529 fHeapBuffer
.buf
= nullptr;
532 void createBuffer(const uint32_t size
, const bool mlock
) noexcept
534 CARLA_SAFE_ASSERT_RETURN(fHeapBuffer
.buf
== nullptr,);
535 CARLA_SAFE_ASSERT_RETURN(size
> 0,);
537 const uint32_t p2size
= carla_nextPowerOf2(size
);
540 fHeapBuffer
.buf
= new uint8_t[p2size
];
541 } CARLA_SAFE_EXCEPTION_RETURN("CarlaHeapRingBuffer::createBuffer",);
543 fHeapBuffer
.size
= p2size
;
544 setRingBuffer(&fHeapBuffer
, true);
548 carla_mlock(&fHeapBuffer
, sizeof(fHeapBuffer
));
549 carla_mlock(fHeapBuffer
.buf
, p2size
);
553 void deleteBuffer() noexcept
555 if (fHeapBuffer
.buf
== nullptr)
558 setRingBuffer(nullptr, false);
560 delete[] fHeapBuffer
.buf
;
561 fHeapBuffer
.buf
= nullptr;
562 fHeapBuffer
.size
= 0;
566 HeapBuffer fHeapBuffer
;
568 CARLA_PREVENT_VIRTUAL_HEAP_ALLOCATION
569 CARLA_DECLARE_NON_COPYABLE(CarlaHeapRingBuffer
)
572 // --------------------------------------------------------------------------------------------------------------------
573 // CarlaRingBuffer using small stack space
575 class CarlaSmallStackRingBuffer
: public CarlaRingBufferControl
<SmallStackBuffer
>
578 CarlaSmallStackRingBuffer() noexcept
579 : fStackBuffer
{0, 0, 0, false, {}}
581 setRingBuffer(&fStackBuffer
, true);
585 SmallStackBuffer fStackBuffer
;
587 CARLA_PREVENT_VIRTUAL_HEAP_ALLOCATION
588 CARLA_DECLARE_NON_COPYABLE(CarlaSmallStackRingBuffer
)
591 // --------------------------------------------------------------------------------------------------------------------
593 #endif // CARLA_RING_BUFFER_HPP_INCLUDED