File: BufferedStreamReader.h

package info (click to toggle)
audacity 3.7.3%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 125,252 kB
  • sloc: cpp: 358,238; ansic: 75,458; lisp: 7,761; sh: 3,410; python: 1,503; xml: 1,385; perl: 854; makefile: 122
file content (112 lines) | stat: -rw-r--r-- 3,317 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
/**********************************************************************

  Audacity: A Digital Audio Editor

  BufferedStreamReader.h

  Dmitry Vedenko

**********************************************************************/

#pragma once

#include <vector>
#include <cstddef>
#include <cstdint>
#include <type_traits>

/*!
 * \brief A facade-like class, that implements buffered reading from the underlying data stream.
 *
 * BufferedStreamReader provides optimal read performance for the built-in types of up to 8 bytes long.
 */
class UTILITY_API BufferedStreamReader /* not final */
{
public:
   static constexpr size_t RequiredAlignment = 8;

   explicit BufferedStreamReader(size_t bufferSize = 4096);

   //! Read up to maxBytes into the buffer. Returns the number of bytes read.
   size_t Read(void* buffer, size_t maxBytes);

   //! Read a single value of ValueType, where sizeof(ValueType) <= 8 and value is aligned to the size boundary
   template<typename ValueType>
   std::enable_if_t<sizeof(ValueType) <= RequiredAlignment, bool>
   ReadValue(ValueType& value) /* ReadData may throw */
   {
      constexpr size_t valueSize = sizeof(ValueType);
      const size_t availableBytes = mCurrentBytes - mCurrentIndex;

      // In case of underflow - just fall back to general Read routine
      if (availableBytes < valueSize)
         return valueSize == Read(&value, valueSize);

      // Special case for one bytes reads
      if constexpr (valueSize == 1)
         value = mBufferStart[mCurrentIndex];
      else
         value = UncheckedRead<ValueType>();

      mCurrentIndex += valueSize;

      return true;
   }

   //! Returns true if there is no more data available
   bool Eof() const;

   //! Read a single byte from the stream. Return -1 on failure. 
   int GetC();

protected:
   //! Should return true, if underlying stream has more data
   virtual bool HasMoreData() const = 0;
   //! Read up to maxBytes into the buffer. Should return the number of bytes read.
   virtual size_t ReadData(void* buffer, size_t maxBytes) = 0;

private:
   //! Reads the data from the stream if there is not enough data buffered
   bool HandleUnderflow();

   /*! Reads a value of type T from the stream.
    * @pre mCurrentIndex + sizeof(T) <= mCurrentBytes
    */
   template<typename T>
   T UncheckedRead() noexcept
   {
      T result;

      // We know, that mBufferStart is always at least RequiredAlignment aligned
      // Only mCurrentIndex alignment matters here

      if ((mCurrentIndex % sizeof(T)) == 0)
      {
         // If the result is aligned - just read it from the memory
         const void* ptr = mBufferStart + mCurrentIndex;
         result = *static_cast<const T*>(ptr);
      }
      else
      {
         // Fallback to std::copy otherwise
         const uint8_t* begin = mBufferStart + mCurrentIndex;
         const uint8_t* end = begin + sizeof(T);
         void* out = &result;

         std::copy(begin, end, static_cast<uint8_t*>(out));
      }

      return result;
   }

   // Cache storage
   std::vector<uint8_t> mBufferData;

   // mBufferStart is aligned to RequiredAlignment bytes boundary.
   // Both values below are initialized in the constructor.
   uint8_t* mBufferStart;
   size_t mBufferSize;

   size_t mCurrentIndex { 0 };
   size_t mCurrentBytes { 0 };
};