├── README.md ├── source ├── baseiids.cpp ├── classfactoryhelpers.h ├── fdynlib.h ├── hexbinary.h ├── timer.h ├── updatehandler.h ├── fdynlib.cpp ├── fstdmethods.h ├── fstreamer.h ├── fdebug.h ├── fobject.cpp ├── fdebug.cpp ├── timer.cpp ├── fcleanup.h ├── fbuffer.h ├── fcommandline.h ├── fbuffer.cpp ├── fstreamer.cpp ├── fobject.h └── updatehandler.cpp ├── LICENSE.txt └── thread ├── include ├── fcondition.h └── flock.h └── source ├── flock.cpp └── fcondition.cpp /README.md: -------------------------------------------------------------------------------- 1 | # Welcome to VST SDK 3 base 2 | 3 | Here you can find some helper classes useful for developing **VST 3** plug-ins. 4 | 5 | ## License & Usage guidelines 6 | 7 | More details are found at [www.steinberg.net/sdklicenses_vst3](http://www.steinberg.net/sdklicenses_vst3) 8 | 9 | ---- 10 | Return to [VST 3 SDK](https://github.com/steinbergmedia/vst3sdk) 11 | -------------------------------------------------------------------------------- /source/baseiids.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Project : SDK Base 3 | // Version : 1.0 4 | // 5 | // Category : Helpers 6 | // Filename : base/source/baseidds.cpp 7 | // Created by : Steinberg, 01/2008 8 | // Description : Basic Interface 9 | // 10 | //----------------------------------------------------------------------------- 11 | // This file is part of a Steinberg SDK. It is subject to the license terms 12 | // in the LICENSE file found in the top-level directory of this distribution 13 | // and at www.steinberg.net/sdklicenses. 14 | // No part of the SDK, including this file, may be copied, modified, propagated, 15 | // or distributed except according to the terms contained in the LICENSE file. 16 | //----------------------------------------------------------------------------- 17 | 18 | #include "pluginterfaces/base/funknown.h" 19 | #include "pluginterfaces/base/istringresult.h" 20 | #include "pluginterfaces/base/ipersistent.h" 21 | 22 | 23 | namespace Steinberg { 24 | 25 | DEF_CLASS_IID (IString) 26 | DEF_CLASS_IID (IStringResult) 27 | 28 | DEF_CLASS_IID (IPersistent) 29 | DEF_CLASS_IID (IAttributes) 30 | DEF_CLASS_IID (IAttributes2) 31 | //------------------------------------------------------------------------ 32 | } // namespace Steinberg 33 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | MIT License 3 | 4 | Copyright (c) 2025, Steinberg Media Technologies GmbH 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | //--------------------------------------------------------------------------------- -------------------------------------------------------------------------------- /source/classfactoryhelpers.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Project : SDK Base 3 | // Version : 1.0 4 | // 5 | // Category : Helpers 6 | // Filename : base/source/classfactoryhelpers.h 7 | // Created by : Steinberg, 03/2017 8 | // Description : Class factory 9 | // 10 | //----------------------------------------------------------------------------- 11 | // This file is part of a Steinberg SDK. It is subject to the license terms 12 | // in the LICENSE file found in the top-level directory of this distribution 13 | // and at www.steinberg.net/sdklicenses. 14 | // No part of the SDK, including this file, may be copied, modified, propagated, 15 | // or distributed except according to the terms contained in the LICENSE file. 16 | //----------------------------------------------------------------------------- 17 | 18 | #pragma once 19 | 20 | //------------------------------------------------------------------------------ 21 | // Helper Macros. Not intended for direct use. 22 | // Use: 23 | // META_CLASS(className), 24 | // META_CLASS_IFACE(className,Interface), 25 | // META_CLASS_SINGLE(className,Interface) 26 | // instead. 27 | //------------------------------------------------------------------------------ 28 | #define META_CREATE_FUNC(funcName) static FUnknown* funcName () 29 | 30 | #define CLASS_CREATE_FUNC(className) \ 31 | namespace Meta { \ 32 | META_CREATE_FUNC (make##className) { return (NEW className)->unknownCast (); } \ 33 | } 34 | 35 | #define SINGLE_CREATE_FUNC(className) \ 36 | namespace Meta { \ 37 | META_CREATE_FUNC (make##className) { return className::instance ()->unknownCast (); } \ 38 | } 39 | 40 | #define _META_CLASS(className) \ 41 | namespace Meta { \ 42 | static Steinberg::MetaClass meta##className ((#className), Meta::make##className); \ 43 | } 44 | 45 | #define _META_CLASS_IFACE(className, Interface) \ 46 | namespace Meta { \ 47 | static Steinberg::MetaClass meta##Interface##className ((#className), Meta::make##className, \ 48 | Interface##_iid); \ 49 | } 50 | 51 | /** TODO 52 | */ 53 | #define META_CLASS(className) \ 54 | CLASS_CREATE_FUNC (className) \ 55 | _META_CLASS (className) 56 | 57 | /** TODO 58 | */ 59 | #define META_CLASS_IFACE(className, Interface) \ 60 | CLASS_CREATE_FUNC (className) \ 61 | _META_CLASS_IFACE (className, Interface) 62 | 63 | /** TODO 64 | */ 65 | #define META_CLASS_SINGLE(className, Interface) \ 66 | SINGLE_CREATE_FUNC (className) \ 67 | _META_CLASS_IFACE (className, Interface) 68 | -------------------------------------------------------------------------------- /thread/include/fcondition.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Project : SDK Base 3 | // Version : 1.0 4 | // 5 | // Category : Helpers 6 | // Filename : base/thread/include/fcondition.h 7 | // Created by : Steinberg, 1995 8 | // Description : the threads and locks and signals... 9 | // 10 | //----------------------------------------------------------------------------- 11 | // This file is part of a Steinberg SDK. It is subject to the license terms 12 | // in the LICENSE file found in the top-level directory of this distribution 13 | // and at www.steinberg.net/sdklicenses. 14 | // No part of the SDK, including this file, may be copied, modified, propagated, 15 | // or distributed except according to the terms contained in the LICENSE file. 16 | //----------------------------------------------------------------------------- 17 | 18 | //---------------------------------------------------------------------------------- 19 | /** @file base/thread/include/fcondition.h 20 | signal. */ 21 | /** @defgroup baseThread Thread Handling */ 22 | //---------------------------------------------------------------------------------- 23 | 24 | #pragma once 25 | 26 | #include "pluginterfaces/base/ftypes.h" 27 | 28 | #if SMTG_PTHREADS 29 | #include 30 | #endif 31 | 32 | //------------------------------------------------------------------------ 33 | namespace Steinberg { 34 | namespace Base { 35 | namespace Thread { 36 | 37 | //------------------------------------------------------------------------ 38 | /** FCondition - wraps the signal and wait calls in win32 39 | @ingroup baseThread */ 40 | //------------------------------------------------------------------------ 41 | class FCondition 42 | { 43 | public: 44 | //------------------------------------------------------------------------ 45 | 46 | /** Condition constructor. 47 | * @param name name of condition 48 | */ 49 | FCondition (const char8* name = nullptr /* "FCondition" */ ); 50 | 51 | /** Condition destructor. 52 | */ 53 | ~FCondition (); 54 | 55 | /** Signals one thread. 56 | */ 57 | void signal (); 58 | 59 | /** Signals all threads. 60 | */ 61 | void signalAll (); 62 | 63 | /** Waits for condition. 64 | */ 65 | void wait (); 66 | 67 | /** Waits for condition with timeout 68 | * @param timeout time out in milliseconds 69 | * @return false if timed out 70 | */ 71 | bool waitTimeout (int32 timeout = -1); 72 | 73 | /** Resets condition. 74 | */ 75 | void reset (); 76 | 77 | #if SMTG_OS_WINDOWS 78 | /** Gets condition handle. 79 | * @return handle 80 | */ 81 | void* getHandle () { return event; } 82 | #endif 83 | 84 | //------------------------------------------------------------------------ 85 | private: 86 | #if SMTG_PTHREADS 87 | pthread_mutex_t mutex; ///< Mutex object 88 | pthread_cond_t cond; ///< Condition object 89 | int32 state; ///< Use to hold the state of the signal 90 | int32 waiters; ///< Number of waiters 91 | 92 | #if DEVELOPMENT 93 | int32 waits; ///< Waits count 94 | int32 signalCount; ///< Signals count 95 | #endif 96 | #elif SMTG_OS_WINDOWS 97 | void* event; ///< Event handle 98 | #endif 99 | }; 100 | 101 | //------------------------------------------------------------------------ 102 | } // namespace Thread 103 | } // namespace Base 104 | } // namespace Steinberg 105 | -------------------------------------------------------------------------------- /source/fdynlib.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Project : SDK Base 3 | // Version : 1.0 4 | // 5 | // Category : Helpers 6 | // Filename : base/source/fdynlib.h 7 | // Created by : Steinberg, 1998 8 | // Description : Dynamic library loading 9 | // 10 | //----------------------------------------------------------------------------- 11 | // This file is part of a Steinberg SDK. It is subject to the license terms 12 | // in the LICENSE file found in the top-level directory of this distribution 13 | // and at www.steinberg.net/sdklicenses. 14 | // No part of the SDK, including this file, may be copied, modified, propagated, 15 | // or distributed except according to the terms contained in the LICENSE file. 16 | //----------------------------------------------------------------------------- 17 | 18 | //------------------------------------------------------------------------ 19 | /** @file base/source/fdynlib.h 20 | Platform independent dynamic library loading. */ 21 | //------------------------------------------------------------------------ 22 | #pragma once 23 | 24 | #include "pluginterfaces/base/ftypes.h" 25 | #include "base/source/fobject.h" 26 | 27 | namespace Steinberg { 28 | 29 | //------------------------------------------------------------------------ 30 | /** Platform independent dynamic library loader. */ 31 | //------------------------------------------------------------------------ 32 | class FDynLibrary : public FObject 33 | { 34 | public: 35 | //------------------------------------------------------------------------ 36 | /** Constructor. 37 | 38 | Loads the specified dynamic library. 39 | 40 | @param[in] name the path of the library to load. 41 | @param[in] addExtension if @c true append the platform dependent default extension to @c name. 42 | 43 | @remarks 44 | - If @c name specifies a full path, the FDynLibrary searches only that path for the library. 45 | - If @c name specifies a relative path or a name without path, 46 | FDynLibrary uses a standard search strategy of the current platform to find the library; 47 | - If @c name is @c NULL the library is not loaded. 48 | - Use init() to load the library. */ 49 | FDynLibrary (const tchar* name = nullptr, bool addExtension = true); 50 | 51 | /** Destructor. 52 | The destructor unloads the library.*/ 53 | ~FDynLibrary () override; 54 | 55 | /** Loads the library if not already loaded. 56 | This function is normally called by FDynLibrary(). 57 | @remarks If the library is already loaded, this call has no effect. */ 58 | bool init (const tchar* name, bool addExtension = true); 59 | 60 | /** Returns the address of the procedure @c name */ 61 | void* getProcAddress (const char* name); 62 | 63 | /** Returns true when the library was successfully loaded. */ 64 | bool isLoaded () {return isloaded;} 65 | 66 | /** Unloads the library if it is loaded. 67 | This function is called by ~FDynLibrary (). */ 68 | bool unload (); 69 | 70 | /** Returns the platform dependent representation of the library instance. */ 71 | void* getPlatformInstance () const { return instance; } 72 | 73 | #if SMTG_OS_MACOS 74 | /** Returns @c true if the library is a bundle (Mac only). */ 75 | bool isBundleLib () const { return isBundle; } 76 | #endif 77 | 78 | //------------------------------------------------------------------------ 79 | OBJ_METHODS(FDynLibrary, FObject) 80 | protected: 81 | bool isloaded; 82 | 83 | void* instance; 84 | 85 | #if SMTG_OS_MACOS 86 | void* firstSymbol; 87 | bool isBundle; 88 | #endif 89 | }; 90 | 91 | //------------------------------------------------------------------------ 92 | } // namespace Steinberg 93 | -------------------------------------------------------------------------------- /thread/source/flock.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Project : SDK Base 3 | // Version : 1.0 4 | // 5 | // Category : Helpers 6 | // Filename : base/thread/source/flock.cpp 7 | // Created by : Steinberg, 1995 8 | // Description : Locks 9 | // 10 | //----------------------------------------------------------------------------- 11 | // This file is part of a Steinberg SDK. It is subject to the license terms 12 | // in the LICENSE file found in the top-level directory of this distribution 13 | // and at www.steinberg.net/sdklicenses. 14 | // No part of the SDK, including this file, may be copied, modified, propagated, 15 | // or distributed except according to the terms contained in the LICENSE file. 16 | //----------------------------------------------------------------------------- 17 | 18 | #define DEBUG_LOCK 0 19 | 20 | #include "base/thread/include/flock.h" 21 | #include "base/source/fdebug.h" 22 | 23 | //------------------------------------------------------------------------ 24 | #if SMTG_OS_WINDOWS 25 | //------------------------------------------------------------------------ 26 | #ifndef WINVER 27 | #define WINVER 0x0500 28 | #endif 29 | #ifndef _WIN32_WINNT 30 | #define _WIN32_WINNT WINVER 31 | #endif 32 | 33 | #include 34 | #include 35 | #define INIT_CS(cs) \ 36 | InitializeCriticalSection ((LPCRITICAL_SECTION)&cs); 37 | 38 | #endif // SMTG_OS_WINDOWS 39 | 40 | namespace Steinberg { 41 | namespace Base { 42 | namespace Thread { 43 | 44 | 45 | //------------------------------------------------------------------------ 46 | // FLock implementation 47 | //------------------------------------------------------------------------ 48 | FLock::FLock (const char8* /*name*/) 49 | { 50 | #if SMTG_PTHREADS 51 | pthread_mutexattr_t mutexAttr; 52 | pthread_mutexattr_init (&mutexAttr); 53 | pthread_mutexattr_settype (&mutexAttr, PTHREAD_MUTEX_RECURSIVE); 54 | if (pthread_mutex_init (&mutex, &mutexAttr) != 0) 55 | {SMTG_WARNING("mutex_init failed")} 56 | pthread_mutexattr_destroy (&mutexAttr); 57 | 58 | #elif SMTG_OS_WINDOWS 59 | INIT_CS (section) 60 | #else 61 | #warning implement FLock! 62 | #endif 63 | } 64 | 65 | //------------------------------------------------------------------------ 66 | FLock::~FLock () 67 | { 68 | #if SMTG_PTHREADS 69 | pthread_mutex_destroy (&mutex); 70 | 71 | #elif SMTG_OS_WINDOWS 72 | DeleteCriticalSection ((LPCRITICAL_SECTION)§ion); 73 | 74 | #endif 75 | } 76 | 77 | //------------------------------------------------------------------------ 78 | void FLock::lock () 79 | { 80 | #if DEBUG_LOCK 81 | FDebugPrint ("FLock::lock () %x\n", this); 82 | #endif 83 | 84 | #if SMTG_PTHREADS 85 | pthread_mutex_lock (&mutex); 86 | 87 | #elif SMTG_OS_WINDOWS 88 | EnterCriticalSection ((LPCRITICAL_SECTION)§ion); 89 | 90 | #endif 91 | } 92 | 93 | //------------------------------------------------------------------------ 94 | void FLock::unlock () 95 | { 96 | #if DEBUG_LOCK 97 | FDebugPrint ("FLock::unlock () %x\n", this); 98 | #endif 99 | 100 | #if SMTG_PTHREADS 101 | pthread_mutex_unlock (&mutex); 102 | 103 | #elif SMTG_OS_WINDOWS 104 | LeaveCriticalSection ((LPCRITICAL_SECTION)§ion); 105 | 106 | #endif 107 | } 108 | 109 | //------------------------------------------------------------------------ 110 | bool FLock::trylock () 111 | { 112 | #if SMTG_PTHREADS 113 | return pthread_mutex_trylock (&mutex) == 0; 114 | 115 | #elif SMTG_OS_WINDOWS 116 | return TryEnterCriticalSection ((LPCRITICAL_SECTION)§ion) != 0 ? true : false; 117 | 118 | #else 119 | return false; 120 | #endif 121 | } 122 | 123 | //------------------------------------------------------------------------ 124 | } // namespace Thread 125 | } // namespace Base 126 | } // namespace Steinberg 127 | -------------------------------------------------------------------------------- /source/hexbinary.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------- 2 | // Project : SDK Base 3 | // Version : 1.0 4 | // 5 | // Category : Helpers 6 | // Filename : base/source/hexbinary.h 7 | // Created by : Steinberg, 1/2012 8 | // Description : HexBinary encoding and decoding 9 | // 10 | //----------------------------------------------------------------------------- 11 | // LICENSE 12 | // (c) 2024, Steinberg Media Technologies GmbH, All Rights Reserved 13 | //----------------------------------------------------------------------------- 14 | // This Software Development Kit may not be distributed in parts or its entirety 15 | // without prior written agreement by Steinberg Media Technologies GmbH. 16 | // This SDK must not be used to re-engineer or manipulate any technology used 17 | // in any Steinberg or Third-party application or software module, 18 | // unless permitted by law. 19 | // Neither the name of the Steinberg Media Technologies nor the names of its 20 | // contributors may be used to endorse or promote products derived from this 21 | // software without specific prior written permission. 22 | // 23 | // THIS SDK IS PROVIDED BY STEINBERG MEDIA TECHNOLOGIES GMBH "AS IS" AND 24 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | // IN NO EVENT SHALL STEINBERG MEDIA TECHNOLOGIES GMBH BE LIABLE FOR ANY DIRECT, 27 | // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | // OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | // OF THE POSSIBILITY OF SUCH DAMAGE. 33 | //---------------------------------------------------------------------------------- 34 | 35 | //---------------------------------------------------------------------------------- 36 | /** @file base/source/hexbinary.h 37 | HexBinary encoding and decoding. */ 38 | //---------------------------------------------------------------------------------- 39 | #pragma once 40 | 41 | #include "base/source/fbuffer.h" 42 | 43 | namespace Steinberg { 44 | 45 | //------------------------------------------------------------------------------ 46 | namespace HexBinary 47 | { 48 | //------------------------------------------------------------------------------ 49 | /** convert the HexBinary input buffer to binary. Note that it is appended to the result buffer. */ 50 | //------------------------------------------------------------------------------ 51 | inline bool decode (const void* input, int32 inputSize, Buffer& result) 52 | { 53 | if ((inputSize & 1) == 1) 54 | return false; 55 | 56 | result.grow (result.getSize () + inputSize / 2); 57 | 58 | const char8* ptr = (const char8*)input; 59 | uint8 current = 0; 60 | for (int32 i = 0; i < inputSize; i++, ptr++) 61 | { 62 | current *= 16; 63 | if (*ptr >= 48 && *ptr <= 57) // 0, 1, 2, .., 9 64 | { 65 | current += *ptr - 48; 66 | } 67 | else if (*ptr >= 65 && *ptr <= 70) // A, B, .., F 68 | { 69 | current += *ptr - 55; 70 | } 71 | else if (*ptr >= 97 && *ptr <= 102) // a, b, .., f 72 | { 73 | current += *ptr - 87; 74 | } 75 | else 76 | { 77 | // malformed 78 | return false; 79 | } 80 | if (i % 2) 81 | { 82 | if (result.put (current) == false) 83 | return false; 84 | current = 0; 85 | } 86 | } 87 | return true; 88 | } 89 | 90 | //------------------------------------------------------------------------------ 91 | /** convert the binary input buffer to HexBinary. Note that it is appended to the result buffer. */ 92 | //------------------------------------------------------------------------------ 93 | inline bool encode (const void* input, int32 inputSize, Buffer& result) 94 | { 95 | result.grow (result.getSize () + inputSize * 2); 96 | 97 | const char8* ptr = (const char8*)input; 98 | for (int32 i = 0; i < inputSize; i++, ptr++) 99 | { 100 | char8 high = (*ptr & 0xF0) >> 4; 101 | char8 low = (*ptr & 0x0F); 102 | if (high > 9) 103 | { 104 | if (result.put ((char8)(high + 55)) == false) 105 | return false; 106 | } 107 | else 108 | { 109 | if (result.put ((char8)(high + 48)) == false) 110 | return false; 111 | } 112 | if (low > 9) 113 | { 114 | if (result.put ((char8)(low + 55)) == false) 115 | return false; 116 | } 117 | else 118 | { 119 | if (result.put ((char8)(low + 48)) == false) 120 | return false; 121 | } 122 | } 123 | return true; 124 | } 125 | //------------------------------------------------------------------------ 126 | } // namespace HexBinary 127 | } // namespace Steinberg 128 | -------------------------------------------------------------------------------- /source/timer.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Project : SDK Base 3 | // Version : 1.0 4 | // 5 | // Category : Helpers 6 | // Filename : base/source/timer.h 7 | // Created by : Steinberg, 05/2006 8 | // Description : Timer class for receiving tiggers at regular intervals 9 | // 10 | //----------------------------------------------------------------------------- 11 | // This file is part of a Steinberg SDK. It is subject to the license terms 12 | // in the LICENSE file found in the top-level directory of this distribution 13 | // and at www.steinberg.net/sdklicenses. 14 | // No part of the SDK, including this file, may be copied, modified, propagated, 15 | // or distributed except according to the terms contained in the LICENSE file. 16 | //----------------------------------------------------------------------------- 17 | 18 | #pragma once 19 | 20 | #include "fobject.h" 21 | #include 22 | 23 | #if SMTG_CPP17 24 | #include 25 | #endif 26 | 27 | namespace Steinberg { 28 | class Timer; 29 | 30 | //------------------------------------------------------------------------ 31 | namespace SystemTime { 32 | 33 | uint64 getTicks64 (); 34 | 35 | inline uint64 getTicksDuration (uint64 old, uint64 now) 36 | { 37 | if (old > now) 38 | return (std::numeric_limits::max) () - old + now; 39 | return now - old; 40 | } 41 | 42 | int32 getTicks (); ///< deprecated, use getTicks64 () 43 | 44 | //------------------------------------------------------------------------ 45 | } // namespace SystemTime 46 | 47 | //------------------------------------------------------------------------ 48 | /** @class ITimerCallback 49 | 50 | Implement this callback interface to receive triggers from a timer. 51 | Note: This interface is intended as a mix-in class and therefore does not provide ref-counting. 52 | 53 | @see Timer */ 54 | class ITimerCallback 55 | { 56 | public: 57 | virtual ~ITimerCallback () {} 58 | /** This method is called at the end of each interval. 59 | \param timer The timer which calls. */ 60 | virtual void onTimer (Timer* timer) = 0; 61 | }; 62 | 63 | template 64 | ITimerCallback* newTimerCallback (const Call& call) 65 | { 66 | struct Callback : public ITimerCallback 67 | { 68 | Callback (const Call& call) : call (call) {} 69 | void onTimer (Timer* timer) SMTG_OVERRIDE { call (timer); } 70 | Call call; 71 | }; 72 | return NEW Callback (call); 73 | } 74 | 75 | #if SMTG_CPP17 76 | //------------------------------------------------------------------------ 77 | /** @class TimerCallback 78 | * 79 | * a timer callback object using a funtion for the timer call 80 | */ 81 | struct TimerCallback final : ITimerCallback 82 | { 83 | using CallbackFunc = std::function; 84 | 85 | TimerCallback (CallbackFunc&& f) : f (std::move (f)) {} 86 | TimerCallback (const CallbackFunc& f) : f (f) {} 87 | 88 | void onTimer (Timer* timer) override { f (timer); } 89 | 90 | private: 91 | CallbackFunc f; 92 | }; 93 | 94 | #endif // SMTG_CPP17 95 | 96 | // ----------------------------------------------------------------- 97 | /** @class Timer 98 | 99 | Timer is a class that allows you to receive triggers at regular intervals. 100 | Note: The timer class is an abstract base class with (hidden) platform specific subclasses. 101 | 102 | Usage: 103 | @code 104 | class TimerReceiver : public FObject, public ITimerCallback 105 | { 106 | ... 107 | virtual void onTimer (Timer* timer) 108 | { 109 | // do stuff 110 | } 111 | ... 112 | }; 113 | 114 | TimerReceiver* receiver = new TimerReceiver (); 115 | Timer* myTimer = Timer::create (receiver, 100); // interval: every 100ms 116 | 117 | ... 118 | ... 119 | 120 | if (myTimer) 121 | myTimer->release (); 122 | if (receiver) 123 | receiver->release (); 124 | @endcode 125 | 126 | @see ITimerCallback 127 | */ 128 | class Timer : public FObject 129 | { 130 | public: 131 | /** Create a timer with a given interval 132 | \param callback The receiver of the timer calls. 133 | \param intervalMilliseconds The timer interval in milliseconds. 134 | \return The created timer if any, callers owns the timer. The timer starts immediately. 135 | */ 136 | static Timer* create (ITimerCallback* callback, uint32 intervalMilliseconds); 137 | 138 | virtual void stop () = 0; ///< Stop the timer. 139 | }; 140 | 141 | // ----------------------------------------------------------------- 142 | /** @class DisableDispatchingTimers 143 | 144 | Disables dispatching of timers for the live time of this object 145 | */ 146 | class DisableDispatchingTimers 147 | { 148 | public: 149 | DisableDispatchingTimers (); 150 | ~DisableDispatchingTimers (); 151 | 152 | private: 153 | bool oldState; 154 | }; 155 | 156 | #if SMTG_OS_LINUX 157 | using CreateTimerFunc = Timer* (*)(ITimerCallback* callback, uint32 intervalMilliseconds); 158 | void InjectCreateTimerFunction (CreateTimerFunc f); 159 | #endif 160 | 161 | //------------------------------------------------------------------------ 162 | } // namespace Steinberg 163 | -------------------------------------------------------------------------------- /source/updatehandler.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Project : SDK Base 3 | // Version : 1.0 4 | // 5 | // Category : Helpers 6 | // Filename : base/source/updatehandler.h 7 | // Created by : Steinberg, 2008 8 | // Description : 9 | // 10 | //----------------------------------------------------------------------------- 11 | // This file is part of a Steinberg SDK. It is subject to the license terms 12 | // in the LICENSE file found in the top-level directory of this distribution 13 | // and at www.steinberg.net/sdklicenses. 14 | // No part of the SDK, including this file, may be copied, modified, propagated, 15 | // or distributed except according to the terms contained in the LICENSE file. 16 | //----------------------------------------------------------------------------- 17 | 18 | #pragma once 19 | 20 | #include "base/source/fobject.h" 21 | #include "base/thread/include/flock.h" 22 | #include "pluginterfaces/base/iupdatehandler.h" 23 | 24 | namespace Steinberg { 25 | 26 | /// @cond ignore 27 | namespace Update { struct Table; } 28 | /// @endcond 29 | 30 | //------------------------------------------------------------------------ 31 | /** Handle Send and Cancel pending message for a given object*/ 32 | //------------------------------------------------------------------------ 33 | class IUpdateManager : public FUnknown 34 | { 35 | public: 36 | //------------------------------------------------------------------------ 37 | /** cancel pending messages send by \param object or by any if object is 0 */ 38 | virtual tresult PLUGIN_API cancelUpdates (FUnknown* object) = 0; 39 | /** send pending messages send by \param object or by any if object is 0 */ 40 | virtual tresult PLUGIN_API triggerDeferedUpdates (FUnknown* object = nullptr) = 0; 41 | static const FUID iid; 42 | }; 43 | 44 | DECLARE_CLASS_IID (IUpdateManager, 0x030B780C, 0xD6E6418D, 0x8CE00BC2, 0x09C834D4) 45 | 46 | //------------------------------------------------------------------------------ 47 | /** 48 | UpdateHandler implements IUpdateManager and IUpdateHandler to handle dependencies 49 | between objects to store and forward messages to dependent objects. 50 | 51 | This implementation is thread save, so objects can send message, add or remove 52 | dependents from different threads. 53 | Do do so it uses mutex, so be aware of locking. 54 | */ 55 | //------------------------------------------------------------------------------ 56 | class UpdateHandler : public FObject, public IUpdateHandler, public IUpdateManager 57 | { 58 | public: 59 | //------------------------------------------------------------------------------ 60 | UpdateHandler (); 61 | ~UpdateHandler () SMTG_OVERRIDE; 62 | 63 | using FObject::addDependent; 64 | using FObject::removeDependent; 65 | using FObject::deferUpdate; 66 | 67 | // IUpdateHandler 68 | //private: 69 | friend class FObject; 70 | /** register \param dependent to get messages from \param object */ 71 | tresult PLUGIN_API addDependent (FUnknown* object, IDependent* dependent) SMTG_OVERRIDE; 72 | /** unregister \param dependent to get no messages from \param object */ 73 | tresult PLUGIN_API removeDependent (FUnknown* object, IDependent* dependent, size_t& earseCount); 74 | tresult PLUGIN_API removeDependent (FUnknown* object, 75 | IDependent* dependent) SMTG_OVERRIDE; 76 | public: 77 | /** send \param message to all dependents of \param object immediately */ 78 | tresult PLUGIN_API triggerUpdates (FUnknown* object, int32 message) SMTG_OVERRIDE; 79 | /** send \param message to all dependents of \param object when idle */ 80 | tresult PLUGIN_API deferUpdates (FUnknown* object, int32 message) SMTG_OVERRIDE; 81 | 82 | // IUpdateManager 83 | /** cancel pending messages send by \param object or by any if object is 0 */ 84 | tresult PLUGIN_API cancelUpdates (FUnknown* object) SMTG_OVERRIDE; 85 | /** send pending messages send by \param object or by any if object is 0 */ 86 | tresult PLUGIN_API triggerDeferedUpdates (FUnknown* object = nullptr) SMTG_OVERRIDE; 87 | 88 | /// @cond ignore 89 | // obsolete functions kept for compatibility 90 | void checkUpdates (FObject* object = nullptr) 91 | { 92 | triggerDeferedUpdates (object ? object->unknownCast () : nullptr); 93 | } 94 | void flushUpdates (FObject* object) 95 | { 96 | if (object) 97 | cancelUpdates (object->unknownCast ()); 98 | } 99 | void deferUpdate (FObject* object, int32 message) 100 | { 101 | if (object) 102 | deferUpdates (object->unknownCast (), message); 103 | } 104 | void signalChange (FObject* object, int32 message, bool suppressUpdateDone = false) 105 | { 106 | if (object) 107 | doTriggerUpdates (object->unknownCast (), message, suppressUpdateDone); 108 | } 109 | #if DEVELOPMENT 110 | bool checkDeferred (FUnknown* object); 111 | bool hasDependencies (FUnknown* object); 112 | void printForObject (FObject* object) const; 113 | #endif 114 | /// @endcond 115 | size_t countDependencies (FUnknown* object = nullptr); 116 | 117 | OBJ_METHODS (UpdateHandler, FObject) 118 | FUNKNOWN_METHODS2 (IUpdateHandler, IUpdateManager, FObject) 119 | SINGLETON (UpdateHandler) 120 | //------------------------------------------------------------------------------ 121 | private: 122 | tresult doTriggerUpdates (FUnknown* object, int32 message, bool suppressUpdateDone); 123 | 124 | Steinberg::Base::Thread::FLock lock; 125 | Update::Table* table = nullptr; 126 | }; 127 | 128 | 129 | //------------------------------------------------------------------------ 130 | } // namespace Steinberg 131 | -------------------------------------------------------------------------------- /thread/include/flock.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Project : SDK Base 3 | // Version : 1.0 4 | // 5 | // Category : Helpers 6 | // Filename : base/thread/include/flock.h 7 | // Created by : Steinberg, 1995 8 | // Description : locks 9 | // 10 | //----------------------------------------------------------------------------- 11 | // This file is part of a Steinberg SDK. It is subject to the license terms 12 | // in the LICENSE file found in the top-level directory of this distribution 13 | // and at www.steinberg.net/sdklicenses. 14 | // No part of the SDK, including this file, may be copied, modified, propagated, 15 | // or distributed except according to the terms contained in the LICENSE file. 16 | //----------------------------------------------------------------------------- 17 | 18 | //---------------------------------------------------------------------------------- 19 | /** @file base/thread/include/flock.h 20 | Locks. */ 21 | /** @defgroup baseLocks Locks */ 22 | //---------------------------------------------------------------------------------- 23 | 24 | #pragma once 25 | 26 | #include "base/source/fobject.h" 27 | #include "pluginterfaces/base/ftypes.h" 28 | 29 | #if SMTG_PTHREADS 30 | #include 31 | 32 | #elif SMTG_OS_WINDOWS 33 | struct CRITSECT // CRITICAL_SECTION 34 | { 35 | void* DebugInfo; // PRTL_CRITICAL_SECTION_DEBUG DebugInfo; 36 | Steinberg::int32 LockCount; // LONG LockCount; 37 | Steinberg::int32 RecursionCount; // LONG RecursionCount; 38 | void* OwningThread; // HANDLE OwningThread 39 | void* LockSemaphore; // HANDLE LockSemaphore 40 | Steinberg::int32 SpinCount; // ULONG_PTR SpinCount 41 | }; 42 | #endif 43 | 44 | namespace Steinberg { 45 | namespace Base { 46 | namespace Thread { 47 | 48 | //------------------------------------------------------------------------ 49 | /** Lock interface declaration. 50 | @ingroup baseLocks */ 51 | //------------------------------------------------------------------------ 52 | struct ILock 53 | { 54 | //------------------------------------------------------------------------ 55 | virtual ~ILock () {} 56 | 57 | /** Enables lock. */ 58 | virtual void lock () = 0; 59 | 60 | /** Disables lock. */ 61 | virtual void unlock () = 0; 62 | 63 | /** Tries to disable lock. */ 64 | virtual bool trylock () = 0; 65 | //------------------------------------------------------------------------ 66 | }; 67 | 68 | //------------------------------------------------------------------------ 69 | /** FLock declaration. 70 | @ingroup baseLocks */ 71 | //------------------------------------------------------------------------ 72 | class FLock : public ILock 73 | { 74 | public: 75 | //------------------------------------------------------------------------ 76 | 77 | /** Lock constructor. 78 | * @param name lock name 79 | */ 80 | FLock (const char8* name = "FLock"); 81 | 82 | /** Lock destructor. */ 83 | ~FLock () SMTG_OVERRIDE; 84 | 85 | //-- ILock ----------------------------------------------------------- 86 | void lock () SMTG_OVERRIDE; 87 | void unlock () SMTG_OVERRIDE; 88 | bool trylock () SMTG_OVERRIDE; 89 | 90 | //------------------------------------------------------------------------ 91 | protected: 92 | #if SMTG_PTHREADS 93 | pthread_mutex_t mutex; ///< Mutex object 94 | 95 | #elif SMTG_OS_WINDOWS 96 | CRITSECT section; ///< Critical section object 97 | #endif 98 | }; 99 | 100 | //------------------------------------------------------------------------ 101 | /** FLockObj declaration. Reference counted lock 102 | @ingroup baseLocks */ 103 | //------------------------------------------------------------------------ 104 | class FLockObject : public FObject, public FLock 105 | { 106 | public: 107 | OBJ_METHODS (FLockObject, FObject) 108 | }; 109 | 110 | //------------------------------------------------------------------------ 111 | /** FGuard - automatic object for locks. 112 | @ingroup baseLocks */ 113 | //------------------------------------------------------------------------ 114 | class FGuard 115 | { 116 | public: 117 | //------------------------------------------------------------------------ 118 | 119 | /** FGuard constructor. 120 | * @param _lock guard this lock 121 | */ 122 | FGuard (ILock& _lock) : lock (_lock) { lock.lock (); } 123 | 124 | /** FGuard destructor. */ 125 | ~FGuard () { lock.unlock (); } 126 | 127 | //------------------------------------------------------------------------ 128 | private: 129 | ILock& lock; ///< guarded lock 130 | }; 131 | 132 | //------------------------------------------------------------------------ 133 | /** Conditional Guard - Locks only if valid lock is passed. 134 | @ingroup baseLocks */ 135 | //------------------------------------------------------------------------ 136 | class FConditionalGuard 137 | { 138 | public: 139 | //------------------------------------------------------------------------ 140 | 141 | /** FConditionGuard constructor. 142 | * @param _lock guard this lock 143 | */ 144 | FConditionalGuard (FLock* _lock) : lock (_lock) 145 | { 146 | if (lock) 147 | lock->lock (); 148 | } 149 | 150 | /** FConditionGuard destructor. */ 151 | ~FConditionalGuard () 152 | { 153 | if (lock) 154 | lock->unlock (); 155 | } 156 | 157 | //------------------------------------------------------------------------ 158 | private: 159 | FLock* lock; ///< guarded lock 160 | }; 161 | 162 | //------------------------------------------------------------------------ 163 | } // namespace Thread 164 | } // namespace Base 165 | } // namespace Steinberg 166 | -------------------------------------------------------------------------------- /thread/source/fcondition.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Project : SDK Base 3 | // Version : 1.0 4 | // 5 | // Category : Helpers 6 | // Filename : base/thread/source/fcondition.cpp 7 | // Created by : Steinberg, 1995 8 | // Description : signals... 9 | // 10 | //----------------------------------------------------------------------------- 11 | // This file is part of a Steinberg SDK. It is subject to the license terms 12 | // in the LICENSE file found in the top-level directory of this distribution 13 | // and at www.steinberg.net/sdklicenses. 14 | // No part of the SDK, including this file, may be copied, modified, propagated, 15 | // or distributed except according to the terms contained in the LICENSE file. 16 | //----------------------------------------------------------------------------- 17 | 18 | #define DEBUG_LOCK 0 19 | 20 | #include "base/thread/include/fcondition.h" 21 | #include "base/source/fdebug.h" 22 | 23 | #include 24 | #include 25 | 26 | //------------------------------------------------------------------------ 27 | #if SMTG_PTHREADS 28 | //------------------------------------------------------------------------ 29 | #if __MACH__ 30 | extern "C" { 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | } 43 | #include 44 | 45 | #else 46 | #include 47 | #endif 48 | 49 | #include 50 | 51 | #if SMTG_OS_MACOS 52 | #include 53 | #if !SMTG_OS_IOS 54 | #include 55 | #endif 56 | #endif 57 | 58 | #include 59 | //------------------------------------------------------------------------ 60 | #elif SMTG_OS_WINDOWS 61 | #include 62 | #endif 63 | 64 | namespace Steinberg { 65 | namespace Base { 66 | namespace Thread { 67 | 68 | //------------------------------------------------------------------------ 69 | /** FCondition CTOR. 70 | * @param name - can be used to set the name of the event. 71 | */ 72 | FCondition::FCondition (const char8* name) 73 | { 74 | #if SMTG_PTHREADS 75 | (void)name; // unused 76 | pthread_mutex_init (&mutex, 0); 77 | pthread_cond_init (&cond, 0); 78 | state = 0; 79 | waiters = 0; 80 | #if DEVELOPMENT 81 | waits = 0; 82 | signalCount = 0; 83 | #endif 84 | 85 | #elif SMTG_OS_WINDOWS 86 | // use name if existing 87 | event = CreateEventA (nullptr, FALSE, FALSE, name); 88 | 89 | #endif 90 | } 91 | 92 | //------------------------------------------------------------------------ 93 | FCondition::~FCondition () 94 | { 95 | #if SMTG_PTHREADS 96 | pthread_mutex_destroy (&mutex); 97 | pthread_cond_destroy (&cond); 98 | 99 | #elif SMTG_OS_WINDOWS 100 | CloseHandle (event); 101 | 102 | #endif 103 | } 104 | 105 | //------------------------------------------------------------------------ 106 | void FCondition::signal () 107 | { 108 | #if SMTG_PTHREADS 109 | pthread_mutex_lock (&mutex); 110 | state = 1; 111 | #if DEVELOPMENT 112 | signalCount++; 113 | #endif 114 | pthread_cond_signal (&cond); 115 | pthread_mutex_unlock (&mutex); 116 | 117 | #elif SMTG_OS_WINDOWS 118 | BOOL result = PulseEvent (event); 119 | if (!result) 120 | { 121 | SMTG_PRINTSYSERROR; 122 | } 123 | 124 | #endif 125 | } 126 | 127 | //------------------------------------------------------------------------ 128 | void FCondition::signalAll () 129 | { 130 | #if SMTG_PTHREADS 131 | pthread_mutex_lock (&mutex); 132 | state = waiters + 1; 133 | 134 | #if DEVELOPMENT 135 | signalCount++; 136 | #endif 137 | pthread_cond_broadcast (&cond); 138 | pthread_mutex_unlock (&mutex); 139 | 140 | #elif SMTG_OS_WINDOWS 141 | BOOL result = SetEvent (event); 142 | if (!result) 143 | { 144 | SMTG_PRINTSYSERROR; 145 | } 146 | 147 | #endif 148 | } 149 | 150 | //------------------------------------------------------------------------ 151 | void FCondition::wait () 152 | { 153 | #if SMTG_PTHREADS 154 | pthread_mutex_lock (&mutex); 155 | #if DEVELOPMENT 156 | waits++; 157 | #endif 158 | waiters++; 159 | while (!state) 160 | pthread_cond_wait (&cond, &mutex); 161 | if (--waiters == 0) 162 | state = 0; 163 | else 164 | --state; 165 | pthread_mutex_unlock (&mutex); 166 | 167 | #elif SMTG_OS_WINDOWS 168 | WaitForSingleObject (event, INFINITE); 169 | 170 | #endif 171 | } 172 | 173 | //------------------------------------------------------------------------ 174 | bool FCondition::waitTimeout (int32 milliseconds) 175 | { 176 | #if SMTG_PTHREADS 177 | // this is the implementation running on mac (2018-07-18) 178 | if (milliseconds == -1) 179 | { // infinite timeout 180 | wait (); 181 | return true; 182 | } 183 | 184 | struct timespec time; 185 | 186 | #if __MACH__ 187 | // this is the implementation running on mac (2018-07-18) 188 | time.tv_sec = milliseconds / 1000; 189 | time.tv_nsec = 1000000 * (milliseconds - (time.tv_sec * 1000)); 190 | 191 | pthread_mutex_lock (&mutex); 192 | #if DEVELOPMENT 193 | waits++; 194 | #endif 195 | // this is the implementation running on mac (2018-07-18) 196 | waiters++; 197 | 198 | bool result = true; 199 | while (!state) 200 | { 201 | int32 err = pthread_cond_timedwait_relative_np (&cond, &mutex, &time); 202 | if (err == ETIMEDOUT) 203 | { 204 | result = false; 205 | break; 206 | } 207 | else 208 | result = true; 209 | } 210 | if (--waiters == 0) 211 | state = 0; 212 | else 213 | --state; 214 | pthread_mutex_unlock (&mutex); 215 | return result; 216 | 217 | #else 218 | // dead code? not compiled in unit test and sequencer (2018-07-18) 219 | clock_gettime (CLOCK_REALTIME, &time); 220 | time.tv_nsec += milliseconds * 1000; // ????????? 221 | 222 | pthread_mutex_lock (&mutex); 223 | bool result = false; 224 | if (pthread_cond_timedwait (&cond, &mutex, &time) == 0) 225 | result = true; 226 | pthread_mutex_unlock (&mutex); 227 | return result; 228 | 229 | #endif 230 | 231 | #elif SMTG_OS_WINDOWS 232 | if (milliseconds == -1) 233 | milliseconds = INFINITE; 234 | 235 | DWORD result = WaitForSingleObject (event, milliseconds); 236 | return result == WAIT_TIMEOUT ? false : true; 237 | 238 | #endif 239 | 240 | #if !SMTG_OS_WINDOWS 241 | // WARNING ("Return false on time out not implemented!") 242 | return true; 243 | #endif 244 | } 245 | 246 | //------------------------------------------------------------------------ 247 | void FCondition::reset () 248 | { 249 | #if SMTG_OS_WINDOWS 250 | ResetEvent (event); 251 | #elif SMTG_PTHREADS 252 | state = 0; 253 | #endif 254 | } 255 | 256 | //------------------------------------------------------------------------ 257 | } // namespace Thread 258 | } // namespace Base 259 | } // namespace Steinberg 260 | -------------------------------------------------------------------------------- /source/fdynlib.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Project : SDK Base 3 | // Version : 1.0 4 | // 5 | // Category : Helpers 6 | // Filename : base/source/fdynlib.cpp 7 | // Created by : Steinberg, 1998 8 | // Description : Dynamic library loading 9 | // 10 | //----------------------------------------------------------------------------- 11 | // This file is part of a Steinberg SDK. It is subject to the license terms 12 | // in the LICENSE file found in the top-level directory of this distribution 13 | // and at www.steinberg.net/sdklicenses. 14 | // No part of the SDK, including this file, may be copied, modified, propagated, 15 | // or distributed except according to the terms contained in the LICENSE file. 16 | //----------------------------------------------------------------------------- 17 | 18 | #include "base/source/fdynlib.h" 19 | #include "pluginterfaces/base/fstrdefs.h" 20 | #include "base/source/fstring.h" 21 | 22 | #if SMTG_OS_WINDOWS 23 | #include 24 | 25 | #elif SMTG_OS_MACOS 26 | #include 27 | #include 28 | 29 | #if !SMTG_OS_IOS 30 | static const Steinberg::tchar kUnixDelimiter = STR ('/'); 31 | #endif 32 | #endif 33 | 34 | namespace Steinberg { 35 | 36 | #if SMTG_OS_MACOS 37 | #include 38 | 39 | // we ignore for the moment that the NSAddImage functions are deprecated 40 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 41 | 42 | static bool CopyProcessPath (Steinberg::String& name) 43 | { 44 | Dl_info info; 45 | if (dladdr ((const void*)CopyProcessPath, &info)) 46 | { 47 | if (info.dli_fname) 48 | { 49 | name.assign (info.dli_fname); 50 | #ifdef UNICODE 51 | name.toWideString (); 52 | #endif 53 | return true; 54 | } 55 | } 56 | return false; 57 | } 58 | 59 | #endif 60 | 61 | //------------------------------------------------------------------------ 62 | // FDynLibrary 63 | //------------------------------------------------------------------------ 64 | FDynLibrary::FDynLibrary (const tchar* n, bool addExtension) 65 | : isloaded (false) 66 | , instance (nullptr) 67 | { 68 | if (n) 69 | init (n, addExtension); 70 | } 71 | 72 | //------------------------------------------------------------------------ 73 | FDynLibrary::~FDynLibrary () 74 | { 75 | unload (); 76 | } 77 | 78 | //------------------------------------------------------------------------ 79 | bool FDynLibrary::init (const tchar* n, bool addExtension) 80 | { 81 | if (isLoaded ()) 82 | return true; 83 | 84 | Steinberg::String name (n); 85 | 86 | #if SMTG_OS_WINDOWS 87 | if (addExtension) 88 | name.append (STR (".dll")); 89 | 90 | instance = LoadLibrary (name); 91 | if (instance) 92 | isloaded = true; 93 | 94 | #elif SMTG_OS_MACOS 95 | isBundle = false; 96 | // first check if it is a bundle 97 | if (addExtension) 98 | name.append (STR (".bundle")); 99 | if (name.getChar16 (0) != STR('/')) // no absoltue path 100 | { 101 | Steinberg::String p; 102 | if (CopyProcessPath (p)) 103 | { 104 | Steinberg::int32 index = p.findLast (STR ('/')); 105 | p.remove (index+1); 106 | name = p + name; 107 | } 108 | } 109 | CFStringRef fsString = (CFStringRef)name.toCFStringRef (); 110 | CFURLRef url = CFURLCreateWithFileSystemPath (NULL, fsString, kCFURLPOSIXPathStyle, true); 111 | if (url) 112 | { 113 | CFBundleRef bundle = CFBundleCreate (NULL, url); 114 | if (bundle) 115 | { 116 | if (CFBundleLoadExecutable (bundle)) 117 | { 118 | isBundle = true; 119 | instance = (void*)bundle; 120 | } 121 | else 122 | CFRelease (bundle); 123 | } 124 | CFRelease (url); 125 | } 126 | CFRelease (fsString); 127 | 128 | name.assign (n); 129 | 130 | #if !SMTG_OS_IOS 131 | if (!isBundle) 132 | { 133 | // now we check for a dynamic library 134 | firstSymbol = NULL; 135 | if (addExtension) 136 | { 137 | name.append (STR (".dylib")); 138 | } 139 | // Only if name is a relative path we use the Process Path as root: 140 | if (name[0] != kUnixDelimiter) 141 | { 142 | Steinberg::String p; 143 | if (CopyProcessPath (p)) 144 | { 145 | Steinberg::int32 index = p.findLast (STR ("/")); 146 | p.remove (index+1); 147 | p.append (name); 148 | p.toMultiByte (Steinberg::kCP_Utf8); 149 | instance = (void*) NSAddImage (p, NSADDIMAGE_OPTION_RETURN_ON_ERROR); 150 | } 151 | } 152 | // Last but not least let the system search for it 153 | // 154 | if (instance == 0) 155 | { 156 | name.toMultiByte (Steinberg::kCP_Utf8); 157 | instance = (void*) NSAddImage (name, NSADDIMAGE_OPTION_RETURN_ON_ERROR); 158 | } 159 | } 160 | #endif // !SMTG_OS_IOS 161 | 162 | if (instance) 163 | isloaded = true; 164 | 165 | #endif 166 | 167 | return isloaded; 168 | } 169 | 170 | //------------------------------------------------------------------------ 171 | bool FDynLibrary::unload () 172 | { 173 | if (!isLoaded ()) 174 | return false; 175 | 176 | #if SMTG_OS_WINDOWS 177 | FreeLibrary ((HINSTANCE)instance); 178 | 179 | #elif SMTG_OS_MACOS 180 | if (isBundle) 181 | { 182 | if (CFGetRetainCount ((CFTypeRef)instance) == 1) 183 | CFBundleUnloadExecutable ((CFBundleRef)instance); 184 | CFRelease ((CFBundleRef)instance); 185 | } 186 | else 187 | { 188 | // we don't use this anymore as the darwin dyld can't unload dynamic libraries yet and may crash 189 | /* if (firstSymbol) 190 | { 191 | NSModule module = NSModuleForSymbol ((NSSymbol)firstSymbol); 192 | if (module) 193 | NSUnLinkModule (module, NSUNLINKMODULE_OPTION_NONE); 194 | }*/ 195 | } 196 | #endif 197 | instance = nullptr; 198 | isloaded = false; 199 | return true; 200 | } 201 | 202 | //------------------------------------------------------------------------ 203 | void* FDynLibrary::getProcAddress (const char* name) 204 | { 205 | if (!isloaded) 206 | return nullptr; 207 | 208 | #if SMTG_OS_WINDOWS 209 | return (void*)GetProcAddress ((HINSTANCE)instance, name); 210 | 211 | #elif SMTG_OS_MACOS 212 | if (isBundle) 213 | { 214 | CFStringRef functionName = CFStringCreateWithCString (NULL, name, kCFStringEncodingASCII); 215 | void* result = CFBundleGetFunctionPointerForName ((CFBundleRef)instance, functionName); 216 | CFRelease (functionName); 217 | return result; 218 | } 219 | #if !SMTG_OS_IOS 220 | else 221 | { 222 | char* symbolName = (char*) malloc (strlen (name) + 2); 223 | strcpy (symbolName, "_"); 224 | strcat (symbolName, name); 225 | NSSymbol symbol; 226 | symbol = NSLookupSymbolInImage ((const struct mach_header*)instance, symbolName, NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_NOW | NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); 227 | free (symbolName); 228 | if (symbol) 229 | { 230 | if (firstSymbol == NULL) 231 | firstSymbol = symbol; 232 | return NSAddressOfSymbol (symbol); 233 | } 234 | } 235 | #endif // !SMTG_OS_IOS 236 | 237 | return nullptr; 238 | #else 239 | return nullptr; 240 | #endif 241 | } 242 | 243 | //------------------------------------------------------------------------ 244 | } // namespace Steinberg 245 | -------------------------------------------------------------------------------- /source/fstdmethods.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Project : SDK Base 3 | // Version : 1.0 4 | // 5 | // Category : Helpers 6 | // Filename : base/source/fstdmethods.h 7 | // Created by : Steinberg, 2007 8 | // Description : Convenient macros to create setter and getter methods. 9 | // 10 | //----------------------------------------------------------------------------- 11 | // This file is part of a Steinberg SDK. It is subject to the license terms 12 | // in the LICENSE file found in the top-level directory of this distribution 13 | // and at www.steinberg.net/sdklicenses. 14 | // No part of the SDK, including this file, may be copied, modified, propagated, 15 | // or distributed except according to the terms contained in the LICENSE file. 16 | //----------------------------------------------------------------------------- 17 | 18 | //------------------------------------------------------------------------ 19 | /** @file base/source/fstdmethods.h 20 | Convenient macros to create setter and getter methods. */ 21 | //------------------------------------------------------------------------ 22 | #pragma once 23 | 24 | //---------------------------------------------------------------------------------- 25 | /** @name Methods for flags. 26 | Macros to create setter and getter methods for flags. 27 | 28 | Usage example with DEFINE_STATE: 29 | \code 30 | class MyClass 31 | { 32 | public: 33 | MyClass () : flags (0) {} 34 | DEFINE_FLAG (flags, isFlagged, 1<<0) 35 | DEFINE_FLAG (flags, isMine, 1<<1) 36 | private: 37 | uint32 flags; 38 | }; 39 | void someFunction () 40 | { 41 | MyClass c; 42 | if (c.isFlagged ()) // check the flag 43 | c.isFlagged (false); // set the flag 44 | } 45 | \endcode 46 | */ 47 | //---------------------------------------------------------------------------------- 48 | ///@{ 49 | 50 | /** Create Methods with @c get and @c set prefix. */ 51 | #define DEFINE_STATE(flagVar,methodName,value)\ 52 | void set##methodName (bool state) { if (state) flagVar |= (value); else flagVar &= ~(value); }\ 53 | bool get##methodName ()const { return (flagVar & (value)) != 0; } 54 | 55 | /** Create Methods with @c get prefix. 56 | There is only a 'get' method. */ 57 | #define DEFINE_GETSTATE(flagVar,methodName,value)\ 58 | bool get##methodName ()const { return (flagVar & (value)) != 0; } 59 | 60 | /** Create Methods. 61 | Same name for the getter and setter. */ 62 | #define DEFINE_FLAG(flagVar,methodName,value)\ 63 | void methodName (bool state) { if (state) flagVar |= (value); else flagVar &= ~(value); }\ 64 | bool methodName ()const { return (flagVar & (value)) != 0; } 65 | 66 | /** Create Methods. 67 | There is only a 'get' method. */ 68 | #define DEFINE_GETFLAG(flagVar,methodName,value)\ 69 | bool methodName ()const { return (flagVar & (value)) != 0; } 70 | 71 | 72 | /** Create @c static Methods. 73 | Same name for the getter and setter. */ 74 | #define DEFINE_FLAG_STATIC(flagVar,methodName,value)\ 75 | static void methodName (bool __state) { if (__state) flagVar |= (value); else flagVar &= ~(value); }\ 76 | static bool methodName () { return (flagVar & (value)) != 0; } 77 | ///@} 78 | 79 | //---------------------------------------------------------------------------------- 80 | /** @name Methods for data members. 81 | Macros to create setter and getter methods for class members. 82 | 83 | Examples: 84 | \code 85 | class MyClass 86 | { 87 | public: 88 | DATA_MEMBER (double, distance, Distance) 89 | STRING_MEMBER (Steinberg::String, name, Name) 90 | SHARED_MEMBER (FUnknown, userData, UserData) 91 | CLASS_MEMBER (Steinberg::Buffer, bufferData, BufferData) 92 | POINTER_MEMBER (Steinberg::FObject, refOnly, RefOnly) 93 | }; 94 | \endcode 95 | */ 96 | //-------------------------------------------------------------------------------------- 97 | ///@{ 98 | 99 | /** Build-in member (pass by value). */ 100 | #define DATA_MEMBER(type,varName,methodName)\ 101 | public:void set##methodName (type v) { varName = v;}\ 102 | type get##methodName ()const { return varName; }\ 103 | protected: type varName; public: 104 | 105 | //** Object member (pass by reference). */ 106 | #define CLASS_MEMBER(type,varName,methodName)\ 107 | public:void set##methodName (const type& v){ varName = v;}\ 108 | const type& get##methodName () const { return varName; }\ 109 | protected: type varName; public: 110 | 111 | //** Simple pointer. */ 112 | #define POINTER_MEMBER(type,varName,methodName)\ 113 | public:void set##methodName (type* ptr){ varName = ptr;}\ 114 | type* get##methodName ()const { return varName; }\ 115 | private: type* varName; public: 116 | 117 | //** Shared member - FUnknown / FObject / etc */ 118 | #define SHARED_MEMBER(type,varName,methodName)\ 119 | public:void set##methodName (type* v){ varName = v;}\ 120 | type* get##methodName ()const { return varName; }\ 121 | private: IPtr varName; public: 122 | 123 | //** Owned member - FUnknown / FObject / CmObject etc */ 124 | #define OWNED_MEMBER(type,varName,methodName)\ 125 | public:void set##methodName (type* v){ varName = v;}\ 126 | type* get##methodName ()const { return varName; }\ 127 | private: OPtr varName; public: 128 | 129 | //** tchar* / String class member - set by const tchar*, return by reference */ 130 | #define STRING_MEMBER(type,varName,methodName)\ 131 | public:void set##methodName (const tchar* v){ varName = v;}\ 132 | const type& get##methodName () const { return varName; }\ 133 | protected: type varName; public: 134 | 135 | //** char8* / String class member - set by const char8*, return by reference */ 136 | #define STRING8_MEMBER(type,varName,methodName)\ 137 | public:void set##methodName (const char8* v){ varName = v;}\ 138 | const type& get##methodName () const { return varName; }\ 139 | protected: type varName; public: 140 | 141 | //** Standard String Member Steinberg::String */ 142 | #define STRING_MEMBER_STD(varName,methodName) STRING_MEMBER(Steinberg::String,varName,methodName) 143 | #define STRING8_MEMBER_STD(varName,methodName) STRING8_MEMBER(Steinberg::String,varName,methodName) 144 | 145 | ///@} 146 | 147 | 148 | // obsolete names 149 | #define DEFINE_VARIABLE(type,varName,methodName) DATA_MEMBER(type,varName,methodName) 150 | #define DEFINE_POINTER(type,varName,methodName) POINTER_MEMBER(type,varName,methodName) 151 | #define DEFINE_MEMBER(type,varName,methodName) CLASS_MEMBER(type,varName,methodName) 152 | 153 | 154 | 155 | 156 | 157 | //------------------------------------------------------------------------ 158 | // for implementing comparison operators using a class member or a compare method: 159 | //------------------------------------------------------------------------ 160 | #define COMPARE_BY_MEMBER_METHODS(className,memberName) \ 161 | bool operator == (const className& other) const {return (memberName == other.memberName);} \ 162 | bool operator != (const className& other) const {return (memberName != other.memberName);} \ 163 | bool operator < (const className& other) const {return (memberName < other.memberName);} \ 164 | bool operator > (const className& other) const {return (memberName > other.memberName);} \ 165 | bool operator <= (const className& other) const {return (memberName <= other.memberName);} \ 166 | bool operator >= (const className& other) const {return (memberName >= other.memberName);} 167 | 168 | #define COMPARE_BY_MEMORY_METHODS(className) \ 169 | bool operator == (const className& other) const {return memcmp (this, &other, sizeof (className)) == 0;} \ 170 | bool operator != (const className& other) const {return memcmp (this, &other, sizeof (className)) != 0;} \ 171 | bool operator < (const className& other) const {return memcmp (this, &other, sizeof (className)) < 0;} \ 172 | bool operator > (const className& other) const {return memcmp (this, &other, sizeof (className)) > 0;} \ 173 | bool operator <= (const className& other) const {return memcmp (this, &other, sizeof (className)) <= 0;} \ 174 | bool operator >= (const className& other) const {return memcmp (this, &other, sizeof (className)) >= 0;} 175 | 176 | #define COMPARE_BY_COMPARE_METHOD(className,methodName) \ 177 | bool operator == (const className& other) const {return methodName (other) == 0;} \ 178 | bool operator != (const className& other) const {return methodName (other) != 0;} \ 179 | bool operator < (const className& other) const {return methodName (other) < 0;} \ 180 | bool operator > (const className& other) const {return methodName (other) > 0;} \ 181 | bool operator <= (const className& other) const {return methodName (other) <= 0; } \ 182 | bool operator >= (const className& other) const {return methodName (other) >= 0; } 183 | -------------------------------------------------------------------------------- /source/fstreamer.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Project : SDK Base 3 | // Version : 1.0 4 | // 5 | // Category : Helpers 6 | // Filename : base/source/fstreamer.h 7 | // Created by : Steinberg, 12/2005 8 | // Description : Extract of typed stream i/o methods from FStream 9 | // 10 | //----------------------------------------------------------------------------- 11 | // This file is part of a Steinberg SDK. It is subject to the license terms 12 | // in the LICENSE file found in the top-level directory of this distribution 13 | // and at www.steinberg.net/sdklicenses. 14 | // No part of the SDK, including this file, may be copied, modified, propagated, 15 | // or distributed except according to the terms contained in the LICENSE file. 16 | //----------------------------------------------------------------------------- 17 | 18 | #pragma once 19 | 20 | #include "pluginterfaces/base/funknown.h" 21 | 22 | namespace Steinberg { 23 | 24 | //------------------------------------------------------------------------ 25 | enum FSeekMode 26 | { 27 | kSeekSet, 28 | kSeekCurrent, 29 | kSeekEnd 30 | }; 31 | 32 | //------------------------------------------------------------------------ 33 | // FStreamer 34 | //------------------------------------------------------------------------ 35 | /** Byteorder-aware base class for typed stream i/o. */ 36 | //------------------------------------------------------------------------ 37 | class FStreamer 38 | { 39 | public: 40 | //------------------------------------------------------------------------ 41 | FStreamer (int16 byteOrder = BYTEORDER); 42 | virtual ~FStreamer () {} 43 | 44 | /** @name Implementing class must override. */ 45 | ///@{ 46 | virtual TSize readRaw (void*, TSize) = 0; ///< Read one buffer of size. 47 | virtual TSize writeRaw (const void*, TSize) = 0; ///< Write one buffer of size. 48 | virtual int64 seek (int64, FSeekMode) = 0; ///< Set file position for stream. 49 | virtual int64 tell () = 0; ///< Return current file position. 50 | ///@} 51 | 52 | /** @name Streams are byteOrder aware. */ 53 | ///@{ 54 | inline void setByteOrder (int32 e) { byteOrder = (int16)e; } 55 | inline int32 getByteOrder () const { return byteOrder; } 56 | ///@} 57 | 58 | /** @name read and write int8 and char. */ 59 | ///@{ 60 | bool writeChar8 (char8); 61 | bool readChar8 (char8&); 62 | bool writeUChar8 (unsigned char); 63 | bool readUChar8 (unsigned char&); 64 | bool writeChar16 (char16 c); 65 | bool readChar16 (char16& c); 66 | 67 | bool writeInt8 (int8 c); 68 | bool readInt8 (int8& c); 69 | bool writeInt8u (uint8 c); 70 | bool readInt8u (uint8& c); 71 | ///@} 72 | 73 | /** @name read and write int16. */ 74 | ///@{ 75 | bool writeInt16 (int16); 76 | bool readInt16 (int16&); 77 | bool writeInt16Array (const int16* array, int32 count); 78 | bool readInt16Array (int16* array, int32 count); 79 | bool writeInt16u (uint16); 80 | bool readInt16u (uint16&); 81 | bool writeInt16uArray (const uint16* array, int32 count); 82 | bool readInt16uArray (uint16* array, int32 count); 83 | ///@} 84 | 85 | /** @name read and write int32. */ 86 | ///@{ 87 | bool writeInt32 (int32); 88 | bool readInt32 (int32&); 89 | bool writeInt32Array (const int32* array, int32 count); 90 | bool readInt32Array (int32* array, int32 count); 91 | bool writeInt32u (uint32); 92 | bool readInt32u (uint32&); 93 | bool writeInt32uArray (const uint32* array, int32 count); 94 | bool readInt32uArray (uint32* array, int32 count); 95 | ///@} 96 | 97 | /** @name read and write int64. */ 98 | ///@{ 99 | bool writeInt64 (int64); 100 | bool readInt64 (int64&); 101 | bool writeInt64Array (const int64* array, int32 count); 102 | bool readInt64Array (int64* array, int32 count); 103 | bool writeInt64u (uint64); 104 | bool readInt64u (uint64&); 105 | bool writeInt64uArray (const uint64* array, int32 count); 106 | bool readInt64uArray (uint64* array, int32 count); 107 | ///@} 108 | 109 | /** @name read and write float and float array. */ 110 | ///@{ 111 | bool writeFloat (float); 112 | bool readFloat (float&); 113 | bool writeFloatArray (const float* array, int32 count); 114 | bool readFloatArray (float* array, int32 count); 115 | ///@} 116 | 117 | /** @name read and write double and double array. */ 118 | ///@{ 119 | bool writeDouble (double); 120 | bool readDouble (double&); 121 | bool writeDoubleArray (const double* array, int32 count); 122 | bool readDoubleArray (double* array, int32 count); 123 | ///@} 124 | 125 | /** @name read and write Boolean. */ 126 | ///@{ 127 | bool writeBool (bool); ///< Write one boolean 128 | bool readBool (bool&); ///< Read one bool. 129 | ///@} 130 | 131 | /** @name read and write Strings. */ 132 | ///@{ 133 | TSize writeString8 (const char8* ptr, bool terminate = false); ///< a direct output function writing only one string (ascii 8bit) 134 | TSize readString8 (char8* ptr, TSize size); ///< a direct input function reading only one string (ascii) (ended by a \n or \0 or eof) 135 | 136 | bool writeStr8 (const char8* ptr); ///< write a string length (strlen) and string itself 137 | char8* readStr8 (); ///< read a string length and string text (The return string must be deleted when use is finished) 138 | 139 | static int32 getStr8Size (const char8* ptr); ///< returns the size of a saved string 140 | 141 | bool writeStringUtf8 (const tchar* ptr); ///< always terminated, converts to utf8 if non ascii characters are in string 142 | int32 readStringUtf8 (tchar* ptr, int32 maxSize); ///< read a UTF8 string 143 | ///@} 144 | 145 | bool skip (uint32 bytes); 146 | bool pad (uint32 bytes); 147 | 148 | 149 | //------------------------------------------------------------------------ 150 | protected: 151 | int16 byteOrder; 152 | }; 153 | 154 | 155 | //------------------------------------------------------------------------ 156 | /** FStreamSizeHolder Declaration 157 | remembers size of stream chunk for backward compatibility. 158 | 159 | Example: 160 | @code 161 | externalize (a) 162 | { 163 | FStreamSizeHolder sizeHolder; 164 | sizeHolder.beginWrite (); // sets start mark, writes dummy size 165 | a << .... 166 | sizeHolder.endWrite (); // jumps to start mark, updates size, jumps back here 167 | } 168 | 169 | internalize (a) 170 | { 171 | FStreamSizeHolder sizeHolder; 172 | sizeHolder.beginRead (); // reads size, mark 173 | a >> .... 174 | sizeHolder.endRead (); // jumps forward if new version has larger size 175 | } 176 | @endcode 177 | */ 178 | //------------------------------------------------------------------------ 179 | class FStreamSizeHolder 180 | { 181 | public: 182 | FStreamSizeHolder (FStreamer &s); 183 | 184 | void beginWrite (); ///< remembers position and writes 0 185 | int32 endWrite (); ///< writes and returns size (since the start marker) 186 | int32 beginRead (); ///< returns size 187 | void endRead (); ///< jump to end of chunk 188 | 189 | protected: 190 | FStreamer &stream; 191 | int64 sizePos; 192 | }; 193 | 194 | class IBStream; 195 | 196 | //------------------------------------------------------------------------ 197 | // IBStreamer 198 | //------------------------------------------------------------------------ 199 | /** Wrapper class for typed reading/writing from or to IBStream. 200 | Can be used framework-independent in plug-ins. */ 201 | //------------------------------------------------------------------------ 202 | class IBStreamer: public FStreamer 203 | { 204 | public: 205 | //------------------------------------------------------------------------ 206 | /** Constructor for a given IBSTream and a byteOrder. */ 207 | IBStreamer (IBStream* stream, int16 byteOrder = BYTEORDER); 208 | 209 | IBStream* getStream () { return stream; } ///< Returns the associated IBStream. 210 | 211 | // FStreamer overrides: 212 | TSize readRaw (void*, TSize) SMTG_OVERRIDE; ///< Read one buffer of size. 213 | TSize writeRaw (const void*, TSize) SMTG_OVERRIDE; ///< Write one buffer of size. 214 | int64 seek (int64, FSeekMode) SMTG_OVERRIDE; ///< Set file position for stream. 215 | int64 tell () SMTG_OVERRIDE; ///< Return current file position. 216 | //------------------------------------------------------------------------ 217 | protected: 218 | IBStream* stream; 219 | }; 220 | 221 | //------------------------------------------------------------------------ 222 | } // namespace Steinberg 223 | -------------------------------------------------------------------------------- /source/fdebug.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Project : SDK Base 3 | // Version : 1.0 4 | // 5 | // Category : Helpers 6 | // Filename : base/source/fdebug.h 7 | // Created by : Steinberg, 1995 8 | // Description : There are 2 levels of debugging messages: 9 | // DEVELOPMENT During development 10 | // RELEASE Program is shipping. 11 | // 12 | //----------------------------------------------------------------------------- 13 | // This file is part of a Steinberg SDK. It is subject to the license terms 14 | // in the LICENSE file found in the top-level directory of this distribution 15 | // and at www.steinberg.net/sdklicenses. 16 | // No part of the SDK, including this file, may be copied, modified, propagated, 17 | // or distributed except according to the terms contained in the LICENSE file. 18 | //----------------------------------------------------------------------------- 19 | 20 | //----------------------------------------------------------------------------- 21 | /** @file base/source/fdebug.h 22 | Debugging tools. 23 | 24 | There are 2 levels of debugging messages: 25 | - DEVELOPMENT 26 | - During development 27 | - RELEASE 28 | - Program is shipping. 29 | */ 30 | //----------------------------------------------------------------------------- 31 | #pragma once 32 | 33 | #include "pluginterfaces/base/ftypes.h" 34 | #include 35 | 36 | #if SMTG_OS_MACOS 37 | #include 38 | #endif 39 | 40 | /** Returns true if a debugger is attached. */ 41 | bool AmIBeingDebugged (); 42 | 43 | //----------------------------------------------------------------------------- 44 | // development / release 45 | //----------------------------------------------------------------------------- 46 | #if !defined (DEVELOPMENT) && !defined (RELEASE) 47 | #ifdef _DEBUG 48 | #define DEVELOPMENT 1 49 | #elif defined (NDEBUG) 50 | #define RELEASE 1 51 | #else 52 | #error DEVELOPMENT, RELEASE, _DEBUG, or NDEBUG must be defined! 53 | #endif 54 | #endif 55 | 56 | //----------------------------------------------------------------------------- 57 | #if SMTG_OS_WINDOWS 58 | 59 | /** Disable compiler warning: 60 | * C4291: "No matching operator delete found; memory will not be freed if initialization throws an 61 | * exception. A placement new is used for which there is no placement delete." */ 62 | #if DEVELOPMENT && defined(_MSC_VER) 63 | #pragma warning(disable : 4291) 64 | #pragma warning(disable : 4985) 65 | #endif 66 | 67 | #endif // SMTG_OS_WINDOWS 68 | 69 | #if DEVELOPMENT 70 | //----------------------------------------------------------------------------- 71 | /** If "f" is not true and a debugger is present, send an error string to the debugger for display 72 | and cause a breakpoint exception to occur in the current process. SMTG_ASSERT is removed 73 | completely in RELEASE configuration. So do not pass methods calls to this macro that are expected 74 | to exist in the RELEASE build (for method calls that need to be present in a RELEASE build, use 75 | the VERIFY macros instead)*/ 76 | #define SMTG_ASSERT(f) \ 77 | if (!(f)) \ 78 | FDebugBreak ("%s(%d) : Assert failed: %s\n", __FILE__, __LINE__, #f); 79 | 80 | #define SMTG_ASSERT_MSG(f, msg) \ 81 | if (!(f)) \ 82 | FDebugBreak ("%s(%d) : Assert failed: [%s] [%s]\n", __FILE__, __LINE__, #f, msg); 83 | 84 | /** Send "comment" string to the debugger for display. */ 85 | #define SMTG_WARNING(comment) FDebugPrint ("%s(%d) : %s\n", __FILE__, __LINE__, comment); 86 | 87 | /** Send the last error string to the debugger for display. */ 88 | #define SMTG_PRINTSYSERROR FPrintLastError (__FILE__, __LINE__); 89 | 90 | /** If a debugger is present, send string "s" to the debugger for display and 91 | cause a breakpoint exception to occur in the current process. */ 92 | #define SMTG_DEBUGSTR(s) FDebugBreak (s); 93 | 94 | /** Use VERIFY for calling methods "f" having a bool result (expecting them to return 'true') 95 | The call of "f" is not removed in RELEASE builds, only the result verification. eg: SMTG_VERIFY 96 | (isValid ()) */ 97 | #define SMTG_VERIFY(f) SMTG_ASSERT (f) 98 | 99 | /** Use VERIFY_IS for calling methods "f" and expect a certain result "r". 100 | The call of "f" is not removed in RELEASE builds, only the result verification. eg: 101 | SMTG_VERIFY_IS (callMethod (), kResultOK) */ 102 | #define SMTG_VERIFY_IS(f, r) \ 103 | if ((f) != (r)) \ 104 | FDebugBreak ("%s(%d) : Assert failed: %s\n", __FILE__, __LINE__, #f); 105 | 106 | /** Use VERIFY_NOT for calling methods "f" and expect the result to be anything else but "r". 107 | The call of "f" is not removed in RELEASE builds, only the result verification. eg: 108 | SMTG_VERIFY_NOT (callMethod (), kResultError) */ 109 | #define SMTG_VERIFY_NOT(f, r) \ 110 | if ((f) == (r)) \ 111 | FDebugBreak ("%s(%d) : Assert failed: %s\n", __FILE__, __LINE__, #f); 112 | 113 | /** @name Shortcut macros for sending strings to the debugger for display. 114 | First parameter is always the format string (printf like). 115 | */ 116 | 117 | ///@{ 118 | #define SMTG_DBPRT0(a) FDebugPrint (a); 119 | #define SMTG_DBPRT1(a, b) FDebugPrint (a, b); 120 | #define SMTG_DBPRT2(a, b, c) FDebugPrint (a, b, c); 121 | #define SMTG_DBPRT3(a, b, c, d) FDebugPrint (a, b, c, d); 122 | #define SMTG_DBPRT4(a, b, c, d, e) FDebugPrint (a, b, c, d, e); 123 | #define SMTG_DBPRT5(a, b, c, d, e, f) FDebugPrint (a, b, c, d, e, f); 124 | ///@} 125 | 126 | /** @name Helper functions for the above defined macros. 127 | 128 | You shouldn't use them directly (if you do so, don't forget "#if DEVELOPMENT")! 129 | It is recommended to use the macros instead. 130 | */ 131 | ///@{ 132 | void FDebugPrint (const char* format, ...); 133 | void FDebugBreak (const char* format, ...); 134 | void FPrintLastError (const char* file, int line); 135 | ///@} 136 | 137 | /** @name Provide a custom assertion handler and debug print handler, eg 138 | so that we can provide an assert with a custom dialog, or redirect 139 | the debug output to a file or stream. 140 | */ 141 | ///@{ 142 | using AssertionHandler = bool (*) (const char* message); 143 | extern AssertionHandler gAssertionHandler; 144 | extern AssertionHandler gPreAssertionHook; 145 | using DebugPrintLogger = void (*) (const char* message); 146 | extern DebugPrintLogger gDebugPrintLogger; 147 | ///@} 148 | 149 | /** Definition of memory allocation macros: 150 | Use "NEW" to allocate storage for individual objects. 151 | Use "NEWVEC" to allocate storage for an array of objects. */ 152 | #if SMTG_OS_MACOS 153 | void* operator new (size_t, int, const char*, int); 154 | void* operator new[] (size_t, int, const char*, int); 155 | void operator delete (void* p, int, const char* file, int line); 156 | void operator delete[] (void* p, int, const char* file, int line); 157 | #ifndef NEW 158 | #define NEW new (1, __FILE__, __LINE__) 159 | #define NEWVEC new (1, __FILE__, __LINE__) 160 | #endif 161 | 162 | #define DEBUG_NEW DEBUG_NEW_LEAKS 163 | 164 | #elif SMTG_OS_WINDOWS && defined(_MSC_VER) 165 | #ifndef NEW 166 | void* operator new (size_t, int, const char*, int); 167 | #define NEW new (1, __FILE__, __LINE__) 168 | #define NEWVEC new (1, __FILE__, __LINE__) 169 | #endif 170 | 171 | #else 172 | #ifndef NEW 173 | #define NEW new 174 | #define NEWVEC new 175 | #endif 176 | #endif 177 | 178 | #else 179 | /** if DEVELOPMENT is not set, these macros will do nothing. */ 180 | #define SMTG_ASSERT(f) 181 | #define SMTG_ASSERT_MSG(f, msg) 182 | #define SMTG_WARNING(s) 183 | #define SMTG_PRINTSYSERROR 184 | #define SMTG_DEBUGSTR(s) 185 | #define SMTG_VERIFY(f) f; 186 | #define SMTG_VERIFY_IS(f, r) f; 187 | #define SMTG_VERIFY_NOT(f, r) f; 188 | 189 | #define SMTG_DBPRT0(a) 190 | #define SMTG_DBPRT1(a, b) 191 | #define SMTG_DBPRT2(a, b, c) 192 | #define SMTG_DBPRT3(a, b, c, d) 193 | #define SMTG_DBPRT4(a, b, c, d, e) 194 | #define SMTG_DBPRT5(a, b, c, d, e, f) 195 | 196 | #ifndef NEW 197 | #define NEW new 198 | #define NEWVEC new 199 | 200 | #endif 201 | #endif 202 | 203 | // replace #if SMTG_CPPUNIT_TESTING 204 | bool isSmtgUnitTesting (); 205 | void setSmtgUnitTesting (); 206 | 207 | #if !SMTG_RENAME_ASSERT 208 | #if SMTG_OS_WINDOWS 209 | #undef ASSERT 210 | #endif 211 | 212 | #define ASSERT SMTG_ASSERT 213 | #define WARNING SMTG_WARNING 214 | #define DEBUGSTR SMTG_DEBUGSTR 215 | #define VERIFY SMTG_VERIFY 216 | #define VERIFY_IS SMTG_VERIFY_IS 217 | #define VERIFY_NOT SMTG_VERIFY_NOT 218 | #define PRINTSYSERROR SMTG_PRINTSYSERROR 219 | 220 | #define DBPRT0 SMTG_DBPRT0 221 | #define DBPRT1 SMTG_DBPRT1 222 | #define DBPRT2 SMTG_DBPRT2 223 | #define DBPRT3 SMTG_DBPRT3 224 | #define DBPRT4 SMTG_DBPRT4 225 | #define DBPRT5 SMTG_DBPRT5 226 | #endif 227 | -------------------------------------------------------------------------------- /source/fobject.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Flags : clang-format SMTGSequencer 3 | // Project : SDK Base 4 | // Version : 1.0 5 | // 6 | // Category : Helpers 7 | // Filename : base/source/fobject.cpp 8 | // Created by : Steinberg, 2008 9 | // Description : Basic Object implementing FUnknown 10 | // 11 | //----------------------------------------------------------------------------- 12 | // This file is part of a Steinberg SDK. It is subject to the license terms 13 | // in the LICENSE file found in the top-level directory of this distribution 14 | // and at www.steinberg.net/sdklicenses. 15 | // No part of the SDK, including this file, may be copied, modified, propagated, 16 | // or distributed except according to the terms contained in the LICENSE file. 17 | //----------------------------------------------------------------------------- 18 | 19 | #include "base/source/fobject.h" 20 | #include "base/thread/include/flock.h" 21 | 22 | #include 23 | #include 24 | #define SMTG_VALIDATE_DEPENDENCY_COUNT DEVELOPMENT // validating dependencyCount 25 | 26 | #if SMTG_DEPENDENCY_COUNT 27 | #include "base/source/updatehandler.h" 28 | #define SMTG_DEPENDENCY_CHECK_LEVEL 1 // 1 => minimal assert, 2 => full assert 29 | #endif // SMTG_DEPENDENCY_COUNT 30 | 31 | namespace Steinberg { 32 | 33 | // Entry point for addRef/release tracking. See fobjecttracker.h 34 | //------------------------------------------------------------------------ 35 | #if DEVELOPMENT 36 | using FObjectTrackerFn = std::function; 37 | FObjectTrackerFn gFObjectTracker = nullptr; 38 | #endif 39 | 40 | IUpdateHandler* FObject::gUpdateHandler = nullptr; 41 | 42 | //------------------------------------------------------------------------ 43 | const FUID FObject::iid; 44 | 45 | //------------------------------------------------------------------------ 46 | struct FObjectIIDInitializer 47 | { 48 | // the object iid is always generated so that different components 49 | // only can cast to their own objects 50 | // this initializer must be after the definition of FObject::iid, otherwise 51 | // the default constructor of FUID will clear the generated iid 52 | FObjectIIDInitializer () { const_cast (FObject::iid).generate (); } 53 | } gFObjectIidInitializer; 54 | 55 | //------------------------------------------------------------------------ 56 | FObject::~FObject () 57 | { 58 | #if SMTG_DEPENDENCY_COUNT && DEVELOPMENT 59 | static bool localNeverDebugger = false; 60 | #endif 61 | 62 | #if DEVELOPMENT 63 | if (refCount > 1) 64 | FDebugPrint ("Refcount is %d when trying to delete %s\n", refCount, isA ()); 65 | #endif 66 | 67 | #if SMTG_DEPENDENCY_COUNT 68 | #if SMTG_DEPENDENCY_CHECK_LEVEL >= 1 69 | if (gUpdateHandler) 70 | { 71 | #if DEVELOPMENT 72 | SMTG_ASSERT (dependencyCount == 0 || localNeverDebugger); 73 | #endif // DEVELOPMENT 74 | } 75 | #endif 76 | #endif // SMTG_DEPENDENCY_COUNT 77 | 78 | #if SMTG_VALIDATE_DEPENDENCY_COUNT 79 | if (!gUpdateHandler || gUpdateHandler != UpdateHandler::instance (false)) 80 | return; 81 | 82 | auto updateHandler = UpdateHandler::instance (); 83 | if (!updateHandler || updateHandler == this) 84 | return; 85 | 86 | SMTG_ASSERT ((updateHandler->checkDeferred (this) == false || localNeverDebugger) && 87 | "'this' has scheduled a deferUpdate that was not yet delivered"); 88 | 89 | if (updateHandler->hasDependencies (this)) 90 | { 91 | SMTG_ASSERT ( 92 | (false || localNeverDebugger) && 93 | "Another object is still dependent on 'this'. This leads to zombie entries in the dependency map that can later crash."); 94 | FDebugPrint ("Object still has dependencies %x %s\n", this, this->isA ()); 95 | updateHandler->printForObject (this); 96 | } 97 | #endif // SMTG_VALIDATE_DEPENDENCY_COUNT 98 | } 99 | 100 | //------------------------------------------------------------------------ 101 | uint32 PLUGIN_API FObject::addRef () 102 | { 103 | #if DEVELOPMENT 104 | if (gFObjectTracker) 105 | { 106 | gFObjectTracker (this, true); 107 | } 108 | #endif 109 | 110 | return FUnknownPrivate::atomicAdd (refCount, 1); 111 | } 112 | 113 | //------------------------------------------------------------------------ 114 | uint32 PLUGIN_API FObject::release () 115 | { 116 | #if DEVELOPMENT 117 | if (gFObjectTracker) 118 | { 119 | gFObjectTracker (this, false); 120 | } 121 | #endif 122 | 123 | auto rc = FUnknownPrivate::atomicAdd (refCount, -1); 124 | if (rc == 0) 125 | { 126 | refCount = -1000; 127 | delete this; 128 | return 0; 129 | } 130 | return rc; 131 | } 132 | 133 | //------------------------------------------------------------------------ 134 | tresult PLUGIN_API FObject::queryInterface (const TUID _iid, void** obj) 135 | { 136 | QUERY_INTERFACE (_iid, obj, FUnknown::iid, FUnknown) 137 | QUERY_INTERFACE (_iid, obj, IDependent::iid, IDependent) 138 | QUERY_INTERFACE (_iid, obj, FObject::iid, FObject) 139 | *obj = nullptr; 140 | return kNoInterface; 141 | } 142 | 143 | //------------------------------------------------------------------------ 144 | void FObject::addDependent (IDependent* dep) 145 | { 146 | if (!gUpdateHandler) 147 | return; 148 | 149 | gUpdateHandler->addDependent (unknownCast (), dep); 150 | #if SMTG_DEPENDENCY_COUNT 151 | dependencyCount++; 152 | #endif 153 | } 154 | 155 | //------------------------------------------------------------------------ 156 | void FObject::removeDependent (IDependent* dep) 157 | { 158 | #if SMTG_DEPENDENCY_COUNT && DEVELOPMENT 159 | static bool localNeverDebugger = false; 160 | #endif 161 | 162 | if (!gUpdateHandler) 163 | return; 164 | 165 | #if SMTG_DEPENDENCY_COUNT 166 | if (gUpdateHandler != UpdateHandler::instance (false)) 167 | { 168 | gUpdateHandler->removeDependent (unknownCast (), dep); 169 | dependencyCount--; 170 | return; 171 | } 172 | #if SMTG_DEPENDENCY_CHECK_LEVEL > 1 173 | SMTG_ASSERT ((dependencyCount > 0 || localNeverDebugger) && 174 | "All dependencies have already been removed - mmichaelis 7/2021"); 175 | #endif 176 | size_t removeCount; 177 | UpdateHandler::instance ()->removeDependent (unknownCast (), dep, removeCount); 178 | if (removeCount == 0) 179 | { 180 | #if SMTG_DEPENDENCY_CHECK_LEVEL > 1 181 | SMTG_ASSERT (localNeverDebugger && "No dependency to remove - ygrabit 8/2021"); 182 | #endif 183 | } 184 | else 185 | { 186 | SMTG_ASSERT ((removeCount == 1 || localNeverDebugger) && 187 | "Duplicated dependencies established - mmichaelis 7/2021"); 188 | } 189 | dependencyCount -= (int16)removeCount; 190 | #else 191 | gUpdateHandler->removeDependent (unknownCast (), dep); 192 | #endif // SMTG_DEPENDENCY_COUNT 193 | } 194 | 195 | //------------------------------------------------------------------------ 196 | void FObject::changed (int32 msg) 197 | { 198 | if (gUpdateHandler) 199 | gUpdateHandler->triggerUpdates (unknownCast (), msg); 200 | else 201 | updateDone (msg); 202 | } 203 | 204 | //------------------------------------------------------------------------ 205 | void FObject::deferUpdate (int32 msg) 206 | { 207 | if (gUpdateHandler) 208 | gUpdateHandler->deferUpdates (unknownCast (), msg); 209 | else 210 | updateDone (msg); 211 | } 212 | 213 | //------------------------------------------------------------------------ 214 | /** Automatic creation and destruction of singleton instances. */ 215 | //------------------------------------------------------------------------ 216 | namespace Singleton { 217 | using ObjectVector = std::vector; 218 | ObjectVector* singletonInstances = nullptr; 219 | bool singletonsTerminated = false; 220 | Steinberg::Base::Thread::FLock* singletonsLock; 221 | 222 | bool isTerminated () 223 | { 224 | return singletonsTerminated; 225 | } 226 | 227 | void lockRegister () 228 | { 229 | if (!singletonsLock) // assume first call not from multiple threads 230 | singletonsLock = NEW Steinberg::Base::Thread::FLock; 231 | singletonsLock->lock (); 232 | } 233 | 234 | void unlockRegister () 235 | { 236 | singletonsLock->unlock (); 237 | } 238 | 239 | void registerInstance (FObject** o) 240 | { 241 | SMTG_ASSERT (singletonsTerminated == false) 242 | if (singletonsTerminated == false) 243 | { 244 | if (singletonInstances == nullptr) 245 | singletonInstances = NEW std::vector; 246 | singletonInstances->push_back (o); 247 | } 248 | } 249 | 250 | struct Deleter 251 | { 252 | ~Deleter () 253 | { 254 | singletonsTerminated = true; 255 | if (singletonInstances) 256 | { 257 | for (Steinberg::FObject** obj : *singletonInstances) 258 | { 259 | (*obj)->release (); 260 | *obj = nullptr; 261 | obj = nullptr; 262 | } 263 | 264 | delete singletonInstances; 265 | singletonInstances = nullptr; 266 | } 267 | delete singletonsLock; 268 | singletonsLock = nullptr; 269 | } 270 | } deleter; 271 | } 272 | 273 | //------------------------------------------------------------------------ 274 | } // namespace Steinberg 275 | -------------------------------------------------------------------------------- /source/fdebug.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Project : SDK Base 3 | // Version : 1.0 4 | // 5 | // Category : Helpers 6 | // Filename : base/source/fdebug.cpp 7 | // Created by : Steinberg, 1995 8 | // Description : There are 2 levels of debugging messages: 9 | // DEVELOPMENT During development 10 | // RELEASE Program is shipping. 11 | // 12 | //----------------------------------------------------------------------------- 13 | // This file is part of a Steinberg SDK. It is subject to the license terms 14 | // in the LICENSE file found in the top-level directory of this distribution 15 | // and at www.steinberg.net/sdklicenses. 16 | // No part of the SDK, including this file, may be copied, modified, propagated, 17 | // or distributed except according to the terms contained in the LICENSE file. 18 | //----------------------------------------------------------------------------- 19 | 20 | #include "base/source/fdebug.h" 21 | 22 | #if SMTG_OS_WINDOWS 23 | #include 24 | 25 | bool AmIBeingDebugged () 26 | { 27 | return IsDebuggerPresent (); 28 | } 29 | #endif 30 | 31 | #if SMTG_OS_LINUX 32 | #include 33 | #include 34 | #include 35 | //-------------------------------------------------------------------------- 36 | bool AmIBeingDebugged () 37 | { 38 | // TODO: check if GDB or LLDB is attached 39 | return true; 40 | } 41 | #endif 42 | 43 | #if SMTG_OS_MACOS 44 | 45 | #include 46 | #include 47 | #include 48 | #include 49 | 50 | //------------------------------------------------------------------------ 51 | // from Technical Q&A QA1361 (http://developer.apple.com/qa/qa2004/qa1361.html) 52 | //------------------------------------------------------------------------ 53 | bool AmIBeingDebugged () 54 | // Returns true if the current process is being debugged (either 55 | // running under the debugger or has a debugger attached post facto). 56 | { 57 | int mib[4]; 58 | struct kinfo_proc info; 59 | size_t size; 60 | 61 | // Initialize the flags so that, if sysctl fails for some bizarre 62 | // reason, we get a predictable result. 63 | 64 | info.kp_proc.p_flag = 0; 65 | 66 | // Initialize mib, which tells sysctl the info we want, in this case 67 | // we're looking for information about a specific process ID. 68 | 69 | mib[0] = CTL_KERN; 70 | mib[1] = KERN_PROC; 71 | mib[2] = KERN_PROC_PID; 72 | mib[3] = getpid (); 73 | 74 | // Call sysctl. 75 | 76 | size = sizeof (info); 77 | sysctl (mib, sizeof (mib) / sizeof (*mib), &info, &size, NULL, 0); 78 | 79 | // We're being debugged if the P_TRACED flag is set. 80 | return ((info.kp_proc.p_flag & P_TRACED) != 0); 81 | } 82 | 83 | #endif // SMTG_OS_MACOS 84 | 85 | #if DEVELOPMENT 86 | 87 | #include 88 | #include 89 | #include 90 | #include 91 | #include 92 | 93 | #if SMTG_OS_WINDOWS 94 | #ifndef _WIN32_WINNT 95 | #define _WIN32_WINNT 0x0400 96 | #endif 97 | #if _MSC_VER 98 | #include 99 | #endif 100 | #define vsnprintf _vsnprintf 101 | #define snprintf _snprintf 102 | 103 | #elif SMTG_OS_MACOS 104 | #include 105 | #include 106 | #include 107 | #include 108 | #include 109 | 110 | #define THREAD_ALLOC_WATCH 0 // check allocations on specific threads 111 | 112 | #if THREAD_ALLOC_WATCH 113 | mach_port_t watchThreadID = 0; 114 | #endif 115 | 116 | #endif 117 | 118 | AssertionHandler gAssertionHandler = nullptr; 119 | AssertionHandler gPreAssertionHook = nullptr; 120 | DebugPrintLogger gDebugPrintLogger = nullptr; 121 | 122 | //-------------------------------------------------------------------------- 123 | static const int kDebugPrintfBufferSize = 10000; 124 | static bool neverDebugger = false; // so I can switch it off in the debugger... 125 | 126 | static std::once_flag neverDebuggerEnvCheckFlag {}; 127 | 128 | //-------------------------------------------------------------------------- 129 | static void initNeverDebugger () 130 | { 131 | std::call_once (neverDebuggerEnvCheckFlag, [] () { 132 | // add this environment variable to not stop in the debugger on ASSERT 133 | if (std::getenv ("SMTG_DEBUG_IGNORE_ASSERT")) 134 | { 135 | neverDebugger = true; 136 | } 137 | }); 138 | } 139 | 140 | //-------------------------------------------------------------------------- 141 | static void printDebugString (const char* string) 142 | { 143 | if (!string) 144 | return; 145 | 146 | if (gDebugPrintLogger) 147 | { 148 | gDebugPrintLogger (string); 149 | } 150 | else 151 | { 152 | #if SMTG_OS_MACOS || defined(__MINGW32__) 153 | fprintf (stderr, "%s", string); 154 | #elif SMTG_OS_WINDOWS 155 | OutputDebugStringA (string); 156 | #endif 157 | } 158 | } 159 | 160 | //-------------------------------------------------------------------------- 161 | // printf style debugging output 162 | //-------------------------------------------------------------------------- 163 | void FDebugPrint (const char* format, ...) 164 | { 165 | char string[kDebugPrintfBufferSize]; 166 | va_list marker; 167 | va_start (marker, format); 168 | vsnprintf (string, kDebugPrintfBufferSize, format, marker); 169 | 170 | printDebugString (string); 171 | } 172 | 173 | //-------------------------------------------------------------------------- 174 | // printf style debugging output 175 | //-------------------------------------------------------------------------- 176 | void FDebugBreak (const char* format, ...) 177 | { 178 | char string[kDebugPrintfBufferSize]; 179 | va_list marker; 180 | va_start (marker, format); 181 | vsnprintf (string, kDebugPrintfBufferSize, format, marker); 182 | 183 | printDebugString (string); 184 | 185 | // The Pre-assertion hook is always called, even if we're not running in the debugger, 186 | // so that we can log asserts without displaying them 187 | if (gPreAssertionHook) 188 | { 189 | gPreAssertionHook (string); 190 | } 191 | 192 | initNeverDebugger (); 193 | if (neverDebugger) 194 | return; 195 | if (AmIBeingDebugged ()) 196 | { 197 | // do not crash if no debugger present 198 | // If there is an assertion handler defined then let this override the UI 199 | // and tell us whether we want to break into the debugger 200 | bool breakIntoDebugger = true; 201 | if (gAssertionHandler && gAssertionHandler (string) == false) 202 | { 203 | breakIntoDebugger = false; 204 | } 205 | 206 | if (breakIntoDebugger) 207 | { 208 | #if SMTG_OS_WINDOWS && _MSC_VER 209 | __debugbreak (); // intrinsic version of DebugBreak() 210 | #elif SMTG_OS_MACOS && __arm64__ 211 | raise (SIGSTOP); 212 | 213 | #elif __ppc64__ || __ppc__ || __arm__ 214 | kill (getpid (), SIGINT); 215 | #elif __i386__ || __x86_64__ 216 | { 217 | __asm__ volatile ("int3"); 218 | } 219 | #endif 220 | } 221 | } 222 | } 223 | 224 | //-------------------------------------------------------------------------- 225 | void FPrintLastError (const char* file, int line) 226 | { 227 | #if SMTG_OS_WINDOWS 228 | LPVOID lpMessageBuffer = nullptr; 229 | FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, nullptr, 230 | GetLastError (), MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), 231 | (LPSTR)&lpMessageBuffer, 0, nullptr); 232 | FDebugPrint ("%s(%d) : %s\n", file, line, lpMessageBuffer); 233 | LocalFree (lpMessageBuffer); 234 | #endif 235 | 236 | #if SMTG_OS_MACOS 237 | #if !__MACH__ 238 | extern int errno; 239 | #endif 240 | FDebugPrint ("%s(%d) : Errno %d\n", file, line, errno); 241 | #endif 242 | } 243 | 244 | #if SMTG_OS_MACOS 245 | 246 | //------------------------------------------------------------------------ 247 | void* operator new (size_t size, int, const char* file, int line) 248 | { 249 | #if THREAD_ALLOC_WATCH 250 | mach_port_t threadID = mach_thread_self (); 251 | if (watchThreadID == threadID) 252 | { 253 | FDebugPrint ("Watched Thread Allocation : %s (Line:%d)\n", file ? file : "Unknown", line); 254 | } 255 | #endif 256 | try 257 | { 258 | return ::operator new (size); 259 | } 260 | catch (std::bad_alloc exception) 261 | { 262 | FDebugPrint ("bad_alloc exception : %s (Line:%d)", file ? file : "Unknown", line); 263 | } 264 | return (void*)-1; 265 | } 266 | 267 | //------------------------------------------------------------------------ 268 | void* operator new[] (size_t size, int, const char* file, int line) 269 | { 270 | #if THREAD_ALLOC_WATCH 271 | mach_port_t threadID = mach_thread_self (); 272 | if (watchThreadID == threadID) 273 | { 274 | FDebugPrint ("Watched Thread Allocation : %s (Line:%d)\n", file ? file : "Unknown", line); 275 | } 276 | #endif 277 | try 278 | { 279 | return ::operator new[] (size); 280 | } 281 | catch (std::bad_alloc exception) 282 | { 283 | FDebugPrint ("bad_alloc exception : %s (Line:%d)", file ? file : "Unknown", line); 284 | } 285 | return (void*)-1; 286 | } 287 | 288 | //------------------------------------------------------------------------ 289 | void operator delete (void* p, int, const char* file, int line) 290 | { 291 | (void)file; 292 | (void)line; 293 | ::operator delete (p); 294 | } 295 | 296 | //------------------------------------------------------------------------ 297 | void operator delete[] (void* p, int, const char* file, int line) 298 | { 299 | (void)file; 300 | (void)line; 301 | ::operator delete[] (p); 302 | } 303 | 304 | #endif // SMTG_OS_MACOS 305 | 306 | #endif // DEVELOPMENT 307 | 308 | static bool smtg_unit_testing_active = false; // ugly hack to unit testing ... 309 | 310 | //------------------------------------------------------------------------ 311 | bool isSmtgUnitTesting () 312 | { 313 | return smtg_unit_testing_active; 314 | } 315 | 316 | //------------------------------------------------------------------------ 317 | void setSmtgUnitTesting () 318 | { 319 | smtg_unit_testing_active = true; 320 | } 321 | -------------------------------------------------------------------------------- /source/timer.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Project : SDK Base 3 | // Version : 1.0 4 | // 5 | // Category : Helpers 6 | // Filename : base/source/timer.cpp 7 | // Created by : Steinberg, 05/2006 8 | // Description : Timer class for receiving triggers at regular intervals 9 | // 10 | //----------------------------------------------------------------------------- 11 | // This file is part of a Steinberg SDK. It is subject to the license terms 12 | // in the LICENSE file found in the top-level directory of this distribution 13 | // and at www.steinberg.net/sdklicenses. 14 | // No part of the SDK, including this file, may be copied, modified, propagated, 15 | // or distributed except according to the terms contained in the LICENSE file. 16 | //----------------------------------------------------------------------------- 17 | 18 | #include "base/source/timer.h" 19 | 20 | namespace Steinberg { 21 | static bool timersEnabled = true; 22 | 23 | //------------------------------------------------------------------------ 24 | DisableDispatchingTimers::DisableDispatchingTimers () 25 | { 26 | oldState = timersEnabled; 27 | timersEnabled = false; 28 | } 29 | 30 | //------------------------------------------------------------------------ 31 | DisableDispatchingTimers::~DisableDispatchingTimers () 32 | { 33 | timersEnabled = oldState; 34 | } 35 | 36 | //------------------------------------------------------------------------ 37 | namespace SystemTime { 38 | 39 | //------------------------------------------------------------------------ 40 | struct ZeroStartTicks 41 | { 42 | static const uint64 startTicks; 43 | 44 | static int32 getTicks32 () 45 | { 46 | return static_cast (SystemTime::getTicks64 () - startTicks); 47 | } 48 | }; 49 | const uint64 ZeroStartTicks::startTicks = SystemTime::getTicks64 (); 50 | 51 | //------------------------------------------------------------------------ 52 | int32 getTicks () 53 | { 54 | return ZeroStartTicks::getTicks32 (); 55 | } 56 | 57 | //------------------------------------------------------------------------ 58 | } // namespace SystemTime 59 | } // namespace Steinberg 60 | 61 | 62 | #if SMTG_OS_MACOS 63 | #include 64 | #include 65 | 66 | #ifdef verify 67 | #undef verify 68 | #endif 69 | 70 | namespace Steinberg { 71 | namespace SystemTime { 72 | 73 | //------------------------------------------------------------------------ 74 | struct MachTimeBase 75 | { 76 | private: 77 | struct mach_timebase_info timebaseInfo; 78 | 79 | MachTimeBase () { mach_timebase_info (&timebaseInfo); } 80 | 81 | static const MachTimeBase& instance () 82 | { 83 | static MachTimeBase gInstance; 84 | return gInstance; 85 | } 86 | 87 | public: 88 | static double getTimeNanos () 89 | { 90 | const MachTimeBase& timeBase = instance (); 91 | double absTime = static_cast (mach_absolute_time ()); 92 | // nano seconds 93 | double d = (absTime / timeBase.timebaseInfo.denom) * timeBase.timebaseInfo.numer; 94 | return d; 95 | } 96 | }; 97 | 98 | /* 99 | @return the current system time in milliseconds 100 | */ 101 | uint64 getTicks64 () 102 | { 103 | return static_cast (MachTimeBase::getTimeNanos () / 1000000.); 104 | } 105 | //------------------------------------------------------------------------ 106 | } // namespace SystemTime 107 | 108 | //------------------------------------------------------------------------ 109 | class MacPlatformTimer : public Timer 110 | { 111 | public: 112 | MacPlatformTimer (ITimerCallback* callback, uint32 milliseconds); 113 | ~MacPlatformTimer (); 114 | 115 | void stop () override; 116 | bool verify () const { return platformTimer != nullptr; } 117 | 118 | static void timerCallback (CFRunLoopTimerRef timer, void* info); 119 | 120 | protected: 121 | CFRunLoopTimerRef platformTimer; 122 | ITimerCallback* callback; 123 | }; 124 | 125 | //------------------------------------------------------------------------ 126 | MacPlatformTimer::MacPlatformTimer (ITimerCallback* callback, uint32 milliseconds) 127 | : platformTimer (nullptr), callback (callback) 128 | { 129 | if (callback) 130 | { 131 | CFRunLoopTimerContext timerContext = {}; 132 | timerContext.info = this; 133 | platformTimer = CFRunLoopTimerCreate ( 134 | kCFAllocatorDefault, CFAbsoluteTimeGetCurrent () + milliseconds * 0.001, 135 | milliseconds * 0.001f, 0, 0, timerCallback, &timerContext); 136 | if (platformTimer) 137 | CFRunLoopAddTimer (CFRunLoopGetMain (), platformTimer, kCFRunLoopCommonModes); 138 | } 139 | } 140 | 141 | //------------------------------------------------------------------------ 142 | MacPlatformTimer::~MacPlatformTimer () 143 | { 144 | stop (); 145 | } 146 | 147 | //------------------------------------------------------------------------ 148 | void MacPlatformTimer::stop () 149 | { 150 | if (platformTimer) 151 | { 152 | CFRunLoopRemoveTimer (CFRunLoopGetMain (), platformTimer, kCFRunLoopCommonModes); 153 | CFRelease (platformTimer); 154 | platformTimer = nullptr; 155 | } 156 | } 157 | 158 | //------------------------------------------------------------------------ 159 | void MacPlatformTimer::timerCallback (CFRunLoopTimerRef, void* info) 160 | { 161 | if (timersEnabled) 162 | { 163 | if (auto timer = (MacPlatformTimer*)info) 164 | timer->callback->onTimer (timer); 165 | } 166 | } 167 | 168 | //------------------------------------------------------------------------ 169 | Timer* Timer::create (ITimerCallback* callback, uint32 milliseconds) 170 | { 171 | auto timer = NEW MacPlatformTimer (callback, milliseconds); 172 | if (timer->verify ()) 173 | return timer; 174 | timer->release (); 175 | return nullptr; 176 | } 177 | //------------------------------------------------------------------------ 178 | } // namespace Steinberg 179 | 180 | #elif SMTG_OS_WINDOWS 181 | 182 | #include 183 | #include 184 | #include 185 | 186 | namespace Steinberg { 187 | namespace SystemTime { 188 | 189 | //------------------------------------------------------------------------ 190 | /* 191 | @return the current system time in milliseconds 192 | */ 193 | uint64 getTicks64 () 194 | { 195 | #if defined(__MINGW32__) 196 | return GetTickCount (); 197 | #else 198 | return GetTickCount64 (); 199 | #endif 200 | } // namespace SystemTime 201 | } // namespace Steinberg 202 | 203 | class WinPlatformTimer; 204 | using WinPlatformTimerList = std::list; 205 | 206 | //------------------------------------------------------------------------ 207 | // WinPlatformTimer 208 | //------------------------------------------------------------------------ 209 | class WinPlatformTimer : public Timer 210 | { 211 | public: 212 | //------------------------------------------------------------------------ 213 | WinPlatformTimer (ITimerCallback* callback, uint32 milliseconds); 214 | ~WinPlatformTimer () override; 215 | 216 | void stop () override; 217 | bool verify () const { return id != 0; } 218 | 219 | //------------------------------------------------------------------------ 220 | private: 221 | UINT_PTR id; 222 | ITimerCallback* callback; 223 | 224 | static void addTimer (WinPlatformTimer* t); 225 | static void removeTimer (WinPlatformTimer* t); 226 | 227 | static void CALLBACK TimerProc (HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime); 228 | static WinPlatformTimerList* timers; 229 | }; 230 | 231 | //------------------------------------------------------------------------ 232 | WinPlatformTimerList* WinPlatformTimer::timers = nullptr; 233 | 234 | //------------------------------------------------------------------------ 235 | WinPlatformTimer::WinPlatformTimer (ITimerCallback* callback, uint32 milliseconds) 236 | : callback (callback) 237 | { 238 | id = SetTimer (nullptr, 0, milliseconds, TimerProc); 239 | if (id) 240 | addTimer (this); 241 | } 242 | 243 | //------------------------------------------------------------------------ 244 | WinPlatformTimer::~WinPlatformTimer () 245 | { 246 | stop (); 247 | } 248 | 249 | //------------------------------------------------------------------------ 250 | void WinPlatformTimer::addTimer (WinPlatformTimer* t) 251 | { 252 | if (timers == nullptr) 253 | timers = NEW WinPlatformTimerList; 254 | timers->push_back (t); 255 | } 256 | 257 | //------------------------------------------------------------------------ 258 | void WinPlatformTimer::removeTimer (WinPlatformTimer* t) 259 | { 260 | if (!timers) 261 | return; 262 | 263 | WinPlatformTimerList::iterator it = std::find (timers->begin (), timers->end (), t); 264 | if (it != timers->end ()) 265 | timers->erase (it); 266 | if (timers->empty ()) 267 | { 268 | delete timers; 269 | timers = nullptr; 270 | } 271 | } 272 | 273 | //------------------------------------------------------------------------ 274 | void WinPlatformTimer::stop () 275 | { 276 | if (!id) 277 | return; 278 | 279 | KillTimer (nullptr, id); 280 | removeTimer (this); 281 | id = 0; 282 | } 283 | 284 | //------------------------------------------------------------------------ 285 | void CALLBACK WinPlatformTimer::TimerProc (HWND /*hwnd*/, UINT /*uMsg*/, UINT_PTR idEvent, 286 | DWORD /*dwTime*/) 287 | { 288 | if (timersEnabled && timers) 289 | { 290 | WinPlatformTimerList::const_iterator it = timers->cbegin (); 291 | while (it != timers->cend ()) 292 | { 293 | WinPlatformTimer* timer = *it; 294 | if (timer->id == idEvent) 295 | { 296 | if (timer->callback) 297 | timer->callback->onTimer (timer); 298 | return; 299 | } 300 | ++it; 301 | } 302 | } 303 | } 304 | 305 | //------------------------------------------------------------------------ 306 | Timer* Timer::create (ITimerCallback* callback, uint32 milliseconds) 307 | { 308 | auto* platformTimer = NEW WinPlatformTimer (callback, milliseconds); 309 | if (platformTimer->verify ()) 310 | return platformTimer; 311 | platformTimer->release (); 312 | return nullptr; 313 | } 314 | 315 | //------------------------------------------------------------------------ 316 | } // namespace Steinberg 317 | 318 | #elif SMTG_OS_LINUX 319 | 320 | #include 321 | #include 322 | 323 | namespace Steinberg { 324 | namespace SystemTime { 325 | 326 | //------------------------------------------------------------------------ 327 | /* 328 | @return the current system time in milliseconds 329 | */ 330 | uint64 getTicks64 () 331 | { 332 | struct timespec ts; 333 | clock_gettime (CLOCK_MONOTONIC, &ts); 334 | return static_cast (ts.tv_sec) * 1000 + static_cast (ts.tv_nsec) / 1000000; 335 | } 336 | //------------------------------------------------------------------------ 337 | } // namespace SystemTime 338 | 339 | static CreateTimerFunc createTimerFunc = nullptr; 340 | 341 | //------------------------------------------------------------------------ 342 | void InjectCreateTimerFunction (CreateTimerFunc f) 343 | { 344 | createTimerFunc = f; 345 | } 346 | 347 | //------------------------------------------------------------------------ 348 | Timer* Timer::create (ITimerCallback* callback, uint32 milliseconds) 349 | { 350 | if (createTimerFunc) 351 | return createTimerFunc (callback, milliseconds); 352 | return nullptr; 353 | } 354 | 355 | //------------------------------------------------------------------------ 356 | } // namespace Steinberg 357 | 358 | #endif 359 | -------------------------------------------------------------------------------- /source/fcleanup.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Project : SDK Base 3 | // Version : 1.0 4 | // 5 | // Category : Helpers 6 | // Filename : base/source/fcleanup.h 7 | // Created by : Steinberg, 2008 8 | // Description : Template classes for automatic resource cleanup 9 | // 10 | //----------------------------------------------------------------------------- 11 | // This file is part of a Steinberg SDK. It is subject to the license terms 12 | // in the LICENSE file found in the top-level directory of this distribution 13 | // and at www.steinberg.net/sdklicenses. 14 | // No part of the SDK, including this file, may be copied, modified, propagated, 15 | // or distributed except according to the terms contained in the LICENSE file. 16 | //----------------------------------------------------------------------------- 17 | 18 | #pragma once 19 | 20 | #include 21 | 22 | namespace Steinberg { 23 | 24 | /** Template definition for classes that help guarding against memory leaks. 25 | A stack allocated object of this type automatically deletes an at construction time passed 26 | dynamically allocated single object when it reaches the end of its scope. \n\n 27 | Intended usage: 28 | \code 29 | { 30 | int* pointerToInt = new int; 31 | Steinberg::FDeleter deleter (pointerToInt); 32 | 33 | // Do something with the variable behind pointerToInt. 34 | 35 | } // No memory leak here, destructor of deleter cleans up the integer. 36 | \endcode 37 | */ 38 | //------------------------------------------------------------------------ 39 | template 40 | struct FDeleter 41 | { 42 | /// Constructor. _toDelete is a pointer to the dynamically allocated object that is to be 43 | /// deleted when this FDeleter object's destructor is executed. 44 | FDeleter (T* _toDelete) : toDelete (_toDelete) {} 45 | /// Destructor. Calls delete on the at construction time passed pointer. 46 | ~FDeleter () 47 | { 48 | if (toDelete) 49 | delete toDelete; 50 | } 51 | T* toDelete; ///< Remembers the object that is to be deleted during destruction. 52 | }; 53 | 54 | /** Template definition for classes that help guarding against memory leaks. 55 | A stack allocated object of this type automatically deletes an at construction time passed 56 | dynamically allocated array of objects when it reaches the end of its scope. \n\n 57 | Intended usage: 58 | \code 59 | { 60 | int* pointerToIntArray = new int[10]; 61 | Steinberg::FArrayDeleter deleter (pointerToIntArray); 62 | 63 | // Do something with the array behind pointerToIntArray. 64 | 65 | } // No memory leak here, destructor of deleter cleans up the integer array. 66 | \endcode 67 | */ 68 | //------------------------------------------------------------------------ 69 | template 70 | struct FArrayDeleter 71 | { 72 | /// Constructor. _arrayToDelete is a pointer to the dynamically allocated array of objects that 73 | /// is to be deleted when this FArrayDeleter object's destructor is executed. 74 | FArrayDeleter (T* _arrayToDelete) : arrayToDelete (_arrayToDelete) {} 75 | 76 | /// Destructor. Calls delete[] on the at construction time passed pointer. 77 | ~FArrayDeleter () 78 | { 79 | if (arrayToDelete) 80 | delete[] arrayToDelete; 81 | } 82 | 83 | T* arrayToDelete; ///< Remembers the array of objects that is to be deleted during destruction. 84 | }; 85 | 86 | /** Template definition for classes that help guarding against dangling pointers. 87 | A stack allocated object of this type automatically resets an at construction time passed 88 | pointer to null when it reaches the end of its scope. \n\n 89 | Intended usage: 90 | \code 91 | int* pointerToInt = 0; 92 | { 93 | int i = 1; 94 | pointerToInt = &i; 95 | Steinberg::FPtrNuller ptrNuller (pointerToInt); 96 | 97 | // Do something with pointerToInt. 98 | 99 | } // No dangling pointer here, pointerToInt is reset to 0 by destructor of ptrNuller. 100 | \endcode 101 | */ 102 | //------------------------------------------------------------------------ 103 | template 104 | struct FPtrNuller 105 | { 106 | /// Constructor. _toNull is a reference to the pointer that is to be reset to NULL when this 107 | /// FPtrNuller object's destructor is executed. 108 | FPtrNuller (T*& _toNull) : toNull (_toNull) {} 109 | /// Destructor. Calls delete[] on the at construction time passed pointer. 110 | ~FPtrNuller () { toNull = 0; } 111 | 112 | T*& toNull; ///< Remembers the pointer that is to be set to NULL during destruction. 113 | }; 114 | 115 | /** Template definition for classes that help resetting an object's value. 116 | A stack allocated object of this type automatically resets the value of an at construction time 117 | passed object to null when it reaches the end of its scope. \n\n Intended usage: \code int theObject 118 | = 0; 119 | { 120 | Steinberg::FNuller theNuller (theObject); 121 | 122 | theObject = 1; 123 | 124 | } // Here the destructor of theNuller resets the value of theObject to 0. 125 | \endcode 126 | */ 127 | //------------------------------------------------------------------------ 128 | template 129 | struct FNuller 130 | { 131 | /// Constructor. _toNull is a reference to the object that is to be assigned 0 when this FNuller 132 | /// object's destructor is executed. 133 | FNuller (T& _toNull) : toNull (_toNull) {} 134 | /// Destructor. Assigns 0 to the at construction time passed object reference. 135 | ~FNuller () { toNull = 0; } 136 | 137 | T& toNull; ///< Remembers the object that is to be assigned 0 during destruction. 138 | }; 139 | 140 | /** Class definition for objects that help resetting boolean variables. 141 | A stack allocated object of this type automatically sets an at construction time passed 142 | boolean variable immediately to TRUE and resets the same variable to FALSE when it 143 | reaches the end of its own scope. \n\n 144 | Intended usage: 145 | \code 146 | bool theBoolean = false; 147 | { 148 | Steinberg::FBoolSetter theBoolSetter (theBoolean); 149 | // Here the constructor of theBoolSetter sets theBoolean to TRUE. 150 | 151 | // Do something. 152 | 153 | } // Here the destructor of theBoolSetter resets theBoolean to FALSE. 154 | \endcode 155 | */ 156 | //------------------------------------------------------------------------ 157 | template 158 | struct FBooleanSetter 159 | { 160 | /// Constructor. _toSet is a reference to the boolean that is set to TRUE immediately in this 161 | /// constructor call and gets reset to FALSE when this FBoolSetter object's destructor is 162 | /// executed. 163 | FBooleanSetter (T& _toSet) : toSet (_toSet) { toSet = true; } 164 | /// Destructor. Resets the at construction time passed boolean to FALSE. 165 | ~FBooleanSetter () { toSet = false; } 166 | 167 | T& toSet; ///< Remembers the boolean that is to be reset during destruction. 168 | }; 169 | 170 | using FBoolSetter = FBooleanSetter; 171 | 172 | /** Class definition for objects that help setting boolean variables. 173 | A stack allocated object of this type automatically sets an at construction time passed 174 | boolean variable to TRUE if the given condition is met. At the end of its own scope the 175 | stack object will reset the same boolean variable to FALSE, if it wasn't set so already. \n\n 176 | Intended usage: 177 | \code 178 | bool theBoolean = false; 179 | { 180 | bool creativityFirst = true; 181 | Steinberg::FConditionalBoolSetter theCBSetter (theBoolean, creativityFirst); 182 | // Here the constructor of theCBSetter sets theBoolean to the value of creativityFirst. 183 | 184 | // Do something. 185 | 186 | } // Here the destructor of theCBSetter resets theBoolean to FALSE. 187 | \endcode 188 | */ 189 | //------------------------------------------------------------------------ 190 | struct FConditionalBoolSetter 191 | { 192 | /// Constructor. _toSet is a reference to the boolean that is to be set. If the in the second 193 | /// parameter given condition is TRUE then also _toSet is set to TRUE immediately. 194 | FConditionalBoolSetter (bool& _toSet, bool condition) : toSet (_toSet) 195 | { 196 | if (condition) 197 | toSet = true; 198 | } 199 | /// Destructor. Resets the at construction time passed boolean to FALSE. 200 | ~FConditionalBoolSetter () { toSet = false; } 201 | 202 | bool& toSet; ///< Remembers the boolean that is to be reset during destruction. 203 | }; 204 | 205 | /** Template definition for classes that help closing resources. 206 | A stack allocated object of this type automatically calls the close method of an at 207 | construction time passed object when it reaches the end of its scope. 208 | It goes without saying that the given type needs to have a close method. \n\n 209 | Intended usage: 210 | \code 211 | struct CloseableObject 212 | { 213 | void close() {}; 214 | }; 215 | 216 | { 217 | CloseableObject theObject; 218 | Steinberg::FCloser theCloser (&theObject); 219 | 220 | // Do something. 221 | 222 | } // Here the destructor of theCloser calls the close method of theObject. 223 | \endcode 224 | */ 225 | template 226 | struct FCloser 227 | { 228 | /// Constructor. _obj is the pointer on which close is to be called when this FCloser object's 229 | /// destructor is executed. 230 | FCloser (T* _obj) : obj (_obj) {} 231 | /// Destructor. Calls the close function on the at construction time passed pointer. 232 | ~FCloser () 233 | { 234 | if (obj) 235 | obj->close (); 236 | } 237 | 238 | T* obj; ///< Remembers the pointer on which close is to be called during destruction. 239 | }; 240 | 241 | /** Class definition for objects that help guarding against memory leaks. 242 | A stack allocated object of this type automatically frees the "malloced" memory behind an at 243 | construction time passed pointer when it reaches the end of its scope. 244 | */ 245 | //------------------------------------------------------------------------ 246 | /*! \class FMallocReleaser 247 | */ 248 | //------------------------------------------------------------------------ 249 | class FMallocReleaser 250 | { 251 | public: 252 | /// Constructor. _data is the pointer to the memory on which free is to be called when this 253 | /// FMallocReleaser object's destructor is executed. 254 | FMallocReleaser (void* _data) : data (_data) {} 255 | /// Destructor. Calls the free function on the at construction time passed pointer. 256 | ~FMallocReleaser () 257 | { 258 | if (data) 259 | free (data); 260 | } 261 | //------------------------------------------------------------------------ 262 | protected: 263 | void* data; ///< Remembers the pointer on which free is to be called during destruction. 264 | }; 265 | 266 | //------------------------------------------------------------------------ 267 | } // namespace Steinberg 268 | 269 | 270 | #if SMTG_OS_MACOS 271 | typedef const void* CFTypeRef; 272 | extern "C" { 273 | extern void CFRelease (CFTypeRef cf); 274 | } 275 | 276 | namespace Steinberg { 277 | 278 | /** Class definition for objects that helps releasing CoreFoundation objects. 279 | A stack allocated object of this type automatically releases an at construction time 280 | passed CoreFoundation object when it reaches the end of its scope. 281 | 282 | Only available on Macintosh platform. 283 | */ 284 | //------------------------------------------------------------------------ 285 | /*! \class CFReleaser 286 | */ 287 | //------------------------------------------------------------------------ 288 | class CFReleaser 289 | { 290 | public: 291 | /// Constructor. _obj is the reference to an CoreFoundation object which is to be released when this CFReleaser object's destructor is executed. 292 | CFReleaser (CFTypeRef _obj) : obj (_obj) {} 293 | /// Destructor. Releases the at construction time passed object. 294 | ~CFReleaser () { if (obj) CFRelease (obj); } 295 | protected: 296 | CFTypeRef obj; ///< Remembers the object which is to be released during destruction. 297 | }; 298 | 299 | //------------------------------------------------------------------------ 300 | } // namespace Steinberg 301 | 302 | #endif // SMTG_OS_MACOS 303 | -------------------------------------------------------------------------------- /source/fbuffer.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Project : SDK Base 3 | // Version : 1.0 4 | // 5 | // Category : Helpers 6 | // Filename : base/source/fbuffer.h 7 | // Created by : Steinberg, 2008 8 | // Description : 9 | // 10 | //----------------------------------------------------------------------------- 11 | // This file is part of a Steinberg SDK. It is subject to the license terms 12 | // in the LICENSE file found in the top-level directory of this distribution 13 | // and at www.steinberg.net/sdklicenses. 14 | // No part of the SDK, including this file, may be copied, modified, propagated, 15 | // or distributed except according to the terms contained in the LICENSE file. 16 | //----------------------------------------------------------------------------- 17 | 18 | #pragma once 19 | 20 | #include "pluginterfaces/base/ftypes.h" 21 | #include 22 | 23 | namespace Steinberg { 24 | class String; 25 | 26 | //------------------------------------------------------------------------ 27 | /** Buffer. 28 | @ingroup adt 29 | 30 | A Buffer is an object-oriented wrapper for a piece of memory. 31 | It adds several utility functions, e.g. for managing the size of the Buffer, 32 | appending or prepending values or strings to it. 33 | Internally it uses the standard memory functions malloc(), free(), etc. */ 34 | //------------------------------------------------------------------------ 35 | class Buffer 36 | { 37 | public: 38 | //--------------------------------------------------------------------- 39 | 40 | /** Default constructor, allocates no memory at all. 41 | */ 42 | Buffer (); 43 | 44 | /** Constructor - creates a new Buffer with a given size and copies contents from optional memory pointer. 45 | \param[in] b : optional memory pointer with the size of at least the given size 46 | \param[in] size : the size of the new Buffer to be allocated, in bytes. 47 | */ 48 | Buffer (const void* b, uint32 size); 49 | 50 | /** Constructor - creates a new Buffer with a given size and fills it all with a given value. 51 | \param[in] size : the size of the new Buffer to be allocated, in bytes. 52 | \param[in] initVal : the initial value the Buffer will be completely filled with 53 | */ 54 | Buffer (uint32 size, uint8 initVal); 55 | 56 | /** Constructor - creates a new Buffer with a given size. 57 | \param[in] size : the size of the new Buffer to be allocated, in bytes. 58 | */ 59 | Buffer (uint32 size); 60 | 61 | /** Copy constructor - creates a new Buffer from a given Buffer. 62 | \param[in] buff : the Buffer from which all memory will be copied to the new one 63 | */ 64 | Buffer (const Buffer& buff); 65 | 66 | /** Destructor - deallocates the internal memory. 67 | */ 68 | virtual ~Buffer (); 69 | 70 | /** Assignment operator - copies contents from a given Buffer and increases the size if necessary. 71 | \param[in] buff : the Buffer from which all memory will be copied 72 | */ 73 | void operator = (const Buffer& buff); 74 | 75 | /** Comparison operator - copies contents from a given Buffer and increases the size if necessary. 76 | \param[in] buff : the Buffer to be compared to 77 | \return true, if the given Buffer's content is equal to this one, else false 78 | */ 79 | bool operator == (const Buffer& buff)const; 80 | 81 | uint32 getSize () const {return memSize;} ///< \return the actual size of the Buffer's memory, in bytes. 82 | 83 | /** Sets a new size for this Buffer, keeping as much content as possible. 84 | \param[in] newSize : the new size for the Buffer, in bytes, newSize maybe zero 85 | \return true, if the new size could be adapted, else false 86 | */ 87 | bool setSize (uint32 newSize); 88 | 89 | /** Increases the Buffer to the next block, block size given by delta. 90 | \param[in] memSize : the new minimum size of the Buffer, newSize maybe zero 91 | \return true, if the Buffer could be grown successfully, else false 92 | */ 93 | bool grow (uint32 memSize); 94 | bool setMaxSize (uint32 size) {return grow (size);} ///< see \ref grow() 95 | 96 | void fillup (uint8 initVal = 0); ///< set from fillSize to end 97 | uint32 getFillSize ()const {return fillSize;} ///< \return the actual fill size 98 | bool setFillSize (uint32 c); ///< sets a new fill size, does not change any memory 99 | inline void flush () {setFillSize (0);} ///< sets fill size to zero 100 | bool truncateToFillSize (); ///< \return always true, truncates the size of the Buffer to the actual fill size 101 | 102 | bool isFull () const { return (fillSize == memSize); } ///< \return true, if all memory is filled up, else false 103 | uint32 getFree () const { return (memSize - fillSize); }///< \return remaining memory 104 | 105 | inline void shiftStart (int32 amount) {return shiftAt (0, amount);} ///< moves all memory by given amount, grows the Buffer if necessary 106 | void shiftAt (uint32 position, int32 amount); ///< moves memory starting at the given position 107 | void move (int32 amount, uint8 initVal = 0); ///< shifts memory at start without growing the buffer, so data is lost and initialized with init val 108 | 109 | bool copy (uint32 from, uint32 to, uint32 bytes); ///< copies a number of bytes from one position to another, the size may be adapted 110 | uint32 get (void* b, uint32 size); ///< copy to buffer from fillSize, and shift fillSize 111 | 112 | void setDelta (uint32 d) {delta = d;} ///< define the block size by which the Buffer grows, see \ref grow() 113 | 114 | bool put (uint8); ///< append value at end, grows Buffer if necessary 115 | bool put (char16 c); ///< append value at end, grows Buffer if necessary 116 | bool put (char c); ///< append value at end, grows Buffer if necessary 117 | bool put (const void* , uint32 size); ///< append bytes from a given buffer, grows Buffer if necessary 118 | bool put (void* , uint32 size); ///< append bytes from a given buffer, grows Buffer if necessary 119 | bool put (uint8* , uint32 size); ///< append bytes from a given buffer, grows Buffer if necessary 120 | bool put (char8* , uint32 size); ///< append bytes from a given buffer, grows Buffer if necessary 121 | bool put (const uint8* , uint32 size); ///< append bytes from a given buffer, grows Buffer if necessary 122 | bool put (const char8* , uint32 size); ///< append bytes from a given buffer, grows Buffer if necessary 123 | bool put (const String&); ///< append String at end, grows Buffer if necessary 124 | 125 | void set (uint8 value); ///< fills complete Buffer with given value 126 | 127 | // strings ---------------- 128 | bool appendString (const tchar* s); 129 | bool appendString (tchar* s); 130 | bool appendString (tchar c) { return put (c); } 131 | 132 | bool appendString8 (const char8* s); 133 | bool appendString16 (const char16* s); 134 | 135 | bool appendString8 (char8* s) { return appendString8 ((const char8*)s); } 136 | bool appendString8 (unsigned char* s) { return appendString8 ((const char8*)s); } 137 | bool appendString8 (const unsigned char* s) { return appendString8 ((const char8*)s); } 138 | 139 | bool appendString8 (char8 c) { return put ((uint8)c); } 140 | bool appendString8 (unsigned char c) { return put (c); } 141 | bool appendString16 (char16 c) { return put (c); } 142 | bool appendString16 (char16* s) { return appendString16 ((const char16*)s); } 143 | 144 | bool prependString (const tchar* s); 145 | bool prependString (tchar* s); 146 | bool prependString (tchar c); 147 | 148 | bool prependString8 (const char8* s); 149 | bool prependString16 (const char16* s); 150 | 151 | bool prependString8 (char8 c); 152 | bool prependString8 (unsigned char c) { return prependString8 ((char8)c); } 153 | bool prependString8 (char8* s) { return prependString8 ((const char8*)s); } 154 | bool prependString8 (unsigned char* s) { return prependString8((const char8*)s); } 155 | bool prependString8 (const unsigned char* s) { return prependString8 ((const char8*)s); } 156 | bool prependString16 (char16 c); 157 | bool prependString16 (char16* s) { return prependString16 ((const char16*)s); } 158 | 159 | bool operator+= (const char* s) { return appendString8 (s); } 160 | bool operator+= (char c) { return appendString8 (c); } 161 | bool operator+= (const char16* s) { return appendString16 (s); } 162 | bool operator+= (char16 c) { return appendString16 (c); } 163 | 164 | bool operator= (const char* s) { flush (); return appendString8 (s); } 165 | bool operator= (const char16* s) { flush (); return appendString16 (s); } 166 | bool operator= (char8 c) { flush (); return appendString8 (c); } 167 | bool operator= (char16 c) { flush (); return appendString16 (c); } 168 | 169 | void endString () {put (tchar (0));} 170 | void endString8 () {put (char8 (0));} 171 | void endString16 () {put (char16 (0));} 172 | 173 | bool makeHexString (String& result); 174 | bool fromHexString (const char8* string); 175 | 176 | // conversion 177 | operator void* () const { return (void*)buffer; } ///< conversion 178 | inline tchar* str () const {return (tchar*)buffer;} ///< conversion 179 | inline char8* str8 () const {return (char8*)buffer;} ///< conversion 180 | inline char16* str16 () const {return (char16*)buffer;} ///< conversion 181 | inline int8* int8Ptr () const {return (int8*)buffer;} ///< conversion 182 | inline uint8* uint8Ptr () const {return (uint8*)buffer; } ///< conversion 183 | inline int16* int16Ptr () const {return (int16*)buffer; } ///< conversion 184 | inline uint16* uint16Ptr () const {return (uint16*)buffer; } ///< conversion 185 | inline int32* int32Ptr () const {return (int32*)buffer; } ///< conversion 186 | inline uint32* uint32Ptr () const {return (uint32*)buffer; } ///< conversion 187 | inline float* floatPtr () const {return (float*)buffer; } ///< conversion 188 | inline double* doublePtr () const {return (double*)buffer; } ///< conversion 189 | inline char16* wcharPtr () const {return (char16*)buffer;} ///< conversion 190 | 191 | int8* operator + (uint32 i); ///< \return the internal Buffer's address plus the given offset i, zero if offset is out of range 192 | 193 | int32 operator ! () { return buffer == nullptr; } 194 | 195 | enum swapSize 196 | { 197 | kSwap16 = 2, 198 | kSwap32 = 4, 199 | kSwap64 = 8 200 | }; 201 | bool swap (int16 swapSize); ///< swap all bytes of this Buffer by the given swapSize 202 | static bool swap (void* buffer, uint32 bufferSize, int16 swapSize); ///< utility, swap given number of bytes in given buffer by the given swapSize 203 | 204 | void take (Buffer& from); ///< takes another Buffer's memory, frees the current Buffer's memory 205 | int8* pass (); ///< pass the current Buffer's memory 206 | 207 | /** Converts a Buffer's content to UTF-16 from a given multi-byte code page, Buffer must contain char8 of given encoding. 208 | \param[in] sourceCodePage : the actual code page of the Buffer's content 209 | \return true, if the conversion was successful, else false 210 | */ 211 | virtual bool toWideString (int32 sourceCodePage); // Buffer contains char8 of given encoding -> utf16 212 | 213 | /** Converts a Buffer's content from UTF-16 to a given multi-byte code page, Buffer must contain UTF-16 encoded characters. 214 | \param[in] destCodePage : the desired code page to convert the Buffer's content to 215 | \return true, if the conversion was successful, else false 216 | */ 217 | virtual bool toMultibyteString (int32 destCodePage); // Buffer contains utf16 -> char8 of given encoding 218 | 219 | //------------------------------------------------------------------------ 220 | protected: 221 | static const uint32 defaultDelta = 0x1000; // 0x1000 222 | 223 | int8* buffer; 224 | uint32 memSize; 225 | uint32 fillSize; 226 | uint32 delta; 227 | }; 228 | 229 | inline bool Buffer::put (void* p, uint32 count) { return put ((const void*)p , count ); } 230 | inline bool Buffer::put (uint8 * p, uint32 count) { return put ((const void*)p , count ); } 231 | inline bool Buffer::put (char8* p, uint32 count) { return put ((const void*)p , count ); } 232 | inline bool Buffer::put (const uint8* p, uint32 count) { return put ((const void*)p , count ); } 233 | inline bool Buffer::put (const char8* p, uint32 count) { return put ((const void*)p , count ); } 234 | 235 | //------------------------------------------------------------------------ 236 | inline bool Buffer::appendString (const tchar* s) 237 | { 238 | #ifdef UNICODE 239 | return appendString16 (s); 240 | #else 241 | return appendString8 (s); 242 | #endif 243 | } 244 | 245 | //------------------------------------------------------------------------ 246 | inline bool Buffer::appendString (tchar* s) 247 | { 248 | #ifdef UNICODE 249 | return appendString16 (s); 250 | #else 251 | return appendString8 (s); 252 | #endif 253 | } 254 | 255 | //------------------------------------------------------------------------ 256 | inline bool Buffer::prependString (const tchar* s) 257 | { 258 | #ifdef UNICODE 259 | return prependString16 (s); 260 | #else 261 | return prependString8 (s); 262 | #endif 263 | } 264 | 265 | //------------------------------------------------------------------------ 266 | inline bool Buffer::prependString (tchar* s) 267 | { 268 | #ifdef UNICODE 269 | return prependString16 (s); 270 | #else 271 | return prependString8 (s); 272 | #endif 273 | } 274 | 275 | //------------------------------------------------------------------------ 276 | inline bool Buffer::prependString (tchar c) 277 | { 278 | #ifdef UNICODE 279 | return prependString16 (c); 280 | #else 281 | return prependString8 (c); 282 | #endif 283 | } 284 | 285 | //------------------------------------------------------------------------ 286 | } // namespace Steinberg 287 | -------------------------------------------------------------------------------- /source/fcommandline.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Project : SDK Base 3 | // Version : 1.0 4 | // 5 | // Category : Helpers 6 | // Filename : base/source/fcommandline.h 7 | // Created by : Steinberg, 2007 8 | // Description : Very simple command-line parser. @see Steinberg::CommandLine 9 | // 10 | //----------------------------------------------------------------------------- 11 | // This file is part of a Steinberg SDK. It is subject to the license terms 12 | // in the LICENSE file found in the top-level directory of this distribution 13 | // and at www.steinberg.net/sdklicenses. 14 | // No part of the SDK, including this file, may be copied, modified, propagated, 15 | // or distributed except according to the terms contained in the LICENSE file. 16 | //----------------------------------------------------------------------------- 17 | 18 | #pragma once 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | namespace Steinberg { 29 | //------------------------------------------------------------------------ 30 | /** Very simple command-line parser. 31 | 32 | Parses the command-line into a CommandLine::VariablesMap.\n 33 | The command-line parser uses CommandLine::Descriptions to define the available options. 34 | 35 | @b Example: 36 | \code 37 | #include "base/source/fcommandline.h" 38 | #include 39 | 40 | int main (int argc, char* argv[]) 41 | { 42 | using namespace std; 43 | 44 | CommandLine::Descriptions desc; 45 | CommandLine::VariablesMap valueMap; 46 | 47 | desc.addOptions ("myTool") 48 | ("help", "produce help message") 49 | ("opt1", string(), "option 1") 50 | ("opt2", string(), "option 2") 51 | ; 52 | CommandLine::parse (argc, argv, desc, valueMap); 53 | 54 | if (valueMap.hasError () || valueMap.count ("help")) 55 | { 56 | cout << desc << "\n"; 57 | return 1; 58 | } 59 | if (valueMap.count ("opt1")) 60 | { 61 | cout << "Value of option 1 " << valueMap["opt1"] << "\n"; 62 | } 63 | if (valueMap.count ("opt2")) 64 | { 65 | cout << "Value of option 2 " << valueMap["opt2"] << "\n"; 66 | } 67 | return 0; 68 | } 69 | \endcode 70 | @note 71 | This is a "header only" implementation.\n 72 | If you need the declarations in more than one cpp file, you have to define 73 | @c SMTG_NO_IMPLEMENTATION in all but one file. 74 | 75 | */ 76 | //------------------------------------------------------------------------ 77 | namespace CommandLine { 78 | 79 | //------------------------------------------------------------------------ 80 | /** Command-line parsing result. 81 | This is the result of the parser.\n 82 | - Use hasError() to check for errors.\n 83 | - To test if a option was specified on the command-line use: count()\n 84 | - To retrieve the value of an options, use operator [](const VariablesMapContainer::key_type 85 | k)\n 86 | */ 87 | //------------------------------------------------------------------------ 88 | class VariablesMap 89 | { 90 | bool mParaError; 91 | using VariablesMapContainer = std::map; 92 | VariablesMapContainer mVariablesMapContainer; 93 | 94 | public: 95 | VariablesMap () : mParaError (false) {} ///< Constructor. Creates a empty VariablesMap. 96 | bool hasError () const { return mParaError; } ///< Returns @c true when an error has occurred. 97 | void setError () { mParaError = true; } ///< Sets the error state to @c true. 98 | 99 | /** Retrieve the value of option @c k.*/ 100 | std::string& operator[] (const VariablesMapContainer::key_type k); 101 | 102 | /** Retrieve the value of option @c k. */ 103 | const std::string& operator[] (const VariablesMapContainer::key_type k) const; 104 | 105 | /** Returns @c != @c 0 if command-line contains option @c k. */ 106 | VariablesMapContainer::size_type count (const VariablesMapContainer::key_type k) const; 107 | }; 108 | 109 | //! type of the list of elements on the command line that are not handled by options parsing 110 | using FilesVector = std::vector; 111 | 112 | //------------------------------------------------------------------------ 113 | /** The description of one single command-line option. 114 | Normally you rarely use a Description directly.\n 115 | In most cases you will use the Descriptions::addOptions (const std::string&) method to create 116 | and add descriptions. 117 | */ 118 | //------------------------------------------------------------------------ 119 | class Description : public std::string 120 | { 121 | public: 122 | /** Construct a Description */ 123 | Description (const std::string& name, const std::string& help, 124 | const std::string& valueType); 125 | std::string mHelp; ///< The help string for this option. 126 | std::string mType; ///< The type of this option (kBool, kString). 127 | static const std::string kBool; 128 | static const std::string kString; 129 | }; 130 | //------------------------------------------------------------------------ 131 | /** List of command-line option descriptions. 132 | 133 | Use addOptions (const std::string&) to add Descriptions. 134 | */ 135 | //------------------------------------------------------------------------ 136 | class Descriptions 137 | { 138 | using DescriptionsList = std::deque; 139 | DescriptionsList mDescriptions; 140 | std::string mCaption; 141 | 142 | public: 143 | /** Sets the command-line tool caption and starts adding Descriptions. */ 144 | Descriptions& addOptions (const std::string& caption = "", 145 | std::initializer_list&& options = {}); 146 | 147 | /** Parse the command-line. */ 148 | bool parse (int ac, char* av[], VariablesMap& result, FilesVector* files = nullptr) const; 149 | 150 | /** Print a brief description for the command-line tool into the stream @c os. */ 151 | void print (std::ostream& os) const; 152 | 153 | /** Add a new switch. Only */ 154 | Descriptions& operator () (const std::string& name, const std::string& help); 155 | 156 | /** Add a new option of type @c inType. Currently only std::string is supported. */ 157 | template 158 | Descriptions& operator () (const std::string& name, const Type& inType, std::string help); 159 | }; 160 | 161 | //------------------------------------------------------------------------ 162 | // If you need the declarations in more than one cpp file you have to define 163 | // SMTG_NO_IMPLEMENTATION in all but one file. 164 | //------------------------------------------------------------------------ 165 | #ifndef SMTG_NO_IMPLEMENTATION 166 | 167 | //------------------------------------------------------------------------ 168 | /*! If command-line contains option @c k more than once, only the last value will survive. */ 169 | //------------------------------------------------------------------------ 170 | std::string& VariablesMap::operator[] (const VariablesMapContainer::key_type k) 171 | { 172 | return mVariablesMapContainer[k]; 173 | } 174 | 175 | //------------------------------------------------------------------------ 176 | /*! If command-line contains option @c k more than once, only the last value will survive. */ 177 | //------------------------------------------------------------------------ 178 | const std::string& VariablesMap::operator[] (const VariablesMapContainer::key_type k) const 179 | { 180 | return (*const_cast (this))[k]; 181 | } 182 | 183 | //------------------------------------------------------------------------ 184 | VariablesMap::VariablesMapContainer::size_type VariablesMap::count ( 185 | const VariablesMapContainer::key_type k) const 186 | { 187 | return mVariablesMapContainer.count (k); 188 | } 189 | 190 | //------------------------------------------------------------------------ 191 | /** Add a new option with a string as parameter. */ 192 | //------------------------------------------------------------------------ 193 | template <> 194 | Descriptions& Descriptions::operator () (const std::string& name, const std::string& inType, 195 | std::string help) 196 | { 197 | mDescriptions.emplace_back (name, help, inType); 198 | return *this; 199 | } 200 | 201 | /** Parse the command - line. */ 202 | bool parse (int ac, char* av[], const Descriptions& desc, VariablesMap& result, 203 | FilesVector* files = nullptr); 204 | 205 | /** Make Descriptions stream able. */ 206 | std::ostream& operator<< (std::ostream& os, const Descriptions& desc); 207 | 208 | const std::string Description::kBool = "bool"; 209 | const std::string Description::kString = "string"; 210 | 211 | //------------------------------------------------------------------------ 212 | /*! In most cases you will use the Descriptions::addOptions (const std::string&) method to create 213 | and add descriptions. 214 | 215 | @param[in] name of the option. 216 | @param[in] help a help description for this option. 217 | @param[out] valueType Description::kBool or Description::kString. 218 | */ 219 | Description::Description (const std::string& name, const std::string& help, 220 | const std::string& valueType) 221 | : std::string (name), mHelp (help), mType (valueType) 222 | { 223 | } 224 | 225 | //------------------------------------------------------------------------ 226 | /*! Returning a reference to *this, enables chaining of calls to operator()(const std::string&, 227 | const std::string&). 228 | 229 | @param[in] name of the added option. 230 | @param[in] help a help description for this option. 231 | @return a reference to *this. 232 | */ 233 | Descriptions& Descriptions::operator () (const std::string& name, const std::string& help) 234 | { 235 | mDescriptions.emplace_back (name, help, Description::kBool); 236 | return *this; 237 | } 238 | 239 | //------------------------------------------------------------------------ 240 | /*! Usage example: 241 | @code 242 | CommandLine::Descriptions desc; 243 | desc.addOptions ("myTool") // Set caption to "myTool" 244 | ("help", "produce help message") // add switch -help 245 | ("opt1", string(), "option 1") // add string option -opt1 246 | ("opt2", string(), "option 2") // add string option -opt2 247 | ; 248 | @endcode 249 | @note 250 | The operator() is used for every additional option. 251 | 252 | Or with initializer list : 253 | @code 254 | CommandLine::Descriptions desc; 255 | desc.addOptions ("myTool", // Set caption to "myTool" 256 | {{"help", "produce help message", Description::kBool}, // add switch -help 257 | {"opt1", "option 1", Description::kString}, // add string option -opt1 258 | {"opt2", "option 2", Description::kString}} // add string option -opt2 259 | ); 260 | @endcode 261 | @param[in] caption the caption of the command-line tool. 262 | @param[in] options initializer list with options 263 | @return a reverense to *this. 264 | */ 265 | Descriptions& Descriptions::addOptions (const std::string& caption, 266 | std::initializer_list&& options) 267 | { 268 | mCaption = caption; 269 | std::move (options.begin (), options.end (), std::back_inserter (mDescriptions)); 270 | return *this; 271 | } 272 | 273 | //------------------------------------------------------------------------ 274 | /*! @param[in] ac count of command-line parameters 275 | @param[in] av command-line as array of strings 276 | @param[out] result the parsing result 277 | @param[out] files optional list of elements on the command line that are not handled by options 278 | parsing 279 | */ 280 | bool Descriptions::parse (int ac, char* av[], VariablesMap& result, FilesVector* files) const 281 | { 282 | using namespace std; 283 | 284 | for (int i = 1; i < ac; i++) 285 | { 286 | string current = av[i]; 287 | if (current[0] == '-') 288 | { 289 | int pos = current[1] == '-' ? 2 : 1; 290 | current = current.substr (pos, string::npos); 291 | 292 | DescriptionsList::const_iterator found = 293 | find (mDescriptions.begin (), mDescriptions.end (), current); 294 | if (found != mDescriptions.end ()) 295 | { 296 | result[*found] = "true"; 297 | if (found->mType != Description::kBool) 298 | { 299 | if (((i + 1) < ac) && *av[i + 1] != '-') 300 | { 301 | result[*found] = av[++i]; 302 | } 303 | else 304 | { 305 | result[*found] = "error!"; 306 | result.setError (); 307 | return false; 308 | } 309 | } 310 | } 311 | else 312 | { 313 | result.setError (); 314 | return false; 315 | } 316 | } 317 | else if (files) 318 | files->push_back (av[i]); 319 | } 320 | return true; 321 | } 322 | 323 | //------------------------------------------------------------------------ 324 | /*! The description includes the help strings for all options. */ 325 | //------------------------------------------------------------------------ 326 | void Descriptions::print (std::ostream& os) const 327 | { 328 | if (!mCaption.empty ()) 329 | os << mCaption << ":\n"; 330 | 331 | size_t maxLength = 0u; 332 | std::for_each (mDescriptions.begin (), mDescriptions.end (), 333 | [&] (const Description& d) { maxLength = std::max (maxLength, d.size ()); }); 334 | 335 | for (const Description& opt : mDescriptions) 336 | { 337 | os << "-" << opt; 338 | for (auto s = opt.size (); s < maxLength; ++s) 339 | os << " "; 340 | os << " | " << opt.mHelp << "\n"; 341 | } 342 | } 343 | 344 | //------------------------------------------------------------------------ 345 | std::ostream& operator<< (std::ostream& os, const Descriptions& desc) 346 | { 347 | desc.print (os); 348 | return os; 349 | } 350 | 351 | //------------------------------------------------------------------------ 352 | /*! @param[in] ac count of command-line parameters 353 | @param[in] av command-line as array of strings 354 | @param[in] desc Descriptions including all allowed options 355 | @param[out] result the parsing result 356 | @param[out] files optional list of elements on the command line that are not handled by options 357 | parsing 358 | */ 359 | bool parse (int ac, char* av[], const Descriptions& desc, VariablesMap& result, FilesVector* files) 360 | { 361 | return desc.parse (ac, av, result, files); 362 | } 363 | #endif 364 | 365 | //------------------------------------------------------------------------ 366 | } // namespace CommandLine 367 | } // namespace Steinberg 368 | -------------------------------------------------------------------------------- /source/fbuffer.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Project : SDK Base 3 | // Version : 1.0 4 | // 5 | // Category : Helpers 6 | // Filename : base/source/fbuffer.cpp 7 | // Created by : Steinberg, 2008 8 | // Description : 9 | // 10 | //----------------------------------------------------------------------------- 11 | // This file is part of a Steinberg SDK. It is subject to the license terms 12 | // in the LICENSE file found in the top-level directory of this distribution 13 | // and at www.steinberg.net/sdklicenses. 14 | // No part of the SDK, including this file, may be copied, modified, propagated, 15 | // or distributed except according to the terms contained in the LICENSE file. 16 | //----------------------------------------------------------------------------- 17 | 18 | #include "base/source/fbuffer.h" 19 | #include "base/source/fstring.h" 20 | #include 21 | 22 | namespace Steinberg { 23 | 24 | //------------------------------------------------------------------------------------- 25 | Buffer::Buffer () 26 | : buffer (nullptr) 27 | , memSize (0) 28 | , fillSize (0) 29 | , delta (defaultDelta) 30 | {} 31 | 32 | //------------------------------------------------------------------------------------- 33 | Buffer::Buffer (uint32 s, uint8 initVal) 34 | : buffer (nullptr) 35 | , memSize (s) 36 | , fillSize (0) 37 | , delta (defaultDelta) 38 | { 39 | if (memSize == 0) 40 | return; 41 | buffer = (int8*)::malloc (memSize); 42 | if (buffer) 43 | memset (buffer, initVal, memSize); 44 | else 45 | memSize = 0; 46 | } 47 | 48 | //------------------------------------------------------------------------------------- 49 | Buffer::Buffer (uint32 s) 50 | : buffer (nullptr) 51 | , memSize (s) 52 | , fillSize (0) 53 | , delta (defaultDelta) 54 | { 55 | if (memSize == 0) 56 | return; 57 | buffer = (int8*)::malloc (memSize); 58 | if (!buffer) 59 | memSize = 0; 60 | } 61 | 62 | //------------------------------------------------------------------------------------- 63 | Buffer::Buffer (const void* b , uint32 s) 64 | : buffer (nullptr) 65 | , memSize (s) 66 | , fillSize (s) 67 | , delta (defaultDelta) 68 | { 69 | if (memSize == 0) 70 | return; 71 | buffer = (int8*)::malloc (memSize); 72 | if (buffer) 73 | memcpy (buffer, b, memSize); 74 | else 75 | { 76 | memSize = 0; 77 | fillSize = 0; 78 | } 79 | } 80 | 81 | //------------------------------------------------------------------------------------- 82 | Buffer::Buffer (const Buffer& bufferR) 83 | : buffer (nullptr) 84 | , memSize (bufferR.memSize) 85 | , fillSize (bufferR.fillSize) 86 | , delta (bufferR.delta) 87 | { 88 | if (memSize == 0) 89 | return; 90 | 91 | buffer = (int8*)::malloc (memSize); 92 | if (buffer) 93 | memcpy (buffer, bufferR.buffer, memSize); 94 | else 95 | memSize = 0; 96 | } 97 | 98 | //------------------------------------------------------------------------------------- 99 | Buffer::~Buffer () 100 | { 101 | if (buffer) 102 | ::free (buffer); 103 | buffer = nullptr; 104 | } 105 | 106 | //------------------------------------------------------------------------------------- 107 | void Buffer::operator = (const Buffer& b2) 108 | { 109 | if (&b2 != this) 110 | { 111 | setSize (b2.memSize); 112 | if (b2.memSize > 0 && buffer) 113 | memcpy (buffer, b2.buffer, b2.memSize); 114 | fillSize = b2.fillSize; 115 | delta = b2.delta; 116 | } 117 | } 118 | 119 | //------------------------------------------------------------------------------------- 120 | bool Buffer::operator == (const Buffer& b2)const 121 | { 122 | if (&b2 == this) 123 | return true; 124 | if (b2.getSize () != getSize ()) 125 | return false; 126 | return memcmp (this->int8Ptr (), b2.int8Ptr (), getSize ()) == 0 ? true : false; 127 | } 128 | 129 | //------------------------------------------------------------------------------------- 130 | uint32 Buffer::get (void* b, uint32 size) 131 | { 132 | uint32 maxGet = memSize - fillSize; 133 | if (size > maxGet) 134 | size = maxGet; 135 | if (size > 0) 136 | memcpy (b, buffer + fillSize, size); 137 | fillSize += size; 138 | return size; 139 | } 140 | 141 | //------------------------------------------------------------------------------------- 142 | bool Buffer::put (char16 c) 143 | { 144 | return put ((const void*)&c, sizeof (c)); 145 | } 146 | 147 | //------------------------------------------------------------------------------------- 148 | bool Buffer::put (uint8 byte) 149 | { 150 | if (grow (fillSize + 1) == false) 151 | return false; 152 | 153 | buffer [fillSize++] = byte; 154 | return true; 155 | } 156 | 157 | //------------------------------------------------------------------------------------- 158 | bool Buffer::put (char c) 159 | { 160 | if (grow (fillSize + 1) == false) 161 | return false; 162 | 163 | buffer [fillSize++] = c; 164 | return true; 165 | } 166 | 167 | //------------------------------------------------------------------------------------- 168 | bool Buffer::put (const void* toPut, uint32 s) 169 | { 170 | if (!toPut) 171 | return false; 172 | 173 | if (grow (fillSize + s) == false) 174 | return false; 175 | 176 | memcpy (buffer + fillSize, toPut, s); 177 | fillSize += s; 178 | return true; 179 | } 180 | 181 | //------------------------------------------------------------------------------------- 182 | bool Buffer::put (const String& str) 183 | { 184 | return put ((const void*)str.text () , (str.length () + 1) * sizeof (tchar)); 185 | } 186 | 187 | //------------------------------------------------------------------------------------- 188 | bool Buffer::appendString8 (const char8* s) 189 | { 190 | if (!s) 191 | return false; 192 | 193 | uint32 len = (uint32) strlen (s); 194 | return put (s, len); 195 | } 196 | 197 | //------------------------------------------------------------------------------------- 198 | bool Buffer::appendString16 (const char16* s) 199 | { 200 | if (!s) 201 | return false; 202 | ConstString str (s); 203 | uint32 len = (uint32) str.length () * sizeof (char16); 204 | return put (s, len); 205 | } 206 | 207 | //------------------------------------------------------------------------------------- 208 | bool Buffer::prependString8 (const char8* s) 209 | { 210 | if (!s) 211 | return false; 212 | 213 | uint32 len = (uint32) strlen (s); 214 | if (len > 0) 215 | { 216 | shiftStart (len); 217 | memcpy (buffer, s, len); 218 | return true; 219 | } 220 | return false; 221 | } 222 | 223 | //------------------------------------------------------------------------------------- 224 | bool Buffer::prependString16 (const char16* s) 225 | { 226 | if (!s) 227 | return false; 228 | 229 | ConstString str (s); 230 | uint32 len = (uint32) str.length () * sizeof (char16); 231 | 232 | if (len > 0) 233 | { 234 | shiftStart (len); 235 | memcpy (buffer, s, len); 236 | return true; 237 | } 238 | return false; 239 | } 240 | 241 | //------------------------------------------------------------------------------------- 242 | bool Buffer::prependString8 (char8 c) 243 | { 244 | shiftStart (sizeof (char)); 245 | char* b = (char*)buffer; 246 | b [0] = c; 247 | return true; 248 | } 249 | 250 | //------------------------------------------------------------------------------------- 251 | bool Buffer::prependString16 (char16 c) 252 | { 253 | shiftStart (sizeof (char16)); 254 | char16* b = (char16*)buffer; 255 | b [0] = c; 256 | return true; 257 | } 258 | 259 | //------------------------------------------------------------------------------------- 260 | bool Buffer::copy (uint32 from, uint32 to, uint32 bytes) 261 | { 262 | if (from + bytes > memSize || bytes == 0) 263 | return false; 264 | 265 | if (to + bytes > memSize) 266 | setSize (to + bytes); 267 | 268 | if (from + bytes > to && from < to) 269 | { // overlap 270 | Buffer tmp (buffer + from, bytes); 271 | memcpy (buffer + to, tmp, bytes); 272 | } 273 | else 274 | memcpy (buffer + to, buffer + from, bytes); 275 | return true; 276 | } 277 | 278 | //------------------------------------------------------------------------------------- 279 | bool Buffer::makeHexString (String& result) 280 | { 281 | unsigned char* data = uint8Ptr (); 282 | uint32 bytes = getSize (); 283 | 284 | if (data == nullptr || bytes == 0) 285 | return false; 286 | 287 | char8* stringBuffer = NEWSTR8 ((bytes * 2) + 1); 288 | if (!stringBuffer) 289 | return false; 290 | 291 | int32 count = 0; 292 | while (bytes > 0) 293 | { 294 | unsigned char t1 = ((*data) >> 4) & 0x0F; 295 | unsigned char t2 = (*data) & 0x0F; 296 | if (t1 < 10) 297 | t1 += '0'; 298 | else 299 | t1 = t1 - 10 + 'A'; 300 | if (t2 < 10) 301 | t2 += '0'; 302 | else 303 | t2 = t2 - 10 + 'A'; 304 | 305 | stringBuffer [count++] = t1; 306 | stringBuffer [count++] = t2; 307 | data++; 308 | bytes--; 309 | } 310 | stringBuffer [count] = 0; 311 | 312 | result.take ((void*)stringBuffer, false); 313 | return true; 314 | } 315 | 316 | //------------------------------------------------------------------------------------- 317 | bool Buffer::fromHexString (const char8* string) 318 | { 319 | flush (); 320 | if (string == nullptr) 321 | return false; 322 | 323 | int32 len = strlen8 (string); 324 | if (len == 0 || ((len & 1) == 1)/*odd number*/ ) 325 | return false; 326 | 327 | setSize (len / 2); 328 | unsigned char* data = uint8Ptr (); 329 | 330 | bool upper = true; 331 | int32 count = 0; 332 | while (count < len) 333 | { 334 | char c = string [count]; 335 | 336 | unsigned char d = 0; 337 | if (c >= '0' && c <= '9') d += c - '0'; 338 | else if (c >= 'A' && c <= 'F') d += c - 'A' + 10; 339 | else if (c >= 'a' && c <= 'f') d += c - 'a' + 10; 340 | else return false; // no hex string 341 | 342 | if (upper) 343 | data [count >> 1] = static_cast (d << 4); 344 | else 345 | data [count >> 1] += d; 346 | 347 | upper = !upper; 348 | count++; 349 | } 350 | setFillSize (len / 2); 351 | return true; 352 | } 353 | 354 | //------------------------------------------------------------------------ 355 | void Buffer::set (uint8 value) 356 | { 357 | if (buffer) 358 | memset (buffer, value, memSize); 359 | } 360 | 361 | //------------------------------------------------------------------------------------- 362 | bool Buffer::setFillSize (uint32 c) 363 | { 364 | if (c <= memSize) 365 | { 366 | fillSize = c; 367 | return true; 368 | } 369 | return false; 370 | } 371 | 372 | //------------------------------------------------------------------------------------- 373 | bool Buffer::truncateToFillSize () 374 | { 375 | if (fillSize < memSize) 376 | setSize (fillSize); 377 | 378 | return true; 379 | } 380 | 381 | //------------------------------------------------------------------------------------- 382 | bool Buffer::grow (uint32 newSize) 383 | { 384 | if (newSize > memSize) 385 | { 386 | if (delta == 0) 387 | delta = defaultDelta; 388 | uint32 s = ((newSize + delta - 1) / delta) * delta; 389 | return setSize (s); 390 | } 391 | return true; 392 | } 393 | 394 | //------------------------------------------------------------------------ 395 | void Buffer::shiftAt (uint32 position, int32 amount) 396 | { 397 | if (amount > 0) 398 | { 399 | if (grow (fillSize + amount)) 400 | { 401 | if (position < fillSize) 402 | memmove (buffer + amount + position, buffer + position, fillSize - position); 403 | 404 | fillSize += amount; 405 | } 406 | } 407 | else if (amount < 0 && fillSize > 0) 408 | { 409 | uint32 toRemove = -amount; 410 | 411 | if (toRemove < fillSize) 412 | { 413 | if (position < fillSize) 414 | memmove (buffer + position, buffer + toRemove + position, fillSize - position - toRemove); 415 | fillSize -= toRemove; 416 | } 417 | } 418 | } 419 | 420 | //------------------------------------------------------------------------------------- 421 | void Buffer::move (int32 amount, uint8 initVal) 422 | { 423 | if (memSize == 0) 424 | return; 425 | 426 | if (amount > 0) 427 | { 428 | if ((uint32)amount < memSize) 429 | { 430 | memmove (buffer + amount, buffer, memSize - amount); 431 | memset (buffer, initVal, amount); 432 | } 433 | else 434 | memset (buffer, initVal, memSize); 435 | } 436 | else 437 | { 438 | uint32 toRemove = -amount; 439 | if (toRemove < memSize) 440 | { 441 | memmove (buffer, buffer + toRemove, memSize - toRemove); 442 | memset (buffer + memSize - toRemove, initVal, toRemove); 443 | } 444 | else 445 | memset (buffer, initVal, memSize); 446 | } 447 | } 448 | 449 | //------------------------------------------------------------------------------------- 450 | bool Buffer::setSize (uint32 newSize) 451 | { 452 | if (memSize != newSize) 453 | { 454 | if (buffer) 455 | { 456 | if (newSize > 0) 457 | { 458 | int8* newBuffer = (int8*) ::realloc (buffer, newSize); 459 | if (newBuffer == nullptr) 460 | { 461 | newBuffer = (int8*)::malloc (newSize); 462 | if (newBuffer) 463 | { 464 | uint32 tmp = newSize; 465 | if (tmp > memSize) 466 | tmp = memSize; 467 | memcpy (newBuffer, buffer, tmp); 468 | ::free (buffer); 469 | buffer = newBuffer; 470 | } 471 | else 472 | { 473 | ::free (buffer); 474 | buffer = nullptr; 475 | } 476 | } 477 | else 478 | buffer = newBuffer; 479 | } 480 | else 481 | { 482 | ::free (buffer); 483 | buffer = nullptr; 484 | } 485 | } 486 | else 487 | buffer = (int8*)::malloc (newSize); 488 | 489 | if (newSize > 0 && !buffer) 490 | memSize = 0; 491 | else 492 | memSize = newSize; 493 | if (fillSize > memSize) 494 | fillSize = memSize; 495 | } 496 | 497 | return (newSize > 0) == (buffer != nullptr); 498 | } 499 | 500 | //------------------------------------------------------------------------------------- 501 | void Buffer::fillup (uint8 value) 502 | { 503 | if (getFree () > 0) 504 | memset (buffer + fillSize, value, getFree ()); 505 | } 506 | 507 | //------------------------------------------------------------------------------------- 508 | int8* Buffer::operator + (uint32 i) 509 | { 510 | if (i < memSize) 511 | return buffer + i; 512 | 513 | static int8 eof; 514 | eof = 0; 515 | return &eof; 516 | } 517 | 518 | //------------------------------------------------------------------------------------- 519 | bool Buffer::swap (int16 swapSize) 520 | { 521 | return swap (buffer, memSize, swapSize); 522 | } 523 | 524 | //------------------------------------------------------------------------------------- 525 | bool Buffer::swap (void* buffer, uint32 bufferSize, int16 swapSize) 526 | { 527 | if (swapSize != kSwap16 && swapSize != kSwap32 && swapSize != kSwap64) 528 | return false; 529 | 530 | if (swapSize == kSwap16) 531 | { 532 | for (uint32 count = 0 ; count < bufferSize ; count += 2) 533 | { 534 | SWAP_16 ( * (((int16*)buffer) + count) ); 535 | } 536 | } 537 | else if (swapSize == kSwap32) 538 | { 539 | for (uint32 count = 0 ; count < bufferSize ; count += 4) 540 | { 541 | SWAP_32 ( * (((int32*)buffer) + count) ); 542 | } 543 | } 544 | else if (swapSize == kSwap64) 545 | { 546 | for (uint32 count = 0 ; count < bufferSize ; count += 8) 547 | { 548 | SWAP_64 ( * (((int64*)buffer) + count) ); 549 | } 550 | } 551 | 552 | return true; 553 | } 554 | 555 | //------------------------------------------------------------------------------------- 556 | void Buffer::take (Buffer& from) 557 | { 558 | setSize (0); 559 | memSize = from.memSize; 560 | fillSize = from.fillSize; 561 | buffer = from.buffer; 562 | from.buffer = nullptr; 563 | from.memSize = 0; 564 | from.fillSize = 0; 565 | } 566 | 567 | //------------------------------------------------------------------------------------- 568 | int8* Buffer::pass () 569 | { 570 | int8* res = buffer; 571 | buffer = nullptr; 572 | memSize = 0; 573 | fillSize = 0; 574 | return res; 575 | } 576 | 577 | //------------------------------------------------------------------------------------- 578 | bool Buffer::toWideString (int32 sourceCodePage) 579 | { 580 | if (getFillSize () > 0) 581 | { 582 | if (str8 () [getFillSize () - 1] != 0) // multiByteToWideString only works with 0-terminated strings 583 | endString8 (); 584 | 585 | Buffer dest (getFillSize () * sizeof (char16)); 586 | int32 result = String::multiByteToWideString (dest.str16 (), str8 (), dest.getFree () / sizeof (char16), sourceCodePage); 587 | if (result > 0) 588 | { 589 | dest.setFillSize ((result - 1) * sizeof (char16)); 590 | take (dest); 591 | return true; 592 | } 593 | return false; 594 | } 595 | return true; 596 | } 597 | 598 | //------------------------------------------------------------------------------------- 599 | bool Buffer::toMultibyteString (int32 destCodePage) 600 | { 601 | if (getFillSize () > 0) 602 | { 603 | int32 textLength = getFillSize () / sizeof (char16); // wideStringToMultiByte only works with 0-terminated strings 604 | if (str16 () [textLength - 1] != 0) 605 | endString16 (); 606 | 607 | Buffer dest (getFillSize ()); 608 | int32 result = String::wideStringToMultiByte (dest.str8 (), str16 (), dest.getFree (), destCodePage); 609 | if (result > 0) 610 | { 611 | dest.setFillSize (result - 1); 612 | take (dest); 613 | return true; 614 | } 615 | return false; 616 | } 617 | return true; 618 | } 619 | 620 | //------------------------------------------------------------------------ 621 | } // namespace Steinberg 622 | -------------------------------------------------------------------------------- /source/fstreamer.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Project : SDK Base 3 | // Version : 1.0 4 | // 5 | // Category : Helpers 6 | // Filename : base/source/fstreamer.cpp 7 | // Created by : Steinberg, 15.12.2005 8 | // Description : Extract of typed stream i/o methods from FStream 9 | // 10 | //----------------------------------------------------------------------------- 11 | // This file is part of a Steinberg SDK. It is subject to the license terms 12 | // in the LICENSE file found in the top-level directory of this distribution 13 | // and at www.steinberg.net/sdklicenses. 14 | // No part of the SDK, including this file, may be copied, modified, propagated, 15 | // or distributed except according to the terms contained in the LICENSE file. 16 | //----------------------------------------------------------------------------- 17 | 18 | #include "fstreamer.h" 19 | 20 | #include "base/source/fstring.h" 21 | #include "base/source/fbuffer.h" 22 | #include "pluginterfaces/base/ibstream.h" 23 | 24 | #ifndef UNICODE 25 | #include "pluginterfaces/base/futils.h" 26 | #endif 27 | 28 | namespace Steinberg { 29 | 30 | //------------------------------------------------------------------------ 31 | // IBStreamer 32 | //------------------------------------------------------------------------ 33 | IBStreamer::IBStreamer (IBStream* stream, int16 _byteOrder) 34 | : FStreamer (_byteOrder) 35 | , stream (stream) 36 | {} 37 | 38 | //------------------------------------------------------------------------ 39 | TSize IBStreamer::readRaw (void* buffer, TSize size) 40 | { 41 | int32 numBytesRead = 0; 42 | stream->read (buffer, (int32)size, &numBytesRead); 43 | return numBytesRead; 44 | } 45 | 46 | //------------------------------------------------------------------------ 47 | TSize IBStreamer::writeRaw (const void* buffer, TSize size) 48 | { 49 | int32 numBytesWritten = 0; 50 | stream->write ((void*)buffer, (int32)size, &numBytesWritten); 51 | return numBytesWritten; 52 | } 53 | 54 | //------------------------------------------------------------------------ 55 | int64 IBStreamer::seek (int64 pos, FSeekMode mode) 56 | { 57 | int64 result = -1; 58 | stream->seek (pos, mode, &result); 59 | return result; 60 | } 61 | 62 | //------------------------------------------------------------------------ 63 | int64 IBStreamer::tell () 64 | { 65 | int64 pos = 0; 66 | stream->tell (&pos); 67 | return pos; 68 | } 69 | 70 | //------------------------------------------------------------------------ 71 | // FStreamSizeHolder Implementation 72 | //------------------------------------------------------------------------ 73 | FStreamSizeHolder::FStreamSizeHolder (FStreamer &s) 74 | : stream (s), sizePos (-1) 75 | {} 76 | 77 | //------------------------------------------------------------------------ 78 | void FStreamSizeHolder::beginWrite () 79 | { 80 | sizePos = stream.tell (); 81 | stream.writeInt32 (0L); 82 | } 83 | 84 | //------------------------------------------------------------------------ 85 | int32 FStreamSizeHolder::endWrite () 86 | { 87 | if (sizePos < 0) 88 | return 0; 89 | 90 | int64 currentPos = stream.tell (); 91 | 92 | stream.seek (sizePos, kSeekSet); 93 | int32 size = int32 (currentPos - sizePos - sizeof (int32)); 94 | stream.writeInt32 (size); 95 | 96 | stream.seek (currentPos, kSeekSet); 97 | return size; 98 | } 99 | 100 | //------------------------------------------------------------------------ 101 | int32 FStreamSizeHolder::beginRead () 102 | { 103 | sizePos = stream.tell (); 104 | int32 size = 0; 105 | stream.readInt32 (size); 106 | sizePos += size + sizeof (int32); 107 | return size; 108 | } 109 | 110 | //------------------------------------------------------------------------ 111 | void FStreamSizeHolder::endRead () 112 | { 113 | if (sizePos >= 0) 114 | stream.seek (sizePos, kSeekSet); 115 | } 116 | 117 | //------------------------------------------------------------------------ 118 | // FStreamer 119 | //------------------------------------------------------------------------ 120 | FStreamer::FStreamer (int16 _byteOrder) 121 | : byteOrder (_byteOrder) 122 | {} 123 | 124 | // int8 / char ----------------------------------------------------------- 125 | //------------------------------------------------------------------------ 126 | bool FStreamer::writeChar8 (char8 c) 127 | { 128 | return writeRaw ((void*)&c, sizeof (char8)) == sizeof (char8); 129 | } 130 | 131 | //------------------------------------------------------------------------ 132 | bool FStreamer::readChar8 (char8& c) 133 | { 134 | return readRaw ((void*)&c, sizeof (char8)) == sizeof (char8); 135 | } 136 | 137 | //------------------------------------------------------------------------ 138 | bool FStreamer::writeUChar8 (unsigned char c) 139 | { 140 | return writeRaw ((void*)&c, sizeof (unsigned char)) == sizeof (unsigned char); 141 | } 142 | 143 | //------------------------------------------------------------------------ 144 | bool FStreamer::readUChar8 (unsigned char& c) 145 | { 146 | return readRaw ((void*)&c, sizeof (unsigned char)) == sizeof (unsigned char); 147 | } 148 | 149 | //------------------------------------------------------------------------ 150 | bool FStreamer::writeChar16 (char16 c) 151 | { 152 | if (BYTEORDER != byteOrder) 153 | SWAP_16 (c); 154 | return writeRaw ((void*)&c, sizeof (char16)) == sizeof (char16); 155 | } 156 | 157 | //------------------------------------------------------------------------ 158 | bool FStreamer::readChar16 (char16& c) 159 | { 160 | if (readRaw ((void*)&c, sizeof (char16)) == sizeof (char16)) 161 | { 162 | if (BYTEORDER != byteOrder) 163 | SWAP_16 (c); 164 | return true; 165 | } 166 | c = 0; 167 | return false; 168 | } 169 | 170 | //------------------------------------------------------------------------ 171 | bool FStreamer::writeInt8 (int8 c) 172 | { 173 | return writeRaw ((void*)&c, sizeof (int8)) == sizeof (int8); 174 | } 175 | 176 | //------------------------------------------------------------------------ 177 | bool FStreamer::readInt8 (int8& c) 178 | { 179 | return readRaw ((void*)&c, sizeof (int8)) == sizeof (int8); 180 | } 181 | 182 | //------------------------------------------------------------------------ 183 | bool FStreamer::writeInt8u (uint8 c) 184 | { 185 | return writeRaw ((void*)&c, sizeof (uint8)) == sizeof (uint8); 186 | } 187 | 188 | //------------------------------------------------------------------------ 189 | bool FStreamer::readInt8u (uint8& c) 190 | { 191 | return readRaw ((void*)&c, sizeof (uint8)) == sizeof (uint8); 192 | } 193 | 194 | // int16 ----------------------------------------------------------------- 195 | //------------------------------------------------------------------------ 196 | bool FStreamer::writeInt16 (int16 i) 197 | { 198 | if (BYTEORDER != byteOrder) 199 | SWAP_16 (i); 200 | return writeRaw ((void*)&i, sizeof (int16)) == sizeof (int16); 201 | } 202 | 203 | //------------------------------------------------------------------------ 204 | bool FStreamer::readInt16 (int16& i) 205 | { 206 | if (readRaw ((void*)&i, sizeof (int16)) == sizeof (int16)) 207 | { 208 | if (BYTEORDER != byteOrder) 209 | SWAP_16 (i); 210 | return true; 211 | } 212 | i = 0; 213 | return false; 214 | } 215 | 216 | //------------------------------------------------------------------------ 217 | bool FStreamer::writeInt16Array (const int16* array, int32 count) 218 | { 219 | for (int32 i = 0; i < count; i++) 220 | { 221 | if (!writeInt16 (array[i])) 222 | return false; 223 | } 224 | return true; 225 | } 226 | 227 | //------------------------------------------------------------------------ 228 | bool FStreamer::readInt16Array (int16* array, int32 count) 229 | { 230 | for (int32 i = 0; i < count; i++) 231 | { 232 | if (!readInt16 (array[i])) 233 | return false; 234 | } 235 | return true; 236 | } 237 | 238 | //------------------------------------------------------------------------ 239 | bool FStreamer::writeInt16u (uint16 i) 240 | { 241 | if (BYTEORDER != byteOrder) 242 | SWAP_16 (i); 243 | return writeRaw ((void*)&i, sizeof (uint16)) == sizeof (uint16); 244 | } 245 | 246 | //------------------------------------------------------------------------ 247 | bool FStreamer::readInt16u (uint16& i) 248 | { 249 | if (readRaw ((void*)&i, sizeof (uint16)) == sizeof (uint16)) 250 | { 251 | if (BYTEORDER != byteOrder) 252 | SWAP_16 (i); 253 | return true; 254 | } 255 | i = 0; 256 | return false; 257 | } 258 | 259 | //------------------------------------------------------------------------ 260 | bool FStreamer::writeInt16uArray (const uint16* array, int32 count) 261 | { 262 | for (int32 i = 0; i < count; i++) 263 | { 264 | if (!writeInt16u (array[i])) 265 | return false; 266 | } 267 | return true; 268 | } 269 | 270 | //------------------------------------------------------------------------ 271 | bool FStreamer::readInt16uArray (uint16* array, int32 count) 272 | { 273 | for (int32 i = 0; i < count; i++) 274 | { 275 | if (!readInt16u (array[i])) 276 | return false; 277 | } 278 | return true; 279 | } 280 | 281 | // int32 ----------------------------------------------------------------- 282 | //------------------------------------------------------------------------ 283 | bool FStreamer::writeInt32 (int32 i) 284 | { 285 | if (BYTEORDER != byteOrder) 286 | SWAP_32 (i); 287 | return writeRaw ((void*)&i, sizeof (int32)) == sizeof (int32); 288 | } 289 | 290 | //------------------------------------------------------------------------ 291 | bool FStreamer::readInt32 (int32& i) 292 | { 293 | if (readRaw ((void*)&i, sizeof (int32)) == sizeof (int32)) 294 | { 295 | if (BYTEORDER != byteOrder) 296 | SWAP_32 (i); 297 | return true; 298 | } 299 | i = 0; 300 | return false; 301 | } 302 | 303 | //------------------------------------------------------------------------ 304 | bool FStreamer::writeInt32Array (const int32* array, int32 count) 305 | { 306 | for (int32 i = 0; i < count; i++) 307 | { 308 | if (!writeInt32 (array[i])) 309 | return false; 310 | } 311 | return true; 312 | } 313 | 314 | //------------------------------------------------------------------------ 315 | bool FStreamer::readInt32Array (int32* array, int32 count) 316 | { 317 | for (int32 i = 0; i < count; i++) 318 | { 319 | if (!readInt32 (array[i])) 320 | return false; 321 | } 322 | return true; 323 | } 324 | 325 | //------------------------------------------------------------------------ 326 | bool FStreamer::writeInt32u (uint32 i) 327 | { 328 | if (BYTEORDER != byteOrder) 329 | SWAP_32 (i); 330 | return writeRaw ((void*)&i, sizeof (uint32)) == sizeof (uint32); 331 | } 332 | 333 | //------------------------------------------------------------------------ 334 | bool FStreamer::readInt32u (uint32& i) 335 | { 336 | if (readRaw ((void*)&i, sizeof (uint32)) == sizeof (uint32)) 337 | { 338 | if (BYTEORDER != byteOrder) 339 | SWAP_32 (i); 340 | return true; 341 | } 342 | i = 0; 343 | return false; 344 | } 345 | 346 | //------------------------------------------------------------------------ 347 | bool FStreamer::writeInt32uArray (const uint32* array, int32 count) 348 | { 349 | for (int32 i = 0; i < count; i++) 350 | { 351 | if (!writeInt32u (array[i])) 352 | return false; 353 | } 354 | return true; 355 | } 356 | 357 | //------------------------------------------------------------------------ 358 | bool FStreamer::readInt32uArray (uint32* array, int32 count) 359 | { 360 | for (int32 i = 0; i < count; i++) 361 | { 362 | if (!readInt32u (array[i])) 363 | return false; 364 | } 365 | return true; 366 | } 367 | 368 | // int64 ----------------------------------------------------------------- 369 | //------------------------------------------------------------------------ 370 | bool FStreamer::writeInt64 (int64 i) 371 | { 372 | if (BYTEORDER != byteOrder) 373 | SWAP_64 (i); 374 | return writeRaw ((void*)&i, sizeof (int64)) == sizeof (int64); 375 | } 376 | 377 | //------------------------------------------------------------------------ 378 | bool FStreamer::readInt64 (int64& i) 379 | { 380 | if (readRaw ((void*)&i, sizeof (int64)) == sizeof (int64)) 381 | { 382 | if (BYTEORDER != byteOrder) 383 | SWAP_64 (i); 384 | return true; 385 | } 386 | i = 0; 387 | return false; 388 | } 389 | 390 | //------------------------------------------------------------------------ 391 | bool FStreamer::writeInt64Array (const int64* array, int32 count) 392 | { 393 | for (int32 i = 0; i < count; i++) 394 | { 395 | if (!writeInt64 (array[i])) 396 | return false; 397 | } 398 | return true; 399 | } 400 | 401 | //------------------------------------------------------------------------ 402 | bool FStreamer::readInt64Array (int64* array, int32 count) 403 | { 404 | for (int32 i = 0; i < count; i++) 405 | { 406 | if (!readInt64 (array[i])) 407 | return false; 408 | } 409 | return true; 410 | } 411 | 412 | //------------------------------------------------------------------------ 413 | bool FStreamer::writeInt64u (uint64 i) 414 | { 415 | if (BYTEORDER != byteOrder) 416 | SWAP_64 (i); 417 | return writeRaw ((void*)&i, sizeof (uint64)) == sizeof (uint64); 418 | } 419 | 420 | //------------------------------------------------------------------------ 421 | bool FStreamer::readInt64u (uint64& i) 422 | { 423 | if (readRaw ((void*)&i, sizeof (uint64)) == sizeof (uint64)) 424 | { 425 | if (BYTEORDER != byteOrder) 426 | SWAP_64 (i); 427 | return true; 428 | } 429 | i = 0; 430 | return false; 431 | } 432 | 433 | //------------------------------------------------------------------------ 434 | bool FStreamer::writeInt64uArray (const uint64* array, int32 count) 435 | { 436 | for (int32 i = 0; i < count; i++) 437 | { 438 | if (!writeInt64u (array[i])) 439 | return false; 440 | } 441 | return true; 442 | } 443 | 444 | //------------------------------------------------------------------------ 445 | bool FStreamer::readInt64uArray (uint64* array, int32 count) 446 | { 447 | for (int32 i = 0; i < count; i++) 448 | { 449 | if (!readInt64u (array[i])) 450 | return false; 451 | } 452 | return true; 453 | } 454 | 455 | // float / double -------------------------------------------------------- 456 | //------------------------------------------------------------------------ 457 | bool FStreamer::writeFloat (float f) 458 | { 459 | if (BYTEORDER != byteOrder) 460 | SWAP_32 (f); 461 | return writeRaw ((void*)&f, sizeof (float)) == sizeof (float); 462 | } 463 | 464 | //------------------------------------------------------------------------ 465 | bool FStreamer::readFloat (float& f) 466 | { 467 | if (readRaw ((void*)&f, sizeof (float)) == sizeof (float)) 468 | { 469 | if (BYTEORDER != byteOrder) 470 | SWAP_32 (f); 471 | return true; 472 | } 473 | f = 0.f; 474 | return false; 475 | } 476 | 477 | //------------------------------------------------------------------------ 478 | bool FStreamer::writeFloatArray (const float* array, int32 count) 479 | { 480 | for (int32 i = 0; i < count; i++) 481 | { 482 | if (!writeFloat (array[i])) 483 | return false; 484 | } 485 | return true; 486 | } 487 | 488 | //------------------------------------------------------------------------ 489 | bool FStreamer::readFloatArray (float* array, int32 count) 490 | { 491 | for (int32 i = 0; i < count; i++) 492 | { 493 | if (!readFloat (array[i])) 494 | return false; 495 | } 496 | return true; 497 | } 498 | 499 | //------------------------------------------------------------------------ 500 | bool FStreamer::writeDouble (double d) 501 | { 502 | if (BYTEORDER != byteOrder) 503 | SWAP_64 (d); 504 | return writeRaw ((void*)&d, sizeof (double)) == sizeof (double); 505 | } 506 | 507 | //------------------------------------------------------------------------ 508 | bool FStreamer::readDouble (double& d) 509 | { 510 | if (readRaw ((void*)&d, sizeof (double)) == sizeof (double)) 511 | { 512 | if (BYTEORDER != byteOrder) 513 | SWAP_64 (d); 514 | return true; 515 | } 516 | d = 0.0; 517 | return false; 518 | } 519 | 520 | //------------------------------------------------------------------------ 521 | bool FStreamer::writeDoubleArray (const double* array, int32 count) 522 | { 523 | for (int32 i = 0; i < count; i++) 524 | { 525 | if (!writeDouble (array[i])) 526 | return false; 527 | } 528 | return true; 529 | } 530 | 531 | //------------------------------------------------------------------------ 532 | bool FStreamer::readDoubleArray (double* array, int32 count) 533 | { 534 | for (int32 i = 0; i < count; i++) 535 | { 536 | if (!readDouble (array[i])) 537 | return false; 538 | } 539 | return true; 540 | } 541 | 542 | //------------------------------------------------------------------------ 543 | bool FStreamer::readBool (bool& b) 544 | { 545 | int16 v = 0; 546 | bool res = readInt16 (v); 547 | b = (v != 0); 548 | return res; 549 | } 550 | 551 | //------------------------------------------------------------------------ 552 | bool FStreamer::writeBool (bool b) 553 | { 554 | return writeInt16 ((int16)b); 555 | } 556 | 557 | //------------------------------------------------------------------------ 558 | TSize FStreamer::writeString8 (const char8* ptr, bool terminate) 559 | { 560 | TSize size = strlen (ptr); 561 | if (terminate) // write \0 562 | size++; 563 | 564 | return writeRaw ((void*)ptr, size); 565 | } 566 | 567 | //------------------------------------------------------------------------ 568 | TSize FStreamer::readString8 (char8* ptr, TSize size) 569 | { 570 | if (size < 1 || ptr == nullptr) 571 | return 0; 572 | 573 | TSize i = 0; 574 | char8 c = 0; 575 | while (i < size) 576 | { 577 | if (readRaw ((void*)&c, sizeof (char)) != sizeof (char)) 578 | break; 579 | ptr[i] = c; 580 | if (c == '\n' || c == '\0') 581 | break; 582 | i++; 583 | } 584 | // remove at end \n (LF) or \r\n (CR+LF) 585 | if (c == '\n') 586 | { 587 | if (i > 0 && ptr[i - 1] == '\r') 588 | i--; 589 | } 590 | if (i >= size) 591 | i = size - 1; 592 | ptr[i] = 0; 593 | 594 | return i; 595 | } 596 | 597 | //------------------------------------------------------------------------ 598 | bool FStreamer::writeStringUtf8 (const tchar* ptr) 599 | { 600 | bool isUtf8 = false; 601 | 602 | String str (ptr); 603 | if (str.isAsciiString () == false) 604 | { 605 | str.toMultiByte (kCP_Utf8); 606 | isUtf8 = true; 607 | } 608 | else 609 | { 610 | str.toMultiByte (); 611 | } 612 | 613 | if (isUtf8) 614 | if (writeRaw (kBomUtf8, kBomUtf8Length) != kBomUtf8Length) 615 | return false; 616 | 617 | TSize size = str.length () + 1; 618 | if (writeRaw (str.text8 (), size) != size) 619 | return false; 620 | 621 | return true; 622 | } 623 | 624 | //------------------------------------------------------------------------ 625 | int32 FStreamer::readStringUtf8 (tchar* ptr, int32 nChars) 626 | { 627 | char8 c = 0; 628 | 629 | ptr [0] = 0; 630 | 631 | Buffer tmp; 632 | tmp.setDelta (1024); 633 | 634 | while (true) 635 | { 636 | if (readRaw ((void*)&c, sizeof (char)) != sizeof (char)) 637 | break; 638 | tmp.put (c); 639 | if (c == '\0') 640 | break; 641 | } 642 | 643 | char8* source = tmp.str8 (); 644 | uint32 codePage = kCP_Default; // for legacy take default page if no utf8 bom is present... 645 | if (tmp.getFillSize () > 2) 646 | { 647 | if (memcmp (source, kBomUtf8, kBomUtf8Length) == 0) 648 | { 649 | codePage = kCP_Utf8; 650 | source += 3; 651 | } 652 | } 653 | 654 | if (tmp.getFillSize () > 1) 655 | { 656 | #ifdef UNICODE 657 | ConstString::multiByteToWideString (ptr, source, nChars, codePage); 658 | #else 659 | if (codePage == kCP_Utf8) 660 | { 661 | Buffer wideBuffer (tmp.getFillSize () * 3); 662 | ConstString::multiByteToWideString (wideBuffer.wcharPtr (), source, wideBuffer.getSize () / 2, kCP_Utf8); 663 | ConstString::wideStringToMultiByte (ptr, wideBuffer.wcharPtr (), nChars); 664 | } 665 | else 666 | { 667 | memcpy (ptr, source, Min (nChars, tmp.getFillSize ())); 668 | } 669 | #endif 670 | } 671 | 672 | ptr[nChars - 1] = 0; 673 | return ConstString (ptr).length (); 674 | } 675 | 676 | //------------------------------------------------------------------------ 677 | bool FStreamer::writeStr8 (const char8* s) 678 | { 679 | int32 length = (s) ? (int32) strlen (s) + 1 : 0; 680 | if (!writeInt32 (length)) 681 | return false; 682 | 683 | if (length > 0) 684 | return writeRaw (s, sizeof (char8) * length) == static_cast(sizeof (char8) * length); 685 | 686 | return true; 687 | } 688 | 689 | //------------------------------------------------------------------------ 690 | int32 FStreamer::getStr8Size (const char8* s) 691 | { 692 | return sizeof (int32) + (int32)strlen (s) + 1; 693 | } 694 | 695 | //------------------------------------------------------------------------ 696 | char8* FStreamer::readStr8 () 697 | { 698 | int32 length; 699 | if (!readInt32 (length)) 700 | return nullptr; 701 | 702 | // check corruption 703 | if (length > 262144) 704 | return nullptr; 705 | 706 | char8* s = (length > 0) ? NEWSTR8 (length) : nullptr; 707 | if (s) 708 | readRaw (s, length * sizeof (char8)); 709 | return s; 710 | } 711 | 712 | //------------------------------------------------------------------------ 713 | bool FStreamer::skip (uint32 bytes) 714 | { 715 | int8 tmp; 716 | while (bytes-- > 0) 717 | { 718 | if (readInt8 (tmp) == false) 719 | return false; 720 | } 721 | return true; 722 | } 723 | 724 | //------------------------------------------------------------------------ 725 | bool FStreamer::pad (uint32 bytes) 726 | { 727 | while (bytes-- > 0) 728 | { 729 | if (writeInt8 (0) == false) 730 | return false; 731 | } 732 | return true; 733 | } 734 | 735 | //------------------------------------------------------------------------ 736 | } // namespace Steinberg 737 | -------------------------------------------------------------------------------- /source/fobject.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Project : SDK Base 3 | // Version : 1.0 4 | // 5 | // Category : Helpers 6 | // Filename : base/source/fobject.h 7 | // Created by : Steinberg, 2008 8 | // Description : Basic Object implementing FUnknown 9 | // 10 | //----------------------------------------------------------------------------- 11 | // This file is part of a Steinberg SDK. It is subject to the license terms 12 | // in the LICENSE file found in the top-level directory of this distribution 13 | // and at www.steinberg.net/sdklicenses. 14 | // No part of the SDK, including this file, may be copied, modified, propagated, 15 | // or distributed except according to the terms contained in the LICENSE file. 16 | //----------------------------------------------------------------------------- 17 | 18 | //------------------------------------------------------------------------ 19 | /** @file base/source/fobject.h 20 | Basic Object implementing FUnknown. */ 21 | //------------------------------------------------------------------------ 22 | 23 | #pragma once 24 | 25 | #include "pluginterfaces/base/funknown.h" 26 | #include "pluginterfaces/base/iupdatehandler.h" 27 | #include "base/source/fdebug.h" // use of NEW 28 | 29 | #define SMTG_DEPENDENCY_COUNT DEVELOPMENT 30 | 31 | namespace Steinberg { 32 | 33 | //---------------------------------- 34 | 35 | using FClassID = FIDString; 36 | 37 | //------------------------------------------------------------------------ 38 | // Basic FObject - implements FUnknown + IDependent 39 | //------------------------------------------------------------------------ 40 | /** Implements FUnknown and IDependent. 41 | 42 | FObject is a polymorphic class that implements IDependent (of SKI module) and therefore derived from 43 | FUnknown, which is the most abstract base class of all. 44 | 45 | All COM-like virtual methods of FUnknown such as queryInterface(), addRef(), release() are 46 | implemented here. On top of that, dependency-related methods are implemented too. 47 | 48 | Pointer casting is done via the template methods FCast, either FObject to FObject or FUnknown to 49 | FObject. 50 | 51 | FObject supports a new singleton concept, therefore these objects are deleted automatically upon 52 | program termination. 53 | 54 | - Runtime type information: An object can be queried at runtime, of what class it is. To do this 55 | correctly, every class must override some methods. This is simplified by using the OBJ_METHODS 56 | macros 57 | 58 | @see 59 | - FUnknown 60 | - IDependent 61 | - IUpdateHandler 62 | */ 63 | //------------------------------------------------------------------------ 64 | class FObject : public IDependent 65 | { 66 | public: 67 | //------------------------------------------------------------------------ 68 | FObject () = default; ///< default constructor... 69 | FObject (const FObject&) ///< overloaded constructor... 70 | : refCount (1) 71 | #if SMTG_DEPENDENCY_COUNT 72 | , dependencyCount (0) 73 | #endif 74 | {} 75 | FObject& operator= (const FObject&) { return *this; } ///< overloads operator "=" as the reference assignment 76 | virtual ~FObject (); ///< destructor... 77 | 78 | // OBJECT_METHODS 79 | static inline FClassID getFClassID () {return "FObject";} ///< return Class ID as an ASCII string (statically) 80 | virtual FClassID isA () const {return FObject::getFClassID ();} ///< a local alternative to getFClassID () 81 | virtual bool isA (FClassID s) const {return isTypeOf (s, false);} ///< evaluates if the passed ID is of the FObject type 82 | virtual bool isTypeOf (FClassID s, bool /*askBaseClass*/ = true) const {return classIDsEqual (s, FObject::getFClassID ());} 83 | ///< evaluates if the passed ID is of the FObject type 84 | int32 getRefCount () {return refCount;} ///< returns the current interface reference count 85 | FUnknown* unknownCast () {return this;} ///< get FUnknown interface from object 86 | 87 | // FUnknown 88 | tresult PLUGIN_API queryInterface (const TUID _iid, void** obj) SMTG_OVERRIDE; ///< please refer to FUnknown::queryInterface () 89 | uint32 PLUGIN_API addRef () SMTG_OVERRIDE; ///< please refer to FUnknown::addref () 90 | uint32 PLUGIN_API release () SMTG_OVERRIDE; ///< please refer to FUnknown::release () 91 | 92 | // IDependent 93 | void PLUGIN_API update (FUnknown* /*changedUnknown*/, int32 /*message*/) SMTG_OVERRIDE {} 94 | ///< empty virtual method that should be overridden by derived classes for data updates upon changes 95 | // IDependency 96 | virtual void addDependent (IDependent* dep); ///< adds dependency to the object 97 | virtual void removeDependent (IDependent* dep); ///< removes dependency from the object 98 | virtual void changed (int32 msg = kChanged); ///< Inform all dependents, that the object has changed. 99 | virtual void deferUpdate (int32 msg = kChanged); ///< Similar to triggerUpdates, except only delivered in idle (usefull in collecting updates). 100 | virtual void updateDone (int32 /* msg */) {} ///< empty virtual method that should be overridden by derived classes 101 | virtual bool isEqualInstance (FUnknown* d) {return this == d;} 102 | 103 | static void setUpdateHandler (IUpdateHandler* handler) {gUpdateHandler = handler;} ///< set method for the local attribute 104 | static IUpdateHandler* getUpdateHandler () {return gUpdateHandler;} ///< get method for the local attribute 105 | 106 | // static helper functions 107 | static inline bool classIDsEqual (FClassID ci1, FClassID ci2); ///< compares (evaluates) 2 class IDs 108 | static inline FObject* unknownToObject (FUnknown* unknown); ///< pointer conversion from FUnknown to FObject 109 | /** convert from FUnknown to FObject */ 110 | template 111 | static inline IPtr fromUnknown (FUnknown* unknown); 112 | 113 | /** Special UID that is used to cast an FUnknown pointer to a FObject */ 114 | static const FUID iid; 115 | 116 | //------------------------------------------------------------------------ 117 | protected: 118 | int32 refCount = 1; ///< COM-model local reference count 119 | #if SMTG_DEPENDENCY_COUNT 120 | int16 dependencyCount = 0; 121 | #endif 122 | static IUpdateHandler* gUpdateHandler; 123 | }; 124 | 125 | 126 | //------------------------------------------------------------------------ 127 | // conversion from FUnknown to FObject subclass 128 | //------------------------------------------------------------------------ 129 | template 130 | inline IPtr FObject::fromUnknown (FUnknown* unknown) 131 | { 132 | if (unknown) 133 | { 134 | FObject* object = nullptr; 135 | if (unknown->queryInterface (FObject::iid, (void**)&object) == kResultTrue && object) 136 | { 137 | if (object->isTypeOf (C::getFClassID (), true)) 138 | return IPtr (static_cast (object), false); 139 | object->release (); 140 | } 141 | } 142 | return {}; 143 | } 144 | 145 | //------------------------------------------------------------------------ 146 | inline FObject* FObject::unknownToObject (FUnknown* unknown) 147 | { 148 | FObject* object = nullptr; 149 | if (unknown) 150 | { 151 | unknown->queryInterface (FObject::iid, (void**)&object); 152 | if (object) 153 | { 154 | if (object->release () == 0) 155 | object = nullptr; 156 | } 157 | } 158 | return object; 159 | } 160 | 161 | //------------------------------------------------------------------------ 162 | inline bool FObject::classIDsEqual (FClassID ci1, FClassID ci2) 163 | { 164 | return (ci1 && ci2) ? (strcmp (ci1, ci2) == 0) : false; 165 | } 166 | 167 | //----------------------------------------------------------------------- 168 | /** FCast overload 1 - FObject to FObject */ 169 | //----------------------------------------------------------------------- 170 | template 171 | inline C* FCast (const FObject* object) 172 | { 173 | if (object && object->isTypeOf (C::getFClassID (), true)) 174 | return (C*) object; 175 | return nullptr; 176 | } 177 | 178 | //----------------------------------------------------------------------- 179 | /** FCast overload 2 - FUnknown to FObject */ 180 | //----------------------------------------------------------------------- 181 | template 182 | inline C* FCast (FUnknown* unknown) 183 | { 184 | FObject* object = FObject::unknownToObject (unknown); 185 | return FCast (object); 186 | } 187 | 188 | //----------------------------------------------------------------------- 189 | /** ICast - casting from FObject to FUnknown Interface */ 190 | //----------------------------------------------------------------------- 191 | template 192 | inline IPtr ICast (FObject* object) 193 | { 194 | return FUnknownPtr (object ? object->unknownCast () : nullptr); 195 | } 196 | 197 | //----------------------------------------------------------------------- 198 | /** ICast - casting from FUnknown to another FUnknown Interface */ 199 | //----------------------------------------------------------------------- 200 | template 201 | inline IPtr ICast (FUnknown* object) 202 | { 203 | return FUnknownPtr (object); 204 | } 205 | 206 | //------------------------------------------------------------------------ 207 | template 208 | inline C* FCastIsA (const FObject* object) 209 | { 210 | if (object && object->isA (C::getFClassID ())) 211 | return (C*)object; 212 | return nullptr; 213 | } 214 | 215 | #ifndef SMTG_HIDE_DEPRECATED_INLINE_FUNCTIONS 216 | //----------------------------------------------------------------------- 217 | /** \deprecated FUCast - casting from FUnknown to Interface */ 218 | //----------------------------------------------------------------------- 219 | template 220 | SMTG_DEPRECATED_MSG("use ICast<>") inline C* FUCast (FObject* object) 221 | { 222 | return FUnknownPtr (object ? object->unknownCast () : nullptr); 223 | } 224 | 225 | template 226 | SMTG_DEPRECATED_MSG("use ICast<>") inline C* FUCast (FUnknown* object) 227 | { 228 | return FUnknownPtr (object); 229 | } 230 | #endif // SMTG_HIDE_DEPRECATED_FUNCTIONS 231 | 232 | //------------------------------------------------------------------------ 233 | /** @name Convenience methods that call release or delete respectively 234 | on a pointer if it is non-zero, and then set the pointer to zero. 235 | Note: you should prefer using IPtr or OPtr instead of these methods 236 | whenever possible. 237 | Examples: 238 | @code 239 | ~Foo () 240 | { 241 | // instead of ... 242 | if (somePointer) 243 | { 244 | somePointer->release (); 245 | somePointer = 0; 246 | } 247 | // ... just being lazy I write 248 | SafeRelease (somePointer) 249 | } 250 | @endcode 251 | */ 252 | ///@{ 253 | //----------------------------------------------------------------------- 254 | template 255 | inline void SafeRelease (I *& ptr) 256 | { 257 | if (ptr) 258 | { 259 | ptr->release (); 260 | ptr = 0; 261 | } 262 | } 263 | 264 | //----------------------------------------------------------------------- 265 | template 266 | inline void SafeRelease (IPtr & ptr) 267 | { 268 | ptr = 0; 269 | } 270 | 271 | 272 | //----------------------------------------------------------------------- 273 | template 274 | inline void SafeDelete (T *& ptr) 275 | { 276 | if (ptr) 277 | { 278 | delete ptr; 279 | ptr = 0; 280 | } 281 | } 282 | ///@} 283 | 284 | //----------------------------------------------------------------------- 285 | template 286 | inline void AssignShared (T*& dest, T* newPtr) 287 | { 288 | if (dest == newPtr) 289 | return; 290 | 291 | if (dest) 292 | dest->release (); 293 | dest = newPtr; 294 | if (dest) 295 | dest->addRef (); 296 | } 297 | 298 | //----------------------------------------------------------------------- 299 | template 300 | inline void AssignSharedDependent (IDependent* _this, T*& dest, T* newPtr) 301 | { 302 | if (dest == newPtr) 303 | return; 304 | 305 | if (dest) 306 | dest->removeDependent (_this); 307 | AssignShared (dest, newPtr); 308 | if (dest) 309 | dest->addDependent (_this); 310 | } 311 | 312 | //----------------------------------------------------------------------- 313 | template 314 | inline void AssignSharedDependent (IDependent* _this, IPtr& dest, T* newPtr) 315 | { 316 | if (dest == newPtr) 317 | return; 318 | 319 | if (dest) 320 | dest->removeDependent (_this); 321 | dest = newPtr; 322 | if (dest) 323 | dest->addDependent (_this); 324 | } 325 | 326 | //----------------------------------------------------------------------- 327 | template 328 | inline void SafeReleaseDependent (IDependent* _this, T*& dest) 329 | { 330 | if (dest) 331 | dest->removeDependent (_this); 332 | SafeRelease (dest); 333 | } 334 | 335 | //----------------------------------------------------------------------- 336 | template 337 | inline void SafeReleaseDependent (IDependent* _this, IPtr& dest) 338 | { 339 | if (dest) 340 | dest->removeDependent (_this); 341 | SafeRelease (dest); 342 | } 343 | 344 | 345 | //------------------------------------------------------------------------ 346 | /** Automatic creation and destruction of singleton instances. */ 347 | namespace Singleton { 348 | /** registers an instance (type FObject) */ 349 | void registerInstance (FObject** o); 350 | 351 | /** Returns true when singleton instances were already released. */ 352 | bool isTerminated (); 353 | 354 | /** lock and unlock the singleton registration for multi-threading safety */ 355 | void lockRegister (); 356 | void unlockRegister (); 357 | } 358 | 359 | //------------------------------------------------------------------------ 360 | } // namespace Steinberg 361 | 362 | //----------------------------------------------------------------------- 363 | #define SINGLETON(ClassName) \ 364 | static ClassName* instance (bool create = true) \ 365 | { \ 366 | static Steinberg::FObject* inst = nullptr; \ 367 | if (inst == nullptr && create && Steinberg::Singleton::isTerminated () == false) \ 368 | { \ 369 | Steinberg::Singleton::lockRegister (); \ 370 | if (inst == nullptr) \ 371 | { \ 372 | inst = NEW ClassName; \ 373 | Steinberg::Singleton::registerInstance (&inst); \ 374 | } \ 375 | Steinberg::Singleton::unlockRegister (); \ 376 | } \ 377 | return (ClassName*)inst; \ 378 | } 379 | 380 | //----------------------------------------------------------------------- 381 | #define OBJ_METHODS(className, baseClass) \ 382 | static inline Steinberg::FClassID getFClassID () {return (#className);} \ 383 | virtual Steinberg::FClassID isA () const SMTG_OVERRIDE {return className::getFClassID ();} \ 384 | virtual bool isA (Steinberg::FClassID s) const SMTG_OVERRIDE {return isTypeOf (s, false);} \ 385 | virtual bool isTypeOf (Steinberg::FClassID s, bool askBaseClass = true) const SMTG_OVERRIDE \ 386 | { return (FObject::classIDsEqual (s, #className) ? true : (askBaseClass ? baseClass::isTypeOf (s, true) : false)); } 387 | 388 | //------------------------------------------------------------------------ 389 | /** Delegate refcount functions to BaseClass. 390 | BaseClase must implement ref counting. 391 | */ 392 | //------------------------------------------------------------------------ 393 | #define REFCOUNT_METHODS(BaseClass) \ 394 | virtual Steinberg::uint32 PLUGIN_API addRef ()SMTG_OVERRIDE{ return BaseClass::addRef (); } \ 395 | virtual Steinberg::uint32 PLUGIN_API release ()SMTG_OVERRIDE{ return BaseClass::release (); } 396 | 397 | //------------------------------------------------------------------------ 398 | /** @name Macros to implement FUnknown::queryInterface (). 399 | 400 | Examples: 401 | @code 402 | class Foo : public FObject, public IFoo2, public IFoo3 403 | { 404 | ... 405 | DEFINE_INTERFACES 406 | DEF_INTERFACE (IFoo2) 407 | DEF_INTERFACE (IFoo3) 408 | END_DEFINE_INTERFACES (FObject) 409 | REFCOUNT_METHODS(FObject) 410 | // Implement IFoo2 interface ... 411 | // Implement IFoo3 interface ... 412 | ... 413 | }; 414 | @endcode 415 | */ 416 | ///@{ 417 | //------------------------------------------------------------------------ 418 | /** Start defining interfaces. */ 419 | //------------------------------------------------------------------------ 420 | #define DEFINE_INTERFACES \ 421 | Steinberg::tresult PLUGIN_API queryInterface (const Steinberg::TUID iid, void** obj) SMTG_OVERRIDE \ 422 | { 423 | 424 | //------------------------------------------------------------------------ 425 | /** Add a interfaces. */ 426 | //------------------------------------------------------------------------ 427 | #define DEF_INTERFACE(InterfaceName) \ 428 | QUERY_INTERFACE (iid, obj, Steinberg::getTUID (), InterfaceName) 429 | 430 | //------------------------------------------------------------------------ 431 | /** End defining interfaces. */ 432 | //------------------------------------------------------------------------ 433 | #define END_DEFINE_INTERFACES(BaseClass) \ 434 | return BaseClass::queryInterface (iid, obj); \ 435 | } 436 | ///@} 437 | 438 | //------------------------------------------------------------------------ 439 | /** @name Convenient macros to implement Steinberg::FUnknown::queryInterface (). 440 | Examples: 441 | @code 442 | class Foo : public FObject, public IFoo2, public IFoo3 443 | { 444 | ... 445 | DEF_INTERFACES_2(IFoo2,IFoo3,FObject) 446 | REFCOUNT_METHODS(FObject) 447 | ... 448 | }; 449 | @endcode 450 | */ 451 | ///@{ 452 | //------------------------------------------------------------------------ 453 | #define DEF_INTERFACES_1(InterfaceName,BaseClass) \ 454 | DEFINE_INTERFACES \ 455 | DEF_INTERFACE (InterfaceName) \ 456 | END_DEFINE_INTERFACES (BaseClass) 457 | 458 | //------------------------------------------------------------------------ 459 | #define DEF_INTERFACES_2(InterfaceName1,InterfaceName2,BaseClass) \ 460 | DEFINE_INTERFACES \ 461 | DEF_INTERFACE (InterfaceName1) \ 462 | DEF_INTERFACE (InterfaceName2) \ 463 | END_DEFINE_INTERFACES (BaseClass) 464 | 465 | //------------------------------------------------------------------------ 466 | #define DEF_INTERFACES_3(InterfaceName1,InterfaceName2,InterfaceName3,BaseClass) \ 467 | DEFINE_INTERFACES \ 468 | DEF_INTERFACE (InterfaceName1) \ 469 | DEF_INTERFACE (InterfaceName2) \ 470 | DEF_INTERFACE (InterfaceName3) \ 471 | END_DEFINE_INTERFACES (BaseClass) 472 | 473 | //------------------------------------------------------------------------ 474 | #define DEF_INTERFACES_4(InterfaceName1,InterfaceName2,InterfaceName3,InterfaceName4,BaseClass) \ 475 | DEFINE_INTERFACES \ 476 | DEF_INTERFACE (InterfaceName1) \ 477 | DEF_INTERFACE (InterfaceName2) \ 478 | DEF_INTERFACE (InterfaceName3) \ 479 | DEF_INTERFACE (InterfaceName4) \ 480 | END_DEFINE_INTERFACES (BaseClass) 481 | ///@} 482 | 483 | //------------------------------------------------------------------------ 484 | /** @name Convenient macros to implement Steinberg::FUnknown methods. 485 | Examples: 486 | @code 487 | class Foo : public FObject, public IFoo2, public IFoo3 488 | { 489 | ... 490 | FUNKNOWN_METHODS2(IFoo2,IFoo3,FObject) 491 | ... 492 | }; 493 | @endcode 494 | */ 495 | ///@{ 496 | #define FUNKNOWN_METHODS(InterfaceName,BaseClass) \ 497 | DEF_INTERFACES_1(InterfaceName,BaseClass) \ 498 | REFCOUNT_METHODS(BaseClass) 499 | 500 | #define FUNKNOWN_METHODS2(InterfaceName1,InterfaceName2,BaseClass) \ 501 | DEF_INTERFACES_2(InterfaceName1,InterfaceName2,BaseClass) \ 502 | REFCOUNT_METHODS(BaseClass) 503 | 504 | #define FUNKNOWN_METHODS3(InterfaceName1,InterfaceName2,InterfaceName3,BaseClass) \ 505 | DEF_INTERFACES_3(InterfaceName1,InterfaceName2,InterfaceName3,BaseClass) \ 506 | REFCOUNT_METHODS(BaseClass) 507 | 508 | #define FUNKNOWN_METHODS4(InterfaceName1,InterfaceName2,InterfaceName3,InterfaceName4,BaseClass) \ 509 | DEF_INTERFACES_4(InterfaceName1,InterfaceName2,InterfaceName3,InterfaceName4,BaseClass) \ 510 | REFCOUNT_METHODS(BaseClass) 511 | ///@} 512 | 513 | 514 | //------------------------------------------------------------------------ 515 | //------------------------------------------------------------------------ 516 | #if COM_COMPATIBLE 517 | //------------------------------------------------------------------------ 518 | /** @name Macros to implement IUnknown interfaces with FObject. 519 | Examples: 520 | @code 521 | class MyEnumFormat : public FObject, IEnumFORMATETC 522 | { 523 | ... 524 | COM_UNKNOWN_METHODS (IEnumFORMATETC, IUnknown) 525 | ... 526 | }; 527 | @endcode 528 | */ 529 | ///@{ 530 | //------------------------------------------------------------------------ 531 | #define IUNKNOWN_REFCOUNT_METHODS(BaseClass) \ 532 | STDMETHOD_ (ULONG, AddRef) (void) {return BaseClass::addRef ();} \ 533 | STDMETHOD_ (ULONG, Release) (void) {return BaseClass::release ();} 534 | 535 | //------------------------------------------------------------------------ 536 | #define COM_QUERY_INTERFACE(iid, obj, InterfaceName) \ 537 | if (riid == __uuidof(InterfaceName)) \ 538 | { \ 539 | addRef (); \ 540 | *obj = (InterfaceName*)this; \ 541 | return kResultOk; \ 542 | } 543 | 544 | //------------------------------------------------------------------------ 545 | #define COM_OBJECT_QUERY_INTERFACE(InterfaceName,BaseClass) \ 546 | STDMETHOD (QueryInterface) (REFIID riid, void** object) \ 547 | { \ 548 | COM_QUERY_INTERFACE (riid, object, InterfaceName) \ 549 | return BaseClass::queryInterface ((FIDString)&riid, object); \ 550 | } 551 | 552 | //------------------------------------------------------------------------ 553 | #define COM_UNKNOWN_METHODS(InterfaceName,BaseClass) \ 554 | COM_OBJECT_QUERY_INTERFACE(InterfaceName,BaseClass) \ 555 | IUNKNOWN_REFCOUNT_METHODS(BaseClass) 556 | ///@} 557 | 558 | #endif // COM_COMPATIBLE 559 | -------------------------------------------------------------------------------- /source/updatehandler.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Project : SDK Base 3 | // Version : 1.0 4 | // 5 | // Category : Helpers 6 | // Filename : base/source/updatehandler.cpp 7 | // Created by : Steinberg, 2008 8 | // Description : 9 | // 10 | //----------------------------------------------------------------------------- 11 | // This file is part of a Steinberg SDK. It is subject to the license terms 12 | // in the LICENSE file found in the top-level directory of this distribution 13 | // and at www.steinberg.net/sdklicenses. 14 | // No part of the SDK, including this file, may be copied, modified, propagated, 15 | // or distributed except according to the terms contained in the LICENSE file. 16 | //----------------------------------------------------------------------------- 17 | 18 | #include "base/source/updatehandler.h" 19 | #include "base/source/classfactoryhelpers.h" 20 | #include "base/source/fstring.h" 21 | 22 | #if SMTG_CPP11_STDLIBSUPPORT 23 | #include 24 | #else 25 | #include 26 | #endif 27 | #include 28 | #include 29 | #include 30 | 31 | #define NON_EXISTING_DEPENDENCY_CHECK 0 // not yet 32 | #define CLASS_NAME_TRACKED DEVELOPMENT 33 | 34 | using Steinberg::Base::Thread::FGuard; 35 | 36 | namespace Steinberg { 37 | 38 | DEF_CLASS_IID (IUpdateManager) 39 | 40 | namespace Update { 41 | const uint32 kHashSize = (1 << 8); // must be power of 2 (16 bytes * 256 == 4096) 42 | const uint32 kMapSize = 1024 * 10; 43 | 44 | //------------------------------------------------------------------------ 45 | inline uint32 hashPointer (void* p) 46 | { 47 | return (uint32)((uint64 (p) >> 12) & (kHashSize - 1)); 48 | } 49 | 50 | //------------------------------------------------------------------------ 51 | inline IPtr getUnknownBase (FUnknown* unknown) 52 | { 53 | FUnknown* result = nullptr; 54 | if (unknown) 55 | { 56 | if (unknown->queryInterface (FObject::iid, (void**)&result) != kResultTrue) 57 | unknown->queryInterface (FUnknown::iid, (void**)&result); 58 | } 59 | 60 | return owned (result); 61 | } 62 | 63 | #if CLASS_NAME_TRACKED 64 | //------------------------------------------------------------------------ 65 | struct Dependency 66 | { 67 | Dependency (FUnknown* o, IDependent* d) 68 | : obj (o), dep (d), objClass (nullptr), depClass (nullptr) 69 | { 70 | } 71 | 72 | inline bool operator== (const Dependency& d) const { return obj == d.obj; } 73 | inline bool operator!= (const Dependency& d) const { return obj != d.obj; } 74 | inline bool operator< (const Dependency& d) const { return obj < d.obj; } 75 | inline bool operator> (const Dependency& d) const { return obj > d.obj; } 76 | FUnknown* obj; 77 | IDependent* dep; 78 | 79 | FClassID objClass; 80 | FClassID depClass; 81 | }; 82 | #endif 83 | 84 | //------------------------------------------------------------------------ 85 | struct DeferedChange 86 | { 87 | DeferedChange (FUnknown* o, int32 m = 0) : obj (o), msg (m) {} 88 | ~DeferedChange () {} 89 | DeferedChange (const DeferedChange& r) : obj (r.obj), msg (r.msg) {} 90 | inline bool operator== (const DeferedChange& d) const { return obj == d.obj; } 91 | inline bool operator!= (const DeferedChange& d) const { return obj != d.obj; } 92 | FUnknown* obj; 93 | int32 msg; 94 | }; 95 | 96 | //------------------------------------------------------------------------ 97 | struct UpdateData 98 | { 99 | UpdateData (FUnknown* o, IDependent** d, uint32 c) 100 | : obj (o), dependents (d), count (c) 101 | { 102 | } 103 | FUnknown* obj; 104 | IDependent** dependents; 105 | uint32 count; 106 | bool operator== (const UpdateData& d) const 107 | { 108 | return d.obj == obj && d.dependents == dependents; 109 | } 110 | }; 111 | 112 | //------------------------------------------------------------------------ 113 | using DeferedChangeList = std::deque; 114 | using DeferedChangeListIterConst = DeferedChangeList::const_iterator; 115 | using DeferedChangeListIter = DeferedChangeList::iterator; 116 | 117 | using UpdateDataList = std::deque; 118 | using UpdateDataListIterConst = UpdateDataList::const_iterator; 119 | 120 | #if CLASS_NAME_TRACKED 121 | using DependentList = std::vector; 122 | #else 123 | typedef std::vector DependentList; 124 | #endif 125 | using DependentListIter = DependentList::iterator; 126 | using DependentListIterConst = DependentList::const_iterator; 127 | 128 | #if SMTG_CPP11_STDLIBSUPPORT 129 | using DependentMap = std::unordered_map; 130 | #else 131 | typedef std::map DependentMap; 132 | #endif 133 | using DependentMapIter = DependentMap::iterator; 134 | using DependentMapIterConst = DependentMap::const_iterator; 135 | 136 | struct Table 137 | { 138 | DependentMap depMap[kHashSize]; 139 | DeferedChangeList defered; 140 | UpdateDataList updateData; 141 | }; 142 | 143 | //------------------------------------------------------------------------ 144 | void updateDone (FUnknown* unknown, int32 message) 145 | { 146 | if (message != IDependent::kDestroyed) 147 | { 148 | FObject* obj = FObject::unknownToObject (unknown); 149 | if (obj) 150 | obj->updateDone (message); 151 | } 152 | } 153 | } // namespace Update 154 | 155 | //------------------------------------------------------------------------ 156 | static int32 countEntries (Update::DependentMap& map) 157 | { 158 | int32 total = 0; 159 | Update::DependentMapIterConst iterMap = map.begin (); 160 | while (iterMap != map.end ()) 161 | { 162 | const Update::DependentList& list = iterMap->second; 163 | Update::DependentListIterConst iterList = list.begin (); 164 | while (iterList != list.end ()) 165 | { 166 | total++; 167 | ++iterList; 168 | } 169 | ++iterMap; 170 | } 171 | return total; 172 | } 173 | 174 | //------------------------------------------------------------------------ 175 | UpdateHandler::UpdateHandler () 176 | { 177 | table = NEW Update::Table; 178 | if (FObject::getUpdateHandler () == nullptr) 179 | FObject::setUpdateHandler (this); 180 | } 181 | 182 | //------------------------------------------------------------------------ 183 | UpdateHandler::~UpdateHandler () 184 | { 185 | if (FObject::getUpdateHandler () == this) 186 | FObject::setUpdateHandler (nullptr); 187 | delete table; 188 | table = nullptr; 189 | } 190 | 191 | //------------------------------------------------------------------------ 192 | tresult PLUGIN_API UpdateHandler::addDependent (FUnknown* u, IDependent* _dependent) 193 | { 194 | IPtr unknown = Update::getUnknownBase (u); 195 | if (!unknown || !_dependent) 196 | return kResultFalse; 197 | 198 | FGuard guard (lock); 199 | 200 | #if CLASS_NAME_TRACKED 201 | Update::Dependency dependent (unknown, _dependent); 202 | 203 | FObject* obj = FObject::unknownToObject (unknown); 204 | if (obj) 205 | dependent.objClass = obj->isA (); 206 | obj = FObject::unknownToObject (_dependent); 207 | if (obj) 208 | dependent.depClass = obj->isA (); 209 | #else 210 | IDependent* dependent = _dependent; 211 | #endif 212 | 213 | Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)]; 214 | Update::DependentMapIter it = map.find (unknown); 215 | if (it == map.end ()) 216 | { 217 | Update::DependentList list; 218 | list.push_back (dependent); 219 | map[unknown] = list; 220 | } 221 | else 222 | { 223 | (*it).second.push_back (dependent); 224 | } 225 | 226 | return kResultTrue; 227 | } 228 | //------------------------------------------------------------------------ 229 | tresult PLUGIN_API UpdateHandler::removeDependent (FUnknown* u, IDependent* dependent) 230 | { 231 | size_t eraseCount; 232 | return removeDependent (u, dependent, eraseCount); 233 | } 234 | 235 | //------------------------------------------------------------------------ 236 | tresult PLUGIN_API UpdateHandler::removeDependent (FUnknown* u, IDependent* dependent, size_t& eraseCount) 237 | { 238 | eraseCount = 0; 239 | IPtr unknown = Update::getUnknownBase (u); 240 | if (unknown == nullptr && dependent == nullptr) 241 | return kResultFalse; 242 | 243 | FGuard guard (lock); 244 | 245 | Update::UpdateDataListIterConst iter = table->updateData.begin (); 246 | while (iter != table->updateData.end ()) 247 | { 248 | if ((*iter).obj == unknown || unknown == nullptr) 249 | { 250 | for (uint32 count = 0; count < (*iter).count; count++) 251 | { 252 | if ((*iter).dependents[count] == dependent) 253 | (*iter).dependents[count] = nullptr; 254 | } 255 | } 256 | ++iter; 257 | } 258 | // Remove all objects for the given dependent 259 | if (unknown == nullptr) 260 | { 261 | for (uint32 j = 0; j < Update::kHashSize; j++) 262 | { 263 | Update::DependentMap& map = table->depMap[j]; 264 | Update::DependentMapIter iterMap = map.begin (); 265 | while (iterMap != map.end ()) 266 | { 267 | Update::DependentList& list = (*iterMap).second; 268 | Update::DependentListIter iterList = list.begin (); 269 | bool listIsEmpty = false; 270 | 271 | while (iterList != list.end ()) 272 | { 273 | #if CLASS_NAME_TRACKED 274 | if ((*iterList).dep == dependent) 275 | #else 276 | if ((*iterList) == dependent) 277 | #endif 278 | { 279 | eraseCount = list.size (); 280 | if (list.size () == 1u) 281 | { 282 | listIsEmpty = true; 283 | break; 284 | } 285 | iterList = list.erase (iterList); 286 | } 287 | else 288 | { 289 | ++iterList; 290 | } 291 | } 292 | 293 | if (listIsEmpty) 294 | iterMap = map.erase (iterMap); 295 | else 296 | ++iterMap; 297 | } 298 | } 299 | } 300 | else 301 | { 302 | bool mustFlush = true; 303 | 304 | Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)]; 305 | Update::DependentMapIter iterList = map.find (unknown); 306 | 307 | #if NON_EXISTING_DEPENDENCY_CHECK 308 | SMTG_ASSERT (iterList != map.end () && "ERROR: Trying to remove a non existing dependency!") 309 | #endif 310 | if (iterList != map.end ()) 311 | { 312 | if (dependent == nullptr) // Remove all dependents of object 313 | { 314 | eraseCount = iterList->second.size (); 315 | map.erase (iterList); 316 | } 317 | else // Remove one dependent 318 | { 319 | Update::DependentList& dependentlist = (*iterList).second; 320 | Update::DependentListIter iterDependentlist = dependentlist.begin (); 321 | while (iterDependentlist != dependentlist.end ()) 322 | { 323 | #if CLASS_NAME_TRACKED 324 | if ((*iterDependentlist).dep == dependent) 325 | #else 326 | if ((*iterDependentlist) == dependent) 327 | #endif 328 | { 329 | iterDependentlist = dependentlist.erase (iterDependentlist); 330 | eraseCount++; 331 | if (dependentlist.empty ()) 332 | { 333 | map.erase (iterList); 334 | break; 335 | } 336 | } 337 | else 338 | { 339 | ++iterDependentlist; 340 | mustFlush = false; 341 | } 342 | } 343 | } 344 | } 345 | if (mustFlush) 346 | cancelUpdates (unknown); 347 | } 348 | 349 | return kResultTrue; 350 | } 351 | 352 | //------------------------------------------------------------------------ 353 | tresult UpdateHandler::doTriggerUpdates (FUnknown* u, int32 message, bool suppressUpdateDone) 354 | { 355 | IPtr unknown = Update::getUnknownBase (u); 356 | if (!unknown) 357 | return kResultFalse; 358 | 359 | // to avoid crashes due to stack overflow, we reduce the amount of memory reserved for the 360 | // dependents 361 | IDependent* smallDependents[Update::kMapSize / 10]; // 8kB for x64 362 | IDependent** dependents = smallDependents; 363 | int32 maxDependents = Update::kMapSize / 10; 364 | int32 count = 0; 365 | 366 | { 367 | FGuard guard (lock); // scope for lock 368 | 369 | Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)]; 370 | Update::DependentMapIterConst iterList = map.find (unknown); 371 | if (iterList != map.end ()) 372 | { 373 | const Update::DependentList& dependentlist = (*iterList).second; 374 | Update::DependentListIterConst iterDependentlist = dependentlist.begin (); 375 | while (iterDependentlist != dependentlist.end ()) 376 | { 377 | #if CLASS_NAME_TRACKED 378 | dependents[count] = (*iterDependentlist).dep; 379 | #else 380 | dependents[count] = *iterDependentlist; 381 | #endif 382 | count++; 383 | 384 | if (count >= maxDependents) 385 | { 386 | if (dependents == smallDependents) 387 | { 388 | dependents = NEW IDependent*[Update::kMapSize]; 389 | memcpy (dependents, smallDependents, count * sizeof (dependents[0])); 390 | maxDependents = Update::kMapSize; 391 | } 392 | else 393 | { 394 | SMTG_WARNING ("Dependency overflow") 395 | break; 396 | } 397 | } 398 | ++iterDependentlist; 399 | } 400 | } 401 | 402 | // push update data on the stack 403 | if (count > 0) 404 | { 405 | Update::UpdateData data (unknown, dependents, count); 406 | table->updateData.push_back (data); 407 | } 408 | } // end scope 409 | 410 | int32 i = 0; 411 | while (i < count) 412 | { 413 | if (dependents[i]) 414 | dependents[i]->update (unknown, message); 415 | i++; 416 | } 417 | 418 | if (dependents != smallDependents) 419 | delete[] dependents; 420 | 421 | // remove update data from the stack 422 | if (count > 0) 423 | { 424 | FGuard guard (lock); 425 | 426 | table->updateData.pop_back (); 427 | } 428 | 429 | // send update done message 430 | if (suppressUpdateDone == false) 431 | Update::updateDone (unknown, message); 432 | 433 | return count > 0 ? kResultTrue : kResultFalse; // Object was found and has been updated 434 | } 435 | 436 | //------------------------------------------------------------------------ 437 | tresult PLUGIN_API UpdateHandler::triggerUpdates (FUnknown* u, int32 message) 438 | { 439 | return doTriggerUpdates (u, message, false); 440 | } 441 | 442 | //------------------------------------------------------------------------ 443 | tresult PLUGIN_API UpdateHandler::deferUpdates (FUnknown* u, int32 message) 444 | { 445 | IPtr unknown = Update::getUnknownBase (u); 446 | if (!unknown) 447 | return kResultFalse; 448 | 449 | FGuard guard (lock); 450 | 451 | Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)]; 452 | Update::DependentMapIterConst iterList = map.find (unknown); 453 | 454 | bool hasDependency = (iterList != map.end ()); 455 | if (hasDependency == false) 456 | { 457 | Update::updateDone (unknown, message); 458 | return kResultTrue; 459 | } 460 | 461 | bool found = false; 462 | Update::DeferedChangeListIterConst iter = table->defered.begin (); 463 | while (iter != table->defered.end ()) 464 | { 465 | if ((*iter).obj == unknown && (*iter).msg == message) 466 | { 467 | found = true; 468 | break; 469 | } 470 | ++iter; 471 | } 472 | 473 | if (!found) 474 | { 475 | Update::DeferedChange change (unknown, message); 476 | table->defered.push_back (change); 477 | } 478 | 479 | return kResultTrue; 480 | } 481 | 482 | //------------------------------------------------------------------------ 483 | tresult PLUGIN_API UpdateHandler::triggerDeferedUpdates (FUnknown* unknown) 484 | { 485 | Update::DeferedChangeList deferedAgain; 486 | if (!unknown) 487 | { 488 | while (table->defered.empty () == false) 489 | { 490 | // Remove first from queue 491 | lock.lock (); 492 | 493 | FUnknown* obj = table->defered.front ().obj; 494 | int32 msg = table->defered.front ().msg; 495 | table->defered.pop_front (); 496 | 497 | // check if this object is currently being updated. in this case, defer update again... 498 | bool canSignal = true; 499 | Update::UpdateDataListIterConst iter = table->updateData.begin (); 500 | while (iter != table->updateData.end ()) 501 | { 502 | if ((*iter).obj == obj) 503 | { 504 | canSignal = false; 505 | break; 506 | } 507 | ++iter; 508 | } 509 | lock.unlock (); 510 | 511 | if (canSignal) 512 | { 513 | triggerUpdates (obj, msg); 514 | } 515 | else 516 | { 517 | Update::DeferedChange change (obj, msg); 518 | deferedAgain.push_back (change); 519 | } 520 | } 521 | } 522 | else 523 | { 524 | IPtr object = Update::getUnknownBase (unknown); 525 | Update::DeferedChange tmp (object); 526 | 527 | while (true) 528 | { 529 | lock.lock (); 530 | Update::DeferedChangeListIter it = 531 | std::find (table->defered.begin (), table->defered.end (), tmp); 532 | if (it == table->defered.end ()) 533 | { 534 | lock.unlock (); 535 | return kResultTrue; 536 | } 537 | 538 | if ((*it).obj != nullptr) 539 | { 540 | int32 msg = (*it).msg; 541 | table->defered.erase (it); 542 | 543 | // check if this object is currently being updated. in this case, defer update 544 | // again... 545 | bool canSignal = true; 546 | Update::UpdateDataListIterConst iter = table->updateData.begin (); 547 | while (iter != table->updateData.end ()) 548 | { 549 | if ((*iter).obj == object) 550 | { 551 | canSignal = false; 552 | break; 553 | } 554 | ++iter; 555 | } 556 | lock.unlock (); 557 | 558 | if (canSignal) 559 | { 560 | triggerUpdates (object, msg); 561 | } 562 | else 563 | { 564 | Update::DeferedChange change (object, msg); 565 | deferedAgain.push_back (change); 566 | } 567 | } 568 | } 569 | } 570 | 571 | if (deferedAgain.empty () == false) 572 | { 573 | FGuard guard (lock); 574 | 575 | Update::DeferedChangeListIterConst iter = deferedAgain.begin (); 576 | while (iter != deferedAgain.end ()) 577 | { 578 | table->defered.push_back (*iter); 579 | ++iter; 580 | } 581 | } 582 | 583 | return kResultTrue; 584 | } 585 | 586 | //------------------------------------------------------------------------ 587 | tresult PLUGIN_API UpdateHandler::cancelUpdates (FUnknown* u) 588 | { 589 | IPtr unknown = Update::getUnknownBase (u); 590 | if (!unknown) 591 | return kResultFalse; 592 | 593 | FGuard guard (lock); 594 | 595 | Update::DeferedChange change (unknown, 0); 596 | while (true) 597 | { 598 | auto iter = std::find (table->defered.begin (), table->defered.end (), change); 599 | if (iter != table->defered.end ()) 600 | table->defered.erase (iter); 601 | else 602 | break; 603 | } 604 | 605 | return kResultTrue; 606 | } 607 | 608 | //------------------------------------------------------------------------ 609 | size_t UpdateHandler::countDependencies (FUnknown* object) 610 | { 611 | FGuard guard (lock); 612 | uint32 res = 0; 613 | 614 | IPtr unknown = Update::getUnknownBase (object); 615 | if (unknown) 616 | { 617 | Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)]; 618 | Update::DependentMapIter iterList = map.find (unknown); 619 | if (iterList != map.end ()) 620 | return iterList->second.size (); 621 | } 622 | else 623 | { 624 | for (uint32 j = 0; j < Update::kHashSize; j++) 625 | { 626 | Update::DependentMap& map = table->depMap[j]; 627 | res += countEntries (map); 628 | } 629 | } 630 | return res; 631 | } 632 | 633 | #if DEVELOPMENT 634 | //------------------------------------------------------------------------ 635 | bool UpdateHandler::checkDeferred (FUnknown* object) 636 | { 637 | IPtr unknown = Update::getUnknownBase (object); 638 | 639 | FGuard guard (lock); 640 | 641 | Update::DeferedChange tmp (unknown); 642 | Update::DeferedChangeListIterConst it = 643 | std::find (table->defered.begin (), table->defered.end (), tmp); 644 | if (it != table->defered.end ()) 645 | return true; 646 | 647 | return false; 648 | } 649 | 650 | //------------------------------------------------------------------------ 651 | bool UpdateHandler::hasDependencies (FUnknown* u) 652 | { 653 | IPtr unknown = Update::getUnknownBase (u); 654 | if (!unknown) 655 | return false; 656 | 657 | FGuard guard (lock); 658 | 659 | Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)]; 660 | Update::DependentMapIterConst iterList = map.find (unknown); 661 | bool hasDependency = (iterList != map.end ()); 662 | 663 | return hasDependency; 664 | } 665 | 666 | //------------------------------------------------------------------------ 667 | void UpdateHandler::printForObject (FObject* obj) const 668 | { 669 | IPtr unknown = Update::getUnknownBase (obj); 670 | if (!unknown) 671 | return; 672 | 673 | FUnknownPtr dep (obj); 674 | 675 | bool header = false; 676 | 677 | Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)]; 678 | Update::DependentMapIterConst iterList = map.begin (); 679 | while (iterList != map.end ()) 680 | { 681 | const Update::DependentList& dependentlist = (*iterList).second; 682 | Update::DependentListIterConst iterDependentlist = dependentlist.begin (); 683 | while (iterDependentlist != dependentlist.end ()) 684 | { 685 | #if CLASS_NAME_TRACKED 686 | if ((*iterList).first == unknown || (*iterDependentlist).dep == dep.getInterface ()) 687 | { 688 | if (!header) 689 | { 690 | FDebugPrint ("Dependencies for object %8" FORMAT_INT64A " %s\n", (uint64)obj, 691 | obj->isA ()); 692 | header = true; 693 | } 694 | FDebugPrint ("%s %8" FORMAT_INT64A "\n <- %s %8" FORMAT_INT64A "\n", 695 | (*iterDependentlist).depClass, (uint64) (*iterDependentlist).dep, 696 | (*iterDependentlist).objClass, (uint64) ((*iterList).first)); 697 | } 698 | #else 699 | if ((*iterList).first == unknown || (*iterDependentlist) == dep.getInterface ()) 700 | { 701 | if (!header) 702 | { 703 | FDebugPrint ("Dependencies for object %8" FORMAT_INT64A " %s\n", (uint64)obj, 704 | obj->isA ()); 705 | header = true; 706 | } 707 | FDebugPrint ("%8" FORMAT_INT64A "\n <- %8" FORMAT_INT64A "\n", 708 | (uint64) (*iterDependentlist), (uint64) ((*iterList).first)); 709 | } 710 | #endif 711 | ++iterDependentlist; 712 | } 713 | 714 | ++iterList; 715 | } 716 | } 717 | #endif 718 | 719 | //------------------------------------------------------------------------ 720 | } // namespace Steinberg 721 | --------------------------------------------------------------------------------