annotate base/RingBuffer.h @ 1833:21c792334c2e sensible-delimited-data-strings

Rewrite all the DelimitedDataString stuff so as to return vectors of individual cell strings rather than having the classes add the delimiters themselves. Rename accordingly to names based on StringExport. Take advantage of this in the CSV writer code so as to properly quote cells that contain delimiter characters.
author Chris Cannam
date Fri, 03 Apr 2020 17:11:05 +0100
parents 51fd27fbce9a
children
rev   line source
Chris@49 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 /*
Chris@52 4 Sonic Visualiser
Chris@52 5 An audio file viewer and annotation editor.
Chris@52 6 Centre for Digital Music, Queen Mary, University of London.
Chris@0 7
Chris@52 8 This program is free software; you can redistribute it and/or
Chris@52 9 modify it under the terms of the GNU General Public License as
Chris@52 10 published by the Free Software Foundation; either version 2 of the
Chris@52 11 License, or (at your option) any later version. See the file
Chris@52 12 COPYING included with this distribution for more information.
Chris@0 13 */
Chris@0 14
Chris@0 15 /*
Chris@0 16 This is a modified version of a source file from the
Chris@0 17 Rosegarden MIDI and audio sequencer and notation editor.
Chris@17 18 This file copyright 2000-2006 Chris Cannam.
Chris@0 19 */
Chris@0 20
Chris@1553 21 #ifndef SV_RINGBUFFER_H
Chris@1553 22 #define SV_RINGBUFFER_H
Chris@0 23
Chris@0 24 #include <sys/types.h>
Chris@0 25
Chris@150 26 #include "system/System.h"
Chris@0 27
Chris@1553 28 #include <bqvec/Barrier.h>
Chris@1827 29 #include <bqvec/VectorOps.h>
Chris@1553 30
Chris@1827 31 #include <vector>
Chris@485 32
Chris@0 33 //#define DEBUG_RINGBUFFER 1
Chris@0 34
Chris@0 35 #ifdef DEBUG_RINGBUFFER
Chris@0 36 #include <iostream>
Chris@0 37 #endif
Chris@0 38
Chris@0 39 /**
Chris@0 40 * RingBuffer implements a lock-free ring buffer for one writer and N
Chris@0 41 * readers, that is to be used to store a sample type T.
Chris@0 42 */
Chris@0 43
Chris@0 44 template <typename T, int N = 1>
Chris@0 45 class RingBuffer
Chris@0 46 {
Chris@0 47 public:
Chris@0 48 /**
Chris@0 49 * Create a ring buffer with room to write n samples.
Chris@0 50 *
Chris@0 51 * Note that the internal storage size will actually be n+1
Chris@0 52 * samples, as one element is unavailable for administrative
Chris@0 53 * reasons. Since the ring buffer performs best if its size is a
Chris@0 54 * power of two, this means n should ideally be some power of two
Chris@0 55 * minus one.
Chris@0 56 */
Chris@928 57 RingBuffer(int n);
Chris@0 58
Chris@0 59 virtual ~RingBuffer();
Chris@0 60
Chris@0 61 /**
Chris@0 62 * Return the total capacity of the ring buffer in samples.
Chris@0 63 * (This is the argument n passed to the constructor.)
Chris@0 64 */
Chris@928 65 int getSize() const;
Chris@0 66
Chris@0 67 /**
Chris@1827 68 * Return a new ring buffer of the given size, containing the same
Chris@1827 69 * data as this one as perceived by reader 0 of this buffer. If
Chris@1827 70 * another thread reads from or writes to this buffer during the
Chris@1827 71 * call, the contents of the new buffer may be incomplete or
Chris@835 72 * inconsistent. If this buffer's data will not fit in the new
Chris@1827 73 * size, the contents are undetermined.
Chris@0 74 */
Chris@1827 75 RingBuffer<T, N> resized(int newSize) const;
Chris@0 76
Chris@0 77 /**
Chris@0 78 * Reset read and write pointers, thus emptying the buffer.
Chris@0 79 * Should be called from the write thread.
Chris@0 80 */
Chris@0 81 void reset();
Chris@0 82
Chris@0 83 /**
Chris@0 84 * Return the amount of data available for reading by reader R, in
Chris@0 85 * samples.
Chris@0 86 */
Chris@928 87 int getReadSpace(int R = 0) const;
Chris@0 88
Chris@0 89 /**
Chris@0 90 * Return the amount of space available for writing, in samples.
Chris@0 91 */
Chris@928 92 int getWriteSpace() const;
Chris@0 93
Chris@0 94 /**
Chris@0 95 * Read n samples from the buffer, for reader R. If fewer than n
Chris@0 96 * are available, the remainder will be zeroed out. Returns the
Chris@0 97 * number of samples actually read.
Chris@0 98 */
Chris@928 99 int read(T *destination, int n, int R = 0);
Chris@0 100
Chris@0 101 /**
Chris@0 102 * Read n samples from the buffer, for reader R, adding them to
Chris@0 103 * the destination. If fewer than n are available, the remainder
Chris@0 104 * will be left alone. Returns the number of samples actually
Chris@0 105 * read.
Chris@0 106 */
Chris@928 107 int readAdding(T *destination, int n, int R = 0);
Chris@0 108
Chris@0 109 /**
Chris@0 110 * Read one sample from the buffer, for reader R. If no sample is
Chris@0 111 * available, this will silently return zero. Calling this
Chris@0 112 * repeatedly is obviously slower than calling read once, but it
Chris@0 113 * may be good enough if you don't want to allocate a buffer to
Chris@0 114 * read into.
Chris@0 115 */
Chris@0 116 T readOne(int R = 0);
Chris@0 117
Chris@0 118 /**
Chris@0 119 * Read n samples from the buffer, if available, for reader R,
Chris@0 120 * without advancing the read pointer -- i.e. a subsequent read()
Chris@0 121 * or skip() will be necessary to empty the buffer. If fewer than
Chris@0 122 * n are available, the remainder will be zeroed out. Returns the
Chris@0 123 * number of samples actually read.
Chris@0 124 */
Chris@928 125 int peek(T *destination, int n, int R = 0) const;
Chris@0 126
Chris@0 127 /**
Chris@0 128 * Read one sample from the buffer, if available, without
Chris@0 129 * advancing the read pointer -- i.e. a subsequent read() or
Chris@0 130 * skip() will be necessary to empty the buffer. Returns zero if
Chris@0 131 * no sample was available.
Chris@0 132 */
Chris@0 133 T peekOne(int R = 0) const;
Chris@0 134
Chris@0 135 /**
Chris@0 136 * Pretend to read n samples from the buffer, for reader R,
Chris@0 137 * without actually returning them (i.e. discard the next n
Chris@0 138 * samples). Returns the number of samples actually available for
Chris@0 139 * discarding.
Chris@0 140 */
Chris@928 141 int skip(int n, int R = 0);
Chris@0 142
Chris@0 143 /**
Chris@0 144 * Write n samples to the buffer. If insufficient space is
Chris@0 145 * available, not all samples may actually be written. Returns
Chris@0 146 * the number of samples actually written.
Chris@0 147 */
Chris@928 148 int write(const T *source, int n);
Chris@0 149
Chris@0 150 /**
Chris@0 151 * Write n zero-value samples to the buffer. If insufficient
Chris@0 152 * space is available, not all zeros may actually be written.
Chris@0 153 * Returns the number of zeroes actually written.
Chris@0 154 */
Chris@928 155 int zero(int n);
Chris@0 156
Chris@1827 157 RingBuffer(const RingBuffer &) =default;
Chris@1827 158 RingBuffer &operator=(const RingBuffer &) =default;
Chris@1827 159
Chris@0 160 protected:
Chris@1829 161 std::vector<T, breakfastquay::StlAllocator<T>> m_buffer;
Chris@1827 162 int m_writer;
Chris@1827 163 std::vector<int> m_readers;
Chris@1827 164 int m_size;
Chris@0 165 };
Chris@0 166
Chris@0 167 template <typename T, int N>
Chris@928 168 RingBuffer<T, N>::RingBuffer(int n) :
Chris@1827 169 m_buffer(n + 1, T()),
Chris@0 170 m_writer(0),
Chris@1827 171 m_readers(N, 0),
Chris@574 172 m_size(n + 1)
Chris@0 173 {
Chris@0 174 #ifdef DEBUG_RINGBUFFER
Chris@0 175 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::RingBuffer(" << n << ")" << std::endl;
Chris@0 176 #endif
Chris@0 177 }
Chris@0 178
Chris@0 179 template <typename T, int N>
Chris@0 180 RingBuffer<T, N>::~RingBuffer()
Chris@0 181 {
Chris@0 182 #ifdef DEBUG_RINGBUFFER
Chris@0 183 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::~RingBuffer" << std::endl;
Chris@0 184 #endif
Chris@0 185 }
Chris@0 186
Chris@0 187 template <typename T, int N>
Chris@928 188 int
Chris@0 189 RingBuffer<T, N>::getSize() const
Chris@0 190 {
Chris@0 191 #ifdef DEBUG_RINGBUFFER
Chris@0 192 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::getSize(): " << m_size-1 << std::endl;
Chris@0 193 #endif
Chris@0 194
Chris@0 195 return m_size - 1;
Chris@0 196 }
Chris@0 197
Chris@0 198 template <typename T, int N>
Chris@1827 199 RingBuffer<T, N>
Chris@928 200 RingBuffer<T, N>::resized(int newSize) const
Chris@0 201 {
Chris@0 202 #ifdef DEBUG_RINGBUFFER
Chris@835 203 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::resized(" << newSize << ")" << std::endl;
Chris@0 204 #endif
Chris@0 205
Chris@1827 206 RingBuffer<T, N> newBuffer(newSize);
Chris@0 207
Chris@835 208 int w = m_writer;
Chris@835 209 int r = m_readers[0];
Chris@835 210
Chris@835 211 while (r != w) {
Chris@835 212 T value = m_buffer[r];
Chris@835 213 newBuffer->write(&value, 1);
Chris@835 214 if (++r == m_size) r = 0;
Chris@0 215 }
Chris@0 216
Chris@835 217 return newBuffer;
Chris@0 218 }
Chris@0 219
Chris@0 220 template <typename T, int N>
Chris@0 221 void
Chris@0 222 RingBuffer<T, N>::reset()
Chris@0 223 {
Chris@0 224 #ifdef DEBUG_RINGBUFFER
Chris@0 225 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::reset" << std::endl;
Chris@0 226 #endif
Chris@0 227
Chris@0 228 m_writer = 0;
Chris@1827 229 for (int i = 0; i < N; ++i) {
Chris@1827 230 m_readers[i] = 0;
Chris@1827 231 }
Chris@0 232 }
Chris@0 233
Chris@0 234 template <typename T, int N>
Chris@928 235 int
Chris@0 236 RingBuffer<T, N>::getReadSpace(int R) const
Chris@0 237 {
Chris@928 238 int writer = m_writer;
Chris@928 239 int reader = m_readers[R];
Chris@928 240 int space = 0;
Chris@0 241
Chris@0 242 if (writer > reader) space = writer - reader;
Chris@0 243 else space = ((writer + m_size) - reader) % m_size;
Chris@0 244
Chris@0 245 #ifdef DEBUG_RINGBUFFER
Chris@0 246 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::getReadSpace(" << R << "): " << space << std::endl;
Chris@0 247 #endif
Chris@0 248
Chris@0 249 return space;
Chris@0 250 }
Chris@0 251
Chris@0 252 template <typename T, int N>
Chris@928 253 int
Chris@0 254 RingBuffer<T, N>::getWriteSpace() const
Chris@0 255 {
Chris@928 256 int space = 0;
Chris@0 257 for (int i = 0; i < N; ++i) {
Chris@1429 258 int here = (m_readers[i] + m_size - m_writer - 1) % m_size;
Chris@1429 259 if (i == 0 || here < space) space = here;
Chris@0 260 }
Chris@0 261
Chris@0 262 #ifdef DEBUG_RINGBUFFER
Chris@928 263 int rs(getReadSpace()), rp(m_readers[0]);
Chris@0 264
Chris@0 265 std::cerr << "RingBuffer: write space " << space << ", read space "
Chris@1429 266 << rs << ", total " << (space + rs) << ", m_size " << m_size << std::endl;
Chris@0 267 std::cerr << "RingBuffer: reader " << rp << ", writer " << m_writer << std::endl;
Chris@0 268 #endif
Chris@0 269
Chris@0 270 #ifdef DEBUG_RINGBUFFER
Chris@0 271 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::getWriteSpace(): " << space << std::endl;
Chris@0 272 #endif
Chris@0 273
Chris@0 274 return space;
Chris@0 275 }
Chris@0 276
Chris@0 277 template <typename T, int N>
Chris@928 278 int
Chris@928 279 RingBuffer<T, N>::read(T *destination, int n, int R)
Chris@0 280 {
Chris@0 281 #ifdef DEBUG_RINGBUFFER
Chris@0 282 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::read(dest, " << n << ", " << R << ")" << std::endl;
Chris@0 283 #endif
Chris@0 284
Chris@928 285 int available = getReadSpace(R);
Chris@0 286 if (n > available) {
Chris@0 287 #ifdef DEBUG_RINGBUFFER
Chris@1429 288 std::cerr << "WARNING: Only " << available << " samples available"
Chris@1429 289 << std::endl;
Chris@0 290 #endif
Chris@1827 291 breakfastquay::v_zero(destination + available, n - available);
Chris@1429 292 n = available;
Chris@0 293 }
Chris@0 294 if (n == 0) return n;
Chris@0 295
Chris@928 296 int here = m_size - m_readers[R];
Chris@0 297 if (here >= n) {
Chris@1827 298 breakfastquay::v_copy(destination, m_buffer.data() + m_readers[R], n);
Chris@0 299 } else {
Chris@1827 300 breakfastquay::v_copy(destination, m_buffer.data() + m_readers[R], here);
Chris@1827 301 breakfastquay::v_copy(destination + here, m_buffer.data(), n - here);
Chris@0 302 }
Chris@0 303
Chris@1553 304 BQ_MBARRIER();
Chris@0 305 m_readers[R] = (m_readers[R] + n) % m_size;
Chris@0 306
Chris@0 307 #ifdef DEBUG_RINGBUFFER
Chris@0 308 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::read: read " << n << ", reader now " << m_readers[R] << std::endl;
Chris@0 309 #endif
Chris@0 310
Chris@0 311 return n;
Chris@0 312 }
Chris@0 313
Chris@0 314 template <typename T, int N>
Chris@928 315 int
Chris@928 316 RingBuffer<T, N>::readAdding(T *destination, int n, int R)
Chris@0 317 {
Chris@0 318 #ifdef DEBUG_RINGBUFFER
Chris@0 319 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::readAdding(dest, " << n << ", " << R << ")" << std::endl;
Chris@0 320 #endif
Chris@0 321
Chris@928 322 int available = getReadSpace(R);
Chris@0 323 if (n > available) {
Chris@0 324 #ifdef DEBUG_RINGBUFFER
Chris@1429 325 std::cerr << "WARNING: Only " << available << " samples available"
Chris@1429 326 << std::endl;
Chris@0 327 #endif
Chris@1429 328 n = available;
Chris@0 329 }
Chris@0 330 if (n == 0) return n;
Chris@0 331
Chris@928 332 int here = m_size - m_readers[R];
Chris@0 333
Chris@0 334 if (here >= n) {
Chris@1429 335 for (int i = 0; i < n; ++i) {
Chris@1827 336 destination[i] += (m_buffer.data() + m_readers[R])[i];
Chris@1429 337 }
Chris@0 338 } else {
Chris@1429 339 for (int i = 0; i < here; ++i) {
Chris@1827 340 destination[i] += (m_buffer.data() + m_readers[R])[i];
Chris@1429 341 }
Chris@1429 342 for (int i = 0; i < (n - here); ++i) {
Chris@1429 343 destination[i + here] += m_buffer[i];
Chris@1429 344 }
Chris@0 345 }
Chris@0 346
Chris@1553 347 BQ_MBARRIER();
Chris@0 348 m_readers[R] = (m_readers[R] + n) % m_size;
Chris@0 349 return n;
Chris@0 350 }
Chris@0 351
Chris@0 352 template <typename T, int N>
Chris@0 353 T
Chris@0 354 RingBuffer<T, N>::readOne(int R)
Chris@0 355 {
Chris@0 356 #ifdef DEBUG_RINGBUFFER
Chris@0 357 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::readOne(" << R << ")" << std::endl;
Chris@0 358 #endif
Chris@0 359
Chris@0 360 if (m_writer == m_readers[R]) {
Chris@0 361 #ifdef DEBUG_RINGBUFFER
Chris@1429 362 std::cerr << "WARNING: No sample available"
Chris@1429 363 << std::endl;
Chris@0 364 #endif
Chris@1827 365 return T();
Chris@0 366 }
Chris@0 367 T value = m_buffer[m_readers[R]];
Chris@1553 368 BQ_MBARRIER();
Chris@0 369 if (++m_readers[R] == m_size) m_readers[R] = 0;
Chris@0 370 return value;
Chris@0 371 }
Chris@0 372
Chris@0 373 template <typename T, int N>
Chris@928 374 int
Chris@928 375 RingBuffer<T, N>::peek(T *destination, int n, int R) const
Chris@0 376 {
Chris@0 377 #ifdef DEBUG_RINGBUFFER
Chris@0 378 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::peek(dest, " << n << ", " << R << ")" << std::endl;
Chris@0 379 #endif
Chris@0 380
Chris@928 381 int available = getReadSpace(R);
Chris@0 382 if (n > available) {
Chris@0 383 #ifdef DEBUG_RINGBUFFER
Chris@1429 384 std::cerr << "WARNING: Only " << available << " samples available"
Chris@1429 385 << std::endl;
Chris@0 386 #endif
Chris@1827 387 breakfastquay::v_zero(destination + available, n - available);
Chris@1429 388 n = available;
Chris@0 389 }
Chris@0 390 if (n == 0) return n;
Chris@0 391
Chris@928 392 int here = m_size - m_readers[R];
Chris@0 393 if (here >= n) {
Chris@1827 394 breakfastquay::v_copy(destination, m_buffer.data() + m_readers[R], n);
Chris@0 395 } else {
Chris@1827 396 breakfastquay::v_copy(destination, m_buffer.data() + m_readers[R], here);
Chris@1827 397 breakfastquay::v_copy(destination + here, m_buffer.data(), n - here);
Chris@0 398 }
Chris@0 399
Chris@0 400 #ifdef DEBUG_RINGBUFFER
Chris@0 401 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::peek: read " << n << std::endl;
Chris@0 402 #endif
Chris@0 403
Chris@0 404 return n;
Chris@0 405 }
Chris@0 406
Chris@0 407 template <typename T, int N>
Chris@0 408 T
Chris@0 409 RingBuffer<T, N>::peekOne(int R) const
Chris@0 410 {
Chris@0 411 #ifdef DEBUG_RINGBUFFER
Chris@0 412 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::peek(" << R << ")" << std::endl;
Chris@0 413 #endif
Chris@0 414
Chris@0 415 if (m_writer == m_readers[R]) {
Chris@0 416 #ifdef DEBUG_RINGBUFFER
Chris@1429 417 std::cerr << "WARNING: No sample available"
Chris@1429 418 << std::endl;
Chris@0 419 #endif
Chris@1827 420 return T();
Chris@0 421 }
Chris@0 422 T value = m_buffer[m_readers[R]];
Chris@0 423 return value;
Chris@0 424 }
Chris@0 425
Chris@0 426 template <typename T, int N>
Chris@928 427 int
Chris@928 428 RingBuffer<T, N>::skip(int n, int R)
Chris@0 429 {
Chris@0 430 #ifdef DEBUG_RINGBUFFER
Chris@0 431 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::skip(" << n << ", " << R << ")" << std::endl;
Chris@0 432 #endif
Chris@0 433
Chris@928 434 int available = getReadSpace(R);
Chris@0 435 if (n > available) {
Chris@0 436 #ifdef DEBUG_RINGBUFFER
Chris@1429 437 std::cerr << "WARNING: Only " << available << " samples available"
Chris@1429 438 << std::endl;
Chris@0 439 #endif
Chris@1429 440 n = available;
Chris@0 441 }
Chris@0 442 if (n == 0) return n;
Chris@0 443 m_readers[R] = (m_readers[R] + n) % m_size;
Chris@0 444 return n;
Chris@0 445 }
Chris@0 446
Chris@0 447 template <typename T, int N>
Chris@928 448 int
Chris@928 449 RingBuffer<T, N>::write(const T *source, int n)
Chris@0 450 {
Chris@0 451 #ifdef DEBUG_RINGBUFFER
Chris@0 452 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::write(" << n << ")" << std::endl;
Chris@0 453 #endif
Chris@0 454
Chris@928 455 int available = getWriteSpace();
Chris@0 456 if (n > available) {
Chris@0 457 #ifdef DEBUG_RINGBUFFER
Chris@1429 458 std::cerr << "WARNING: Only room for " << available << " samples"
Chris@1429 459 << std::endl;
Chris@0 460 #endif
Chris@1429 461 n = available;
Chris@0 462 }
Chris@0 463 if (n == 0) return n;
Chris@0 464
Chris@928 465 int here = m_size - m_writer;
Chris@0 466 if (here >= n) {
Chris@1827 467 breakfastquay::v_copy(m_buffer.data() + m_writer, source, n);
Chris@0 468 } else {
Chris@1827 469 breakfastquay::v_copy(m_buffer.data() + m_writer, source, here);
Chris@1827 470 breakfastquay::v_copy(m_buffer.data(), source + here, n - here);
Chris@0 471 }
Chris@0 472
Chris@1553 473 BQ_MBARRIER();
Chris@0 474 m_writer = (m_writer + n) % m_size;
Chris@0 475
Chris@0 476 #ifdef DEBUG_RINGBUFFER
Chris@0 477 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::write: wrote " << n << ", writer now " << m_writer << std::endl;
Chris@0 478 #endif
Chris@0 479
Chris@0 480 return n;
Chris@0 481 }
Chris@0 482
Chris@0 483 template <typename T, int N>
Chris@928 484 int
Chris@928 485 RingBuffer<T, N>::zero(int n)
Chris@0 486 {
Chris@0 487 #ifdef DEBUG_RINGBUFFER
Chris@0 488 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::zero(" << n << ")" << std::endl;
Chris@0 489 #endif
Chris@0 490
Chris@928 491 int available = getWriteSpace();
Chris@0 492 if (n > available) {
Chris@0 493 #ifdef DEBUG_RINGBUFFER
Chris@1429 494 std::cerr << "WARNING: Only room for " << available << " samples"
Chris@1429 495 << std::endl;
Chris@0 496 #endif
Chris@1429 497 n = available;
Chris@0 498 }
Chris@0 499 if (n == 0) return n;
Chris@0 500
Chris@928 501 int here = m_size - m_writer;
Chris@0 502 if (here >= n) {
Chris@1827 503 breakfastquay::v_zero(m_buffer.data() + m_writer, n);
Chris@0 504 } else {
Chris@1827 505 breakfastquay::v_zero(m_buffer.data() + m_writer, here);
Chris@1827 506 breakfastquay::v_zero(m_buffer.data(), n - here);
Chris@0 507 }
Chris@835 508
Chris@1553 509 BQ_MBARRIER();
Chris@0 510 m_writer = (m_writer + n) % m_size;
Chris@0 511 return n;
Chris@0 512 }
Chris@0 513
Chris@0 514 #endif // _RINGBUFFER_H_