OpenShot Audio Library | OpenShotAudio  0.6.0
juce_BufferedInputStream.cpp
1 /*
2  ==============================================================================
3 
4  This file is part of the JUCE library.
5  Copyright (c) 2022 - Raw Material Software Limited
6 
7  JUCE is an open source library subject to commercial or open-source
8  licensing.
9 
10  The code included in this file is provided under the terms of the ISC license
11  http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12  To use, copy, modify, and/or distribute this software for any purpose with or
13  without fee is hereby granted provided that the above copyright notice and
14  this permission notice appear in all copies.
15 
16  JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17  EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18  DISCLAIMED.
19 
20  ==============================================================================
21 */
22 
23 namespace juce
24 {
25 
26 static int calcBufferStreamBufferSize (int requestedSize, InputStream* source) noexcept
27 {
28  // You need to supply a real stream when creating a BufferedInputStream
29  jassert (source != nullptr);
30 
31  requestedSize = jmax (256, requestedSize);
32  auto sourceSize = source->getTotalLength();
33 
34  if (sourceSize >= 0 && sourceSize < requestedSize)
35  return jmax (32, (int) sourceSize);
36 
37  return requestedSize;
38 }
39 
40 //==============================================================================
41 BufferedInputStream::BufferedInputStream (InputStream* sourceStream, int size, bool takeOwnership)
42  : source (sourceStream, takeOwnership),
43  bufferedRange (sourceStream->getPosition(), sourceStream->getPosition()),
44  position (bufferedRange.getStart()),
45  bufferLength (calcBufferStreamBufferSize (size, sourceStream))
46 {
47  buffer.malloc (bufferLength);
48 }
49 
51  : BufferedInputStream (&sourceStream, size, false)
52 {
53 }
54 
56 
57 //==============================================================================
59 {
60  if (! ensureBuffered())
61  return 0;
62 
63  return position < lastReadPos ? buffer[(int) (position - bufferedRange.getStart())] : 0;
64 }
65 
67 {
68  return source->getTotalLength();
69 }
70 
72 {
73  return position;
74 }
75 
76 bool BufferedInputStream::setPosition (int64 newPosition)
77 {
78  position = jmax ((int64) 0, newPosition);
79  return true;
80 }
81 
83 {
84  return position >= lastReadPos && source->isExhausted();
85 }
86 
87 bool BufferedInputStream::ensureBuffered()
88 {
89  auto bufferEndOverlap = lastReadPos - bufferOverlap;
90 
91  if (position < bufferedRange.getStart() || position >= bufferEndOverlap)
92  {
93  int bytesRead = 0;
94 
95  if (position < lastReadPos
96  && position >= bufferEndOverlap
97  && position >= bufferedRange.getStart())
98  {
99  auto bytesToKeep = (int) (lastReadPos - position);
100  memmove (buffer, buffer + (int) (position - bufferedRange.getStart()), (size_t) bytesToKeep);
101 
102  bytesRead = source->read (buffer + bytesToKeep,
103  (int) (bufferLength - bytesToKeep));
104 
105  if (bytesRead < 0)
106  return false;
107 
108  lastReadPos += bytesRead;
109  bytesRead += bytesToKeep;
110  }
111  else
112  {
113  if (! source->setPosition (position))
114  return false;
115 
116  bytesRead = (int) source->read (buffer, (size_t) bufferLength);
117 
118  if (bytesRead < 0)
119  return false;
120 
121  lastReadPos = position + bytesRead;
122  }
123 
124  bufferedRange = Range<int64> (position, lastReadPos);
125 
126  while (bytesRead < bufferLength)
127  buffer[bytesRead++] = 0;
128  }
129 
130  return true;
131 }
132 
133 int BufferedInputStream::read (void* destBuffer, const int maxBytesToRead)
134 {
135  const auto initialPosition = position;
136 
137  const auto getBufferedRange = [this] { return bufferedRange; };
138 
139  const auto readFromReservoir = [this, &destBuffer, &initialPosition] (const Range<int64> rangeToRead)
140  {
141  memcpy (static_cast<char*> (destBuffer) + (rangeToRead.getStart() - initialPosition),
142  buffer + (rangeToRead.getStart() - bufferedRange.getStart()),
143  (size_t) rangeToRead.getLength());
144  };
145 
146  const auto fillReservoir = [this] (int64 requestedStart)
147  {
148  position = requestedStart;
149  ensureBuffered();
150  };
151 
152  const auto remaining = Reservoir::doBufferedRead (Range<int64> (position, position + maxBytesToRead),
153  getBufferedRange,
154  readFromReservoir,
155  fillReservoir);
156 
157  const auto bytesRead = maxBytesToRead - remaining.getLength();
158  position = remaining.getStart();
159  return (int) bytesRead;
160 }
161 
163 {
164  if (position >= bufferedRange.getStart()
165  && position < lastReadPos)
166  {
167  auto maxChars = (int) (lastReadPos - position);
168  auto* src = buffer + (int) (position - bufferedRange.getStart());
169 
170  for (int i = 0; i < maxChars; ++i)
171  {
172  if (src[i] == 0)
173  {
174  position += i + 1;
175  return String::fromUTF8 (src, i);
176  }
177  }
178  }
179 
180  return InputStream::readString();
181 }
182 
183 
184 //==============================================================================
185 //==============================================================================
186 #if JUCE_UNIT_TESTS
187 
188 struct BufferedInputStreamTests final : public UnitTest
189 {
190  template <typename Fn, size_t... Ix, typename Values>
191  static void applyImpl (Fn&& fn, std::index_sequence<Ix...>, Values&& values)
192  {
193  fn (std::get<Ix> (values)...);
194  }
195 
196  template <typename Fn, typename... Values>
197  static void apply (Fn&& fn, std::tuple<Values...> values)
198  {
199  applyImpl (fn, std::make_index_sequence<sizeof... (Values)>(), values);
200  }
201 
202  template <typename Fn, typename Values>
203  static void allCombinationsImpl (Fn&& fn, Values&& values)
204  {
205  apply (fn, values);
206  }
207 
208  template <typename Fn, typename Values, typename Range, typename... Ranges>
209  static void allCombinationsImpl (Fn&& fn, Values&& values, Range&& range, Ranges&&... ranges)
210  {
211  for (auto& item : range)
212  allCombinationsImpl (fn, std::tuple_cat (values, std::tie (item)), ranges...);
213  }
214 
215  template <typename Fn, typename... Ranges>
216  static void allCombinations (Fn&& fn, Ranges&&... ranges)
217  {
218  allCombinationsImpl (fn, std::tie(), ranges...);
219  }
220 
221  BufferedInputStreamTests()
222  : UnitTest ("BufferedInputStream", UnitTestCategories::streams)
223  {}
224 
225  void runTest() override
226  {
227  const MemoryBlock testBufferA ("abcdefghijklmnopqrstuvwxyz", 26);
228 
229  const auto testBufferB = [&]
230  {
231  MemoryBlock mb { 8192 };
232  auto r = getRandom();
233 
234  std::for_each (mb.begin(), mb.end(), [&] (char& item)
235  {
236  item = (char) r.nextInt (std::numeric_limits<char>::max());
237  });
238 
239  return mb;
240  }();
241 
242  const MemoryBlock buffers[] { testBufferA, testBufferB };
243  const int readSizes[] { 3, 10, 50 };
244  const bool shouldPeek[] { false, true };
245 
246  const auto runTest = [this] (const MemoryBlock& data, const int readSize, const bool peek)
247  {
248  MemoryInputStream mi (data, true);
249 
250  BufferedInputStream stream (mi, jmin (200, (int) data.getSize()));
251 
252  beginTest ("Read");
253 
254  expectEquals (stream.getPosition(), (int64) 0);
255  expectEquals (stream.getTotalLength(), (int64) data.getSize());
256  expectEquals (stream.getNumBytesRemaining(), stream.getTotalLength());
257  expect (! stream.isExhausted());
258 
259  size_t numBytesRead = 0;
260  MemoryBlock readBuffer (data.getSize());
261 
262  while (numBytesRead < data.getSize())
263  {
264  if (peek)
265  expectEquals (stream.peekByte(), *(char*) (data.begin() + numBytesRead));
266 
267  const auto startingPos = numBytesRead;
268  numBytesRead += (size_t) stream.read (readBuffer.begin() + numBytesRead, readSize);
269 
270  expect (std::equal (readBuffer.begin() + startingPos,
271  readBuffer.begin() + numBytesRead,
272  data.begin() + startingPos,
273  data.begin() + numBytesRead));
274  expectEquals (stream.getPosition(), (int64) numBytesRead);
275  expectEquals (stream.getNumBytesRemaining(), (int64) (data.getSize() - numBytesRead));
276  expect (stream.isExhausted() == (numBytesRead == data.getSize()));
277  }
278 
279  expectEquals (stream.getPosition(), (int64) data.getSize());
280  expectEquals (stream.getNumBytesRemaining(), (int64) 0);
281  expect (stream.isExhausted());
282 
283  expect (readBuffer == data);
284 
285  beginTest ("Skip");
286 
287  stream.setPosition (0);
288  expectEquals (stream.getPosition(), (int64) 0);
289  expectEquals (stream.getTotalLength(), (int64) data.getSize());
290  expectEquals (stream.getNumBytesRemaining(), stream.getTotalLength());
291  expect (! stream.isExhausted());
292 
293  numBytesRead = 0;
294  const int numBytesToSkip = 5;
295 
296  while (numBytesRead < data.getSize())
297  {
298  expectEquals (stream.peekByte(), *(char*) (data.begin() + numBytesRead));
299 
300  stream.skipNextBytes (numBytesToSkip);
301  numBytesRead += numBytesToSkip;
302  numBytesRead = std::min (numBytesRead, data.getSize());
303 
304  expectEquals (stream.getPosition(), (int64) numBytesRead);
305  expectEquals (stream.getNumBytesRemaining(), (int64) (data.getSize() - numBytesRead));
306  expect (stream.isExhausted() == (numBytesRead == data.getSize()));
307  }
308 
309  expectEquals (stream.getPosition(), (int64) data.getSize());
310  expectEquals (stream.getNumBytesRemaining(), (int64) 0);
311  expect (stream.isExhausted());
312  };
313 
314  allCombinations (runTest, buffers, readSizes, shouldPeek);
315  }
316 };
317 
318 static BufferedInputStreamTests bufferedInputStreamTests;
319 
320 #endif
321 
322 } // namespace juce
BufferedInputStream(InputStream *sourceStream, int bufferSize, bool deleteSourceWhenDestroyed)
int read(void *destBuffer, int maxBytesToRead) override
bool setPosition(int64 newPosition) override
void malloc(SizeType newNumElements, size_t elementSize=sizeof(ElementType))
virtual String readString()
constexpr ValueType getStart() const noexcept
Definition: juce_Range.h:80
static String fromUTF8(const char *utf8buffer, int bufferSizeBytes=-1)
static Range< Index > doBufferedRead(Range< Index > rangeToRead, GetBufferedRange &&getBufferedRange, ReadFromReservoir &&readFromReservoir, FillReservoir &&fillReservoir)