1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - 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 
MemoryInputStream(const void * sourceData,size_t sourceDataSize,bool keepCopy)26 MemoryInputStream::MemoryInputStream (const void* sourceData, size_t sourceDataSize, bool keepCopy)
27     : data (sourceData),
28       dataSize (sourceDataSize)
29 {
30     if (keepCopy)
31     {
32         internalCopy = MemoryBlock (sourceData, sourceDataSize);
33         data = internalCopy.getData();
34     }
35 }
36 
MemoryInputStream(const MemoryBlock & sourceData,bool keepCopy)37 MemoryInputStream::MemoryInputStream (const MemoryBlock& sourceData, bool keepCopy)
38     : data (sourceData.getData()),
39       dataSize (sourceData.getSize())
40 {
41     if (keepCopy)
42     {
43         internalCopy = sourceData;
44         data = internalCopy.getData();
45     }
46 }
47 
MemoryInputStream(MemoryBlock && source)48 MemoryInputStream::MemoryInputStream (MemoryBlock&& source)
49     : internalCopy (std::move (source))
50 {
51     data = internalCopy.getData();
52     dataSize = internalCopy.getSize();
53 }
54 
~MemoryInputStream()55 MemoryInputStream::~MemoryInputStream()
56 {
57 }
58 
getTotalLength()59 int64 MemoryInputStream::getTotalLength()
60 {
61     return (int64) dataSize;
62 }
63 
read(void * buffer,int howMany)64 int MemoryInputStream::read (void* buffer, int howMany)
65 {
66     jassert (buffer != nullptr && howMany >= 0);
67 
68     if (howMany <= 0 || position >= dataSize)
69         return 0;
70 
71     auto num = jmin ((size_t) howMany, dataSize - position);
72 
73     if (num > 0)
74     {
75         memcpy (buffer, addBytesToPointer (data, position), num);
76         position += num;
77     }
78 
79     return (int) num;
80 }
81 
isExhausted()82 bool MemoryInputStream::isExhausted()
83 {
84     return position >= dataSize;
85 }
86 
setPosition(const int64 pos)87 bool MemoryInputStream::setPosition (const int64 pos)
88 {
89     position = (size_t) jlimit ((int64) 0, (int64) dataSize, pos);
90     return true;
91 }
92 
getPosition()93 int64 MemoryInputStream::getPosition()
94 {
95     return (int64) position;
96 }
97 
skipNextBytes(int64 numBytesToSkip)98 void MemoryInputStream::skipNextBytes (int64 numBytesToSkip)
99 {
100     if (numBytesToSkip > 0)
101         setPosition (getPosition() + numBytesToSkip);
102 }
103 
104 
105 //==============================================================================
106 //==============================================================================
107 #if JUCE_UNIT_TESTS
108 
109 class MemoryStreamTests  : public UnitTest
110 {
111 public:
MemoryStreamTests()112     MemoryStreamTests()
113         : UnitTest ("MemoryInputStream & MemoryOutputStream", UnitTestCategories::streams)
114     {}
115 
runTest()116     void runTest() override
117     {
118         beginTest ("Basics");
119         Random r = getRandom();
120 
121         int randomInt = r.nextInt();
122         int64 randomInt64 = r.nextInt64();
123         double randomDouble = r.nextDouble();
124         String randomString (createRandomWideCharString (r));
125 
126         MemoryOutputStream mo;
127         mo.writeInt (randomInt);
128         mo.writeIntBigEndian (randomInt);
129         mo.writeCompressedInt (randomInt);
130         mo.writeString (randomString);
131         mo.writeInt64 (randomInt64);
132         mo.writeInt64BigEndian (randomInt64);
133         mo.writeDouble (randomDouble);
134         mo.writeDoubleBigEndian (randomDouble);
135 
136         MemoryInputStream mi (mo.getData(), mo.getDataSize(), false);
137         expect (mi.readInt() == randomInt);
138         expect (mi.readIntBigEndian() == randomInt);
139         expect (mi.readCompressedInt() == randomInt);
140         expectEquals (mi.readString(), randomString);
141         expect (mi.readInt64() == randomInt64);
142         expect (mi.readInt64BigEndian() == randomInt64);
143         expect (mi.readDouble() == randomDouble);
144         expect (mi.readDoubleBigEndian() == randomDouble);
145 
146         const MemoryBlock data ("abcdefghijklmnopqrstuvwxyz", 26);
147         MemoryInputStream stream (data, true);
148 
149         beginTest ("Read");
150 
151         expectEquals (stream.getPosition(), (int64) 0);
152         expectEquals (stream.getTotalLength(), (int64) data.getSize());
153         expectEquals (stream.getNumBytesRemaining(), stream.getTotalLength());
154         expect (! stream.isExhausted());
155 
156         size_t numBytesRead = 0;
157         MemoryBlock readBuffer (data.getSize());
158 
159         while (numBytesRead < data.getSize())
160         {
161             numBytesRead += (size_t) stream.read (&readBuffer[numBytesRead], 3);
162 
163             expectEquals (stream.getPosition(), (int64) numBytesRead);
164             expectEquals (stream.getNumBytesRemaining(), (int64) (data.getSize() - numBytesRead));
165             expect (stream.isExhausted() == (numBytesRead == data.getSize()));
166         }
167 
168         expectEquals (stream.getPosition(), (int64) data.getSize());
169         expectEquals (stream.getNumBytesRemaining(), (int64) 0);
170         expect (stream.isExhausted());
171 
172         expect (readBuffer == data);
173 
174         beginTest ("Skip");
175 
176         stream.setPosition (0);
177         expectEquals (stream.getPosition(), (int64) 0);
178         expectEquals (stream.getTotalLength(), (int64) data.getSize());
179         expectEquals (stream.getNumBytesRemaining(), stream.getTotalLength());
180         expect (! stream.isExhausted());
181 
182         numBytesRead = 0;
183         const int numBytesToSkip = 5;
184 
185         while (numBytesRead < data.getSize())
186         {
187             stream.skipNextBytes (numBytesToSkip);
188             numBytesRead += numBytesToSkip;
189             numBytesRead = std::min (numBytesRead, data.getSize());
190 
191             expectEquals (stream.getPosition(), (int64) numBytesRead);
192             expectEquals (stream.getNumBytesRemaining(), (int64) (data.getSize() - numBytesRead));
193             expect (stream.isExhausted() == (numBytesRead == data.getSize()));
194         }
195 
196         expectEquals (stream.getPosition(), (int64) data.getSize());
197         expectEquals (stream.getNumBytesRemaining(), (int64) 0);
198         expect (stream.isExhausted());
199     }
200 
createRandomWideCharString(Random & r)201     static String createRandomWideCharString (Random& r)
202     {
203         juce_wchar buffer [50] = { 0 };
204 
205         for (int i = 0; i < numElementsInArray (buffer) - 1; ++i)
206         {
207             if (r.nextBool())
208             {
209                 do
210                 {
211                     buffer[i] = (juce_wchar) (1 + r.nextInt (0x10ffff - 1));
212                 }
213                 while (! CharPointer_UTF16::canRepresent (buffer[i]));
214             }
215             else
216                 buffer[i] = (juce_wchar) (1 + r.nextInt (0xff));
217         }
218 
219         return CharPointer_UTF32 (buffer);
220     }
221 };
222 
223 static MemoryStreamTests memoryInputStreamUnitTests;
224 
225 #endif
226 
227 } // namespace juce
228