/[svn]/linuxsampler/trunk/src/common/RingBuffer.h
ViewVC logotype

Diff of /linuxsampler/trunk/src/common/RingBuffer.h

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 56 by schoenebeck, Tue Apr 27 09:21:58 2004 UTC revision 970 by schoenebeck, Wed Dec 6 22:28:17 2006 UTC
# Line 3  Line 3 
3   *   LinuxSampler - modular, streaming capable sampler                     *   *   LinuxSampler - modular, streaming capable sampler                     *
4   *                                                                         *   *                                                                         *
5   *   Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck   *   *   Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck   *
6     *   Copyright (C) 2005, 2006 Christian Schoenebeck                        *
7   *                                                                         *   *                                                                         *
8   *   This program is free software; you can redistribute it and/or modify  *   *   This program is free software; you can redistribute it and/or modify  *
9   *   it under the terms of the GNU General Public License as published by  *   *   it under the terms of the GNU General Public License as published by  *
# Line 29  Line 30 
30    
31  #include "atomic.h"  #include "atomic.h"
32    
33  template<class T>  /** @brief Real-time safe and type safe RingBuffer implementation.
34     *
35     * This constant size buffer can be used to send data from exactly one
36     * sender / writing thread to exactly one receiver / reading thread. It is
37     * real-time safe due to the fact that data is only allocated when this
38     * RingBuffer is created and no system level mechanisms are used for
39     * ensuring thread safety of this class.
40     *
41     * <b>Important:</b> There are two distinct behaviors of this RingBuffer
42     * which has to be given as template argument @c T_DEEP_COPY, which is a
43     * boolean flag:
44     *
45     * - @c true: The RingBuffer will copy elements of type @c T by using type
46     *   @c T's assignment operator. This behavior is mandatory for all data
47     *   structures (classes) which additionally allocate memory on the heap.
48     *   Type @c T's needs to have an assignment operator implementation though,
49     *   otherwise this will cause a compilation error. This behavior is more
50     *   safe, but usually slower (except for very small buffer sizes, where it
51     *   might be even faster).
52     * - @c false: The RingBuffer will copy elements of type @c T by flatly
53     *   copying their structural data ( i.e. with @c memcpy() ) in one piece.
54     *   This will only work if class @c T (and all of its subelements) does not
55     *   allocate any additional data on the heap by itself. So use this option
56     *   with great care, because otherwise it will result in very ugly behavior
57     *   and crashes! For larger buffer sizes, this behavior will most probably
58     *   be faster.
59     */
60    template<class T, bool T_DEEP_COPY>
61  class RingBuffer  class RingBuffer
62  {  {
63  public:  public:
# Line 58  public: Line 86  public:
86       * Sets all remaining write space elements to zero. The write pointer       * Sets all remaining write space elements to zero. The write pointer
87       * will currently not be incremented after, but that might change in       * will currently not be incremented after, but that might change in
88       * future.       * future.
89         *
90         * @e Caution: for @c T_DEEP_COPY=true you might probably @e NOT want
91         * to call this method at all, at least not in case type @c T allocates
92         * any additional data on the heap by itself.
93       */       */
94      inline void fill_write_space_with_null() {      inline void fill_write_space_with_null() {
95               int w = atomic_read(&write_ptr),               int w = atomic_read(&write_ptr),
# Line 122  public: Line 154  public:
154                 w += cnt;                 w += cnt;
155                 if(w >= size) {                 if(w >= size) {
156                   w -= size;                   w -= size;
157                   memcpy(&buf[0], &buf[size], w*sizeof(T));                   copy(&buf[0], &buf[size], w);
158  //printf("DEBUG !!!! increment_write_ptr_with_wrap: buffer wrapped, elements wrapped = %d (wrap_elements %d)\n",w,wrap_elements);  //printf("DEBUG !!!! increment_write_ptr_with_wrap: buffer wrapped, elements wrapped = %d (wrap_elements %d)\n",w,wrap_elements);
159                 }                 }
160                 atomic_set(&write_ptr, w);                 atomic_set(&write_ptr, w);
# Line 249  public: Line 281  public:
281      int size;      int size;
282      int wrap_elements;      int wrap_elements;
283    
284        /**
285         * Independent, random access reading from a RingBuffer. This class
286         * allows to read from a RingBuffer without being forced to free read
287         * data while reading / positioning.
288         */
289        template<class T1, bool T1_DEEP_COPY>
290        class _NonVolatileReader {
291            public:
292                int read_space() {
293                    int r = read_ptr;
294                    int w = atomic_read(&pBuf->write_ptr);
295                    return (w >= r) ? w - r : (w - r + pBuf->size) & pBuf->size_mask;
296                }
297    
298                /**
299                 * Prefix decrement operator, for reducing NonVolatileReader's
300                 * read position by one.
301                 */
302                inline void operator--() {
303                    if (read_ptr == atomic_read(&pBuf->read_ptr)) return; //TODO: or should we react oh this case (e.g. force segfault), as this is a very odd case?
304                    --read_ptr & pBuf->size_mask;
305                }
306    
307                /**
308                 * Postfix decrement operator, for reducing NonVolatileReader's
309                 * read position by one.
310                 */
311                inline void operator--(int) {
312                    --*this;
313                }
314    
315                /**
316                 * Returns pointer to the RingBuffer data of current
317                 * NonVolatileReader's read position and increments
318                 * NonVolatileReader's read position by one.
319                 *
320                 * @returns pointer to element of current read position
321                 */
322                T* pop() {
323                    if (!read_space()) return NULL;
324                    T* pData = &pBuf->buf[read_ptr];
325                    read_ptr++;
326                    read_ptr &= pBuf->size_mask;
327                    return pData;
328                }
329    
330                /**
331                 * Reads one element from the NonVolatileReader's current read
332                 * position and copies it to the variable pointed by \a dst and
333                 * finally increments the NonVolatileReader's read position by
334                 * one.
335                 *
336                 * @param dst - where the element is copied to
337                 * @returns 1 on success, 0 otherwise
338                 */
339                int pop(T* dst) { return read(dst,1); }
340    
341                /**
342                 * Reads \a cnt elements from the NonVolatileReader's current
343                 * read position and copies it to the buffer pointed by \a dest
344                 * and finally increments the NonVolatileReader's read position
345                 * by the number of read elements.
346                 *
347                 * @param dest - destination buffer
348                 * @param cnt  - number of elements to read
349                 * @returns number of read elements
350                 */
351                int read(T* dest, int cnt) {
352                    int free_cnt;
353                    int cnt2;
354                    int to_read;
355                    int n1, n2;
356                    int priv_read_ptr;
357    
358                    priv_read_ptr = read_ptr;
359    
360                    if ((free_cnt = read_space()) == 0) return 0;
361    
362                    to_read = cnt > free_cnt ? free_cnt : cnt;
363    
364                    cnt2 = priv_read_ptr + to_read;
365    
366                    if (cnt2 > pBuf->size) {
367                        n1 = pBuf->size - priv_read_ptr;
368                        n2 = cnt2 & pBuf->size_mask;
369                    } else {
370                        n1 = to_read;
371                        n2 = 0;
372                    }
373    
374                    copy(dest, &pBuf->buf[priv_read_ptr], n1);
375                    priv_read_ptr = (priv_read_ptr + n1) & pBuf->size_mask;
376    
377                    if (n2) {
378                        copy(dest+n1, pBuf->buf, n2);
379                        priv_read_ptr = n2;
380                    }
381    
382                    this->read_ptr = priv_read_ptr;
383                    return to_read;
384                }
385    
386                /**
387                 * Finally when the read data is not needed anymore, this method
388                 * should be called to free the data in the RingBuffer up to the
389                 * current read position of this NonVolatileReader.
390                 *
391                 * @see RingBuffer::increment_read_ptr()
392                 */
393                void free() {
394                    atomic_set(&pBuf->read_ptr, read_ptr);
395                }
396    
397            protected:
398                _NonVolatileReader(RingBuffer<T1,T1_DEEP_COPY>* pBuf) {
399                    this->pBuf     = pBuf;
400                    this->read_ptr = atomic_read(&pBuf->read_ptr);
401                }
402    
403                RingBuffer<T1,T1_DEEP_COPY>* pBuf;
404                int read_ptr;
405    
406                friend class RingBuffer<T1,T1_DEEP_COPY>;
407        };
408    
409        typedef _NonVolatileReader<T,T_DEEP_COPY> NonVolatileReader;
410    
411        NonVolatileReader get_non_volatile_reader() { return NonVolatileReader(this); }
412    
413    protected:    protected:
414      T *buf;      T *buf;
415      atomic_t write_ptr;      atomic_t write_ptr;
416      atomic_t read_ptr;      atomic_t read_ptr;
417      int size_mask;      int size_mask;
418    
419        /**
420         * Copies \a n amount of elements from the buffer given by
421         * \a pSrc to the buffer given by \a pDst.
422         */
423        inline static void copy(T* pDst, T* pSrc, int n);
424    
425        friend class _NonVolatileReader<T,T_DEEP_COPY>;
426  };  };
427    
428  template<class T> T *  template<class T, bool T_DEEP_COPY>
429  RingBuffer<T>::get_write_ptr (void) {  T* RingBuffer<T,T_DEEP_COPY>::get_write_ptr (void) {
430    return(&buf[atomic_read(&write_ptr)]);    return(&buf[atomic_read(&write_ptr)]);
431  }  }
432    
433  template<class T> T *  template<class T, bool T_DEEP_COPY>
434  RingBuffer<T>::get_buffer_begin (void) {  T* RingBuffer<T,T_DEEP_COPY>::get_buffer_begin (void) {
435    return(buf);    return(buf);
436  }  }
437    
438    
439    
440  template<class T> int  template<class T, bool T_DEEP_COPY>
441  RingBuffer<T>::read (T *dest, int cnt)  int RingBuffer<T,T_DEEP_COPY>::read(T* dest, int cnt)
   
442  {  {
443          int free_cnt;          int free_cnt;
444          int cnt2;          int cnt2;
# Line 296  RingBuffer<T>::read (T *dest, int cnt) Line 464  RingBuffer<T>::read (T *dest, int cnt)
464                  n2 = 0;                  n2 = 0;
465          }          }
466    
467          memcpy (dest, &buf[priv_read_ptr], n1 * sizeof (T));          copy(dest, &buf[priv_read_ptr], n1);
468          priv_read_ptr = (priv_read_ptr + n1) & size_mask;          priv_read_ptr = (priv_read_ptr + n1) & size_mask;
469    
470          if (n2) {          if (n2) {
471                  memcpy (dest+n1, buf, n2 * sizeof (T));                  copy(dest+n1, buf, n2);
472                  priv_read_ptr = n2;                  priv_read_ptr = n2;
473          }          }
474    
# Line 308  RingBuffer<T>::read (T *dest, int cnt) Line 476  RingBuffer<T>::read (T *dest, int cnt)
476          return to_read;          return to_read;
477  }  }
478    
479  template<class T> int  template<class T, bool T_DEEP_COPY>
480  RingBuffer<T>::write (T *src, int cnt)  int RingBuffer<T,T_DEEP_COPY>::write(T* src, int cnt)
   
