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