diff --git a/source/modules/water/streams/MemoryInputStream.cpp b/source/modules/water/streams/MemoryInputStream.cpp new file mode 100644 index 000000000..5c65016d6 --- /dev/null +++ b/source/modules/water/streams/MemoryInputStream.cpp @@ -0,0 +1,167 @@ +/* + ============================================================================== + + This file is part of the Water library. + Copyright (c) 2016 - ROLI Ltd. + Copyright (C) 2021 Filipe Coelho + + Permission is granted to use this software under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license/ + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, + OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + OF THIS SOFTWARE. + + ============================================================================== +*/ + +#include "MemoryInputStream.h" + +namespace water { + +MemoryInputStream::MemoryInputStream (const void* const sourceData, + const size_t sourceDataSize, + const bool keepInternalCopy) + : data (sourceData), + dataSize (sourceDataSize), + position (0) +{ + if (keepInternalCopy) + createInternalCopy(); +} + +MemoryInputStream::MemoryInputStream (const MemoryBlock& sourceData, + const bool keepInternalCopy) + : data (sourceData.getData()), + dataSize (sourceData.getSize()), + position (0) +{ + if (keepInternalCopy) + createInternalCopy(); +} + +void MemoryInputStream::createInternalCopy() +{ + internalCopy.malloc (dataSize); + memcpy (internalCopy, data, dataSize); + data = internalCopy; +} + +MemoryInputStream::~MemoryInputStream() +{ +} + +int64 MemoryInputStream::getTotalLength() +{ + return (int64) dataSize; +} + +int MemoryInputStream::read (void* const buffer, const int howMany) +{ + wassert (buffer != nullptr && howMany >= 0); + + if (howMany <= 0 || position >= dataSize) + return 0; + + const size_t num = jmin ((size_t) howMany, dataSize - position); + + if (num > 0) + { + memcpy (buffer, addBytesToPointer (data, position), num); + position += num; + } + + return (int) num; +} + +bool MemoryInputStream::isExhausted() +{ + return position >= dataSize; +} + +bool MemoryInputStream::setPosition (const int64 pos) +{ + position = (size_t) jlimit ((int64) 0, (int64) dataSize, pos); + return true; +} + +int64 MemoryInputStream::getPosition() +{ + return (int64) position; +} + + +//============================================================================== +#if WATER_UNIT_TESTS + +class MemoryStreamTests : public UnitTest +{ +public: + MemoryStreamTests() : UnitTest ("MemoryInputStream & MemoryOutputStream") {} + + void runTest() override + { + beginTest ("Basics"); + Random r = getRandom(); + + int randomInt = r.nextInt(); + int64 randomInt64 = r.nextInt64(); + double randomDouble = r.nextDouble(); + String randomString (createRandomWideCharString (r)); + + MemoryOutputStream mo; + mo.writeInt (randomInt); + mo.writeIntBigEndian (randomInt); + mo.writeCompressedInt (randomInt); + mo.writeString (randomString); + mo.writeInt64 (randomInt64); + mo.writeInt64BigEndian (randomInt64); + mo.writeDouble (randomDouble); + mo.writeDoubleBigEndian (randomDouble); + + MemoryInputStream mi (mo.getData(), mo.getDataSize(), false); + expect (mi.readInt() == randomInt); + expect (mi.readIntBigEndian() == randomInt); + expect (mi.readCompressedInt() == randomInt); + expectEquals (mi.readString(), randomString); + expect (mi.readInt64() == randomInt64); + expect (mi.readInt64BigEndian() == randomInt64); + expect (mi.readDouble() == randomDouble); + expect (mi.readDoubleBigEndian() == randomDouble); + } + + static String createRandomWideCharString (Random& r) + { + water_wchar buffer [50] = { 0 }; + + for (int i = 0; i < numElementsInArray (buffer) - 1; ++i) + { + if (r.nextBool()) + { + do + { + buffer[i] = (water_wchar) (1 + r.nextInt (0x10ffff - 1)); + } + while (! CharPointer_UTF16::canRepresent (buffer[i])); + } + else + buffer[i] = (water_wchar) (1 + r.nextInt (0xff)); + } + + return CharPointer_UTF32 (buffer); + } +}; + +static MemoryStreamTests memoryInputStreamUnitTests; + +#endif + +} diff --git a/source/modules/water/streams/MemoryInputStream.h b/source/modules/water/streams/MemoryInputStream.h new file mode 100644 index 000000000..8d615f11c --- /dev/null +++ b/source/modules/water/streams/MemoryInputStream.h @@ -0,0 +1,101 @@ +/* + ============================================================================== + + This file is part of the Water library. + Copyright (c) 2016 - ROLI Ltd. + Copyright (C) 2021 Filipe Coelho + + Permission is granted to use this software under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license/ + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, + OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + OF THIS SOFTWARE. + + ============================================================================== +*/ + +#ifndef WATER_MEMORYINPUTSTREAM_H_INCLUDED +#define WATER_MEMORYINPUTSTREAM_H_INCLUDED + +#include "InputStream.h" +#include "../memory/MemoryBlock.h" +#include "../memory/HeapBlock.h" + +namespace water { + +//============================================================================== +/** + Allows a block of data to be accessed as a stream. + + This can either be used to refer to a shared block of memory, or can make its + own internal copy of the data when the MemoryInputStream is created. +*/ +class MemoryInputStream : public InputStream +{ +public: + //============================================================================== + /** Creates a MemoryInputStream. + + @param sourceData the block of data to use as the stream's source + @param sourceDataSize the number of bytes in the source data block + @param keepInternalCopyOfData if false, the stream will just keep a pointer to + the source data, so this data shouldn't be changed + for the lifetime of the stream; if this parameter is + true, the stream will make its own copy of the + data and use that. + */ + MemoryInputStream (const void* sourceData, + size_t sourceDataSize, + bool keepInternalCopyOfData); + + /** Creates a MemoryInputStream. + + @param data a block of data to use as the stream's source + @param keepInternalCopyOfData if false, the stream will just keep a reference to + the source data, so this data shouldn't be changed + for the lifetime of the stream; if this parameter is + true, the stream will make its own copy of the + data and use that. + */ + MemoryInputStream (const MemoryBlock& data, + bool keepInternalCopyOfData); + + /** Destructor. */ + ~MemoryInputStream(); + + /** Returns a pointer to the source data block from which this stream is reading. */ + const void* getData() const noexcept { return data; } + + /** Returns the number of bytes of source data in the block from which this stream is reading. */ + size_t getDataSize() const noexcept { return dataSize; } + + //============================================================================== + int64 getPosition() override; + bool setPosition (int64 pos) override; + int64 getTotalLength() override; + bool isExhausted() override; + int read (void* destBuffer, int maxBytesToRead) override; + +private: + //============================================================================== + const void* data; + size_t dataSize, position; + HeapBlock internalCopy; + + void createInternalCopy(); + + CARLA_DECLARE_NON_COPY_CLASS (MemoryInputStream) +}; + +} + +#endif // WATER_MEMORYINPUTSTREAM_H_INCLUDED diff --git a/source/modules/water/water.cpp b/source/modules/water/water.cpp index c5b2aa32a..23869295d 100644 --- a/source/modules/water/water.cpp +++ b/source/modules/water/water.cpp @@ -69,6 +69,7 @@ BOOL WINAPI DllMain(HINSTANCE hInst, DWORD, LPVOID) #include "streams/FileInputSource.cpp" #include "streams/InputStream.cpp" +#include "streams/MemoryInputStream.cpp" #include "streams/MemoryOutputStream.cpp" #include "streams/OutputStream.cpp" diff --git a/source/modules/water/water.files.cpp b/source/modules/water/water.files.cpp index ff9bae7d7..996069840 100644 --- a/source/modules/water/water.files.cpp +++ b/source/modules/water/water.files.cpp @@ -51,6 +51,7 @@ HINSTANCE getCurrentModuleInstanceHandle() noexcept # include "maths/Random.cpp" # include "memory/MemoryBlock.cpp" # include "streams/InputStream.cpp" +# include "streams/MemoryInputStream.cpp" # include "streams/MemoryOutputStream.cpp" # include "streams/OutputStream.cpp" #endif