481  {  {
482          int free_cnt;          int free_cnt;
483          int cnt2;          int cnt2;
# Line 336  RingBuffer<T>::write (T *src, int cnt) Line 503  RingBuffer<T>::write (T *src, int cnt)
503                  n2 = 0;                  n2 = 0;
504          }          }
505    
506          memcpy (&buf[priv_write_ptr], src, n1 * sizeof (T));          copy(&buf[priv_write_ptr], src, n1);
507          priv_write_ptr = (priv_write_ptr + n1) & size_mask;          priv_write_ptr = (priv_write_ptr + n1) & size_mask;
508    
509          if (n2) {          if (n2) {
510                  memcpy (buf, src+n1, n2 * sizeof (T));                  copy(buf, src+n1, n2);
511                  priv_write_ptr = n2;                  priv_write_ptr = n2;
512          }          }
513          atomic_set(&write_ptr, priv_write_ptr);          atomic_set(&write_ptr, priv_write_ptr);
514          return to_write;          return to_write;
515  }  }
516    
517    template<class T, bool T_DEEP_COPY>
518    void RingBuffer<T,T_DEEP_COPY>::copy(T* pDst, T* pSrc, int n) {
519        if (T_DEEP_COPY) { // deep copy - won't work for data structures without assignment operator implementation
520            for (int i = 0; i < n; i++) pDst[i] = pSrc[i];
521        } else { // flat copy - won't work for complex data structures !
522            memcpy(pDst, pSrc, n * sizeof(T));
523        }
524    }
525    
526  #endif /* RINGBUFFER_H */  #endif /* RINGBUFFER_H */

Legend:
Removed from v.56  
changed lines
  Added in v.970

  ViewVC Help
Powered by ViewVC