Utilisateur:VST3Programme/Brouillon

Une page de Wikipédia, l'encyclopédie libre.
//------------------------------------------------------------------------
// Project     : SDK Base
// Version     : 1.0
//
// Category    : Helpers
// Filename    : base/source/baseidds.cpp
// Created by  : Steinberg, 01/2008
// Description : Basic Interface
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
// 
//   * Redistributions of source code must retain the above copyright notice, 
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation 
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this 
//     software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#include "pluginterfaces/base/funknown.h"
#include "pluginterfaces/base/istringresult.h"
#include "pluginterfaces/base/ipersistent.h"


namespace Steinberg {

DEF_CLASS_IID (IString)
DEF_CLASS_IID (IStringResult)

DEF_CLASS_IID (IPersistent)
DEF_CLASS_IID (IAttributes)
DEF_CLASS_IID (IAttributes2)
//------------------------------------------------------------------------
} // namespace Steinberg
//------------------------------------------------------------------------
// Project     : SDK Base
// Version     : 1.0
//
// Category    : Helpers
// Filename    : base/source/fbuffer.cpp
// Created by  : Steinberg, 2008
// Description : 
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
// 
//   * Redistributions of source code must retain the above copyright notice, 
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation 
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this 
//     software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#include "base/source/fbuffer.h"
#include "base/source/fstring.h"
#include <cstdlib>

namespace Steinberg {

//-------------------------------------------------------------------------------------
Buffer::Buffer () 
: buffer (nullptr)
, memSize (0)
, fillSize (0)
, delta (defaultDelta)
{}

//-------------------------------------------------------------------------------------
Buffer::Buffer (uint32 s, uint8 initVal)
: buffer (nullptr)
, memSize (s)
, fillSize (0)
, delta (defaultDelta)
{
 	if (memSize == 0)
		return;		
	buffer = (int8*)::malloc (memSize);
	if (buffer)
		memset (buffer, initVal, memSize);
	else
		memSize = 0;
}

//-------------------------------------------------------------------------------------
Buffer::Buffer (uint32 s)
: buffer (nullptr)
, memSize (s)
, fillSize (0)
, delta (defaultDelta)
{
 	if (memSize == 0)
		return;		
 	buffer = (int8*)::malloc (memSize);
	if (!buffer)
		memSize = 0;
}

//-------------------------------------------------------------------------------------
Buffer::Buffer (const void* b , uint32 s) 
: buffer (nullptr)
, memSize (s)
, fillSize (s)
, delta (defaultDelta)
{
 	if (memSize == 0)
		return;
 	buffer = (int8*)::malloc (memSize);
 	if (buffer)
		memcpy (buffer, b, memSize);
	else
	{
		memSize = 0;
		fillSize = 0;
	}
}

//-------------------------------------------------------------------------------------
Buffer::Buffer (const Buffer& bufferR)
: buffer (nullptr)
, memSize (bufferR.memSize)
, fillSize (bufferR.fillSize)
, delta (bufferR.delta)
{
 	if (memSize == 0)
		return;

 	buffer = (int8*)::malloc (memSize);
 	if (buffer)
		memcpy (buffer, bufferR.buffer, memSize);
	else
		memSize = 0;
}

//-------------------------------------------------------------------------------------
Buffer::~Buffer ()
{
 	if (buffer)
		::free (buffer);
 	buffer = nullptr;
}

//-------------------------------------------------------------------------------------
void Buffer::operator = (const Buffer& b2)
{
 	if (&b2 != this) 
	{
		setSize (b2.memSize);		
		if (b2.memSize > 0 && buffer)
			memcpy (buffer, b2.buffer, b2.memSize);
		fillSize = b2.fillSize;
		delta = b2.delta;
 	}
}

//-------------------------------------------------------------------------------------
bool Buffer::operator == (const Buffer& b2)const
{
	if (&b2 == this)
		return true;
	if (b2.getSize () != getSize ())
		return false;
	return memcmp (this->int8Ptr (), b2.int8Ptr (), getSize ()) == 0 ? true : false;		
}

//-------------------------------------------------------------------------------------
uint32 Buffer::get (void* b, uint32 size)
{
	uint32 maxGet = memSize - fillSize;
	if (size > maxGet)
		size = maxGet;
	if (size > 0)
		memcpy (b, buffer + fillSize, size);
	fillSize += size;
	return size;
}

//-------------------------------------------------------------------------------------
bool Buffer::put (char16 c)
{
	return put ((const void*)&c, sizeof (c));
}

//-------------------------------------------------------------------------------------
bool Buffer::put (uint8 byte)
{
	if (grow (fillSize + 1) == false)
		return false;
 	
  	buffer [fillSize++] = byte;
  	return true;
}

//-------------------------------------------------------------------------------------
bool Buffer::put (char c)
{
	if (grow (fillSize + 1) == false)
		return false;
 	
  	buffer [fillSize++] = c;
  	return true;
}

//-------------------------------------------------------------------------------------
bool Buffer::put (const void* toPut, uint32 s)
{
	if (!toPut)
		return false;

	if (grow (fillSize + s) == false)
		return false;

	memcpy (buffer + fillSize, toPut, s);
	fillSize += s;
	return true;
}

//-------------------------------------------------------------------------------------
bool Buffer::put (const String& str)
{ 
	return put ((const void*)str.text () , (str.length () + 1) * sizeof (tchar)); 
}

//-------------------------------------------------------------------------------------
bool Buffer::appendString8 (const char8* s)
{
	if (!s)
		return false;

	uint32 len = (uint32) strlen (s);
	return put (s, len);
}

//-------------------------------------------------------------------------------------
bool Buffer::appendString16 (const char16* s)
{
	if (!s)
		return false;
	ConstString str (s);
	uint32 len = (uint32) str.length () * sizeof (char16);
	return put (s, len);
}

//-------------------------------------------------------------------------------------
bool Buffer::prependString8 (const char8* s)
{
	if (!s)
		return false;

	uint32 len = (uint32) strlen (s);
	if (len > 0)
	{
		shiftStart (len);
		memcpy (buffer, s, len);
		return true;
	}
	return false;
}

//-------------------------------------------------------------------------------------
bool Buffer::prependString16 (const char16* s)
{
	if (!s)
		return false;

	ConstString str (s);
	uint32 len = (uint32) str.length () * sizeof (char16);
	
	if (len > 0)
	{
		shiftStart (len);
		memcpy (buffer, s, len);
		return true;
	}
	return false;
}

//-------------------------------------------------------------------------------------
bool Buffer::prependString8 (char8 c)
{
	shiftStart (sizeof (char));
	char* b = (char*)buffer;
	b [0] = c;
	return true;
}

//-------------------------------------------------------------------------------------
bool Buffer::prependString16 (char16 c)
{
	shiftStart (sizeof (char16));
	char16* b = (char16*)buffer;
	b [0] = c;
	return true;
}

//-------------------------------------------------------------------------------------
bool Buffer::copy (uint32 from, uint32 to, uint32 bytes)
{
	if (from + bytes > memSize || bytes == 0)
		return false;
	
	if (to + bytes > memSize)
		setSize (to + bytes);
	
	if (from + bytes > to && from < to)
	{              // overlap
		Buffer tmp (buffer + from, bytes);
		memcpy (buffer + to, tmp, bytes);
	}
	else	
		memcpy (buffer + to, buffer + from, bytes);
	return true;
}

//-------------------------------------------------------------------------------------
bool Buffer::makeHexString (String& result)
{
	unsigned char* data = uint8Ptr ();
	uint32 bytes = getSize ();

	if (data == nullptr || bytes == 0)
		return false;

	char8* stringBuffer = (char8*)malloc ((bytes * 2) + 1);
	if (!stringBuffer)
		return false;

	int32 count = 0;
	while (bytes > 0)
	{
		unsigned char t1 = ((*data) >> 4) & 0x0F;
		unsigned char t2 = (*data) & 0x0F;
		if (t1 < 10)
			t1 += '0';
		else
			t1 = t1 - 10 + 'A';
		if (t2 < 10)
			t2 += '0';
		else
			t2 = t2 - 10 + 'A';
		
		stringBuffer [count++] = t1;
		stringBuffer [count++] = t2;
		data++;
		bytes--;
	}
	stringBuffer [count] = 0;

	result.take ((void*)stringBuffer, false);
	return true;
}

//-------------------------------------------------------------------------------------
bool Buffer::fromHexString (const char8* string)
{
	flush ();
	if (string == nullptr)
		return false;

	int32 len = strlen8 (string);
	if (len == 0 || ((len & 1) == 1)/*odd number*/ )
		return false;

	setSize (len / 2);
	unsigned char* data = uint8Ptr ();

	bool upper = true;
	int32 count = 0;
	while (count < len)
	{
		char c = string [count];

		unsigned char d = 0;
		if (c >= '0' && c <= '9')		d += c - '0';
		else if (c >= 'A' && c <= 'F')	d += c - 'A' + 10;
		else if (c >= 'a' && c <= 'f')	d += c - 'a' + 10;
		else return false; // no hex string

		if (upper)
			data [count >> 1] = static_cast<unsigned char> (d << 4);
		else
			data [count >> 1] += d;

		upper = !upper;
		count++;
	}
	setFillSize (len / 2);
	return true;
}

//------------------------------------------------------------------------
void Buffer::set (uint8 value)
{
	if (buffer)
		memset (buffer, value, memSize);
}

//-------------------------------------------------------------------------------------
bool Buffer::setFillSize (uint32 c)
{
	if (c <= memSize)
	{
		fillSize = c;
		return true;	
	}
	return false;
}

//-------------------------------------------------------------------------------------
bool Buffer::truncateToFillSize ()
{
	if (fillSize < memSize)
		setSize (fillSize);
	
	return true;
}

//-------------------------------------------------------------------------------------
bool Buffer::grow (uint32 newSize)
{
	if (newSize > memSize)
	{
		if (delta == 0)
			delta = defaultDelta;
		uint32 s = ((newSize + delta - 1) / delta) * delta;
		return setSize (s);
	}
	return true;
}

//------------------------------------------------------------------------
void Buffer::shiftAt (uint32 position, int32 amount)
{
	if (amount > 0)
	{
		if (grow (fillSize + amount))
		{
			if (position < fillSize)
				memmove (buffer + amount + position, buffer + position, fillSize - position);
			
			fillSize += amount;
		}
	}	
	else if (amount < 0 && fillSize > 0)
	{
		uint32 toRemove = -amount;
	
		if (toRemove < fillSize)
		{
			if (position < fillSize)
				memmove (buffer + position, buffer + toRemove + position, fillSize - position - toRemove);
			fillSize -= toRemove;
		}
	}
}

//-------------------------------------------------------------------------------------
void Buffer::move (int32 amount, uint8 initVal)
{
	if (memSize == 0)
		return;

	if (amount > 0)
	{
		if ((uint32)amount < memSize)
		{
			memmove (buffer + amount, buffer, memSize - amount);
			memset (buffer, initVal, amount);
		}
		else
			memset (buffer, initVal, memSize);
	}
	else
	{	
		uint32 toRemove = -amount;
		if (toRemove < memSize)
		{
			memmove (buffer, buffer + toRemove, memSize - toRemove);
			memset (buffer + memSize - toRemove, initVal, toRemove);	
		}
		else
			memset (buffer, initVal, memSize);	
	}
}

//-------------------------------------------------------------------------------------
bool Buffer::setSize (uint32 newSize)
{
	if (memSize != newSize)
	{
 		if (buffer)
		{
			if (newSize > 0)
			{
				int8* newBuffer = (int8*) ::realloc (buffer, newSize);
				if (newBuffer == nullptr)
				{
					newBuffer = (int8*)::malloc (newSize);
					if (newBuffer)
					{
						uint32 tmp = newSize;
						if (tmp > memSize)
							tmp = memSize;
						memcpy (newBuffer, buffer, tmp);
						::free (buffer);
						buffer = newBuffer;
					}
					else
					{
						::free (buffer);
						buffer = nullptr;
					}
				}
				else
					buffer = newBuffer;
			}
			else
			{
				::free (buffer);
				buffer = nullptr;
			}
		}
		else
			buffer = (int8*)::malloc (newSize);

		if (newSize > 0 && !buffer)
			memSize = 0;
		else
			memSize = newSize;
		if (fillSize > memSize)
			fillSize = memSize;
	}

	return (newSize > 0) == (buffer != nullptr);
}

//-------------------------------------------------------------------------------------
void Buffer::fillup (uint8 value)
{
	if (getFree () > 0)
		memset (buffer + fillSize, value, getFree ());
}

//-------------------------------------------------------------------------------------
int8* Buffer::operator + (uint32 i)
{
	if (i < memSize)
		return buffer + i;
	
	static int8 eof;
	eof = 0;
	return &eof;
}

//-------------------------------------------------------------------------------------
bool Buffer::swap (int16 swapSize)
{
	return swap (buffer, memSize, swapSize);
}

//-------------------------------------------------------------------------------------
bool Buffer::swap (void* buffer, uint32 bufferSize, int16 swapSize)
{
	if (swapSize != kSwap16 && swapSize != kSwap32 && swapSize != kSwap64)
		return false;
	
	if (swapSize == kSwap16)
	{
		for (uint32 count = 0 ; count < bufferSize ; count += 2)
		{
			SWAP_16 ( * (((int16*)buffer) + count) );
		}
	}
	else if (swapSize == kSwap32)
	{
		for (uint32 count = 0 ; count < bufferSize ; count += 4) 
		{
			SWAP_32 ( * (((int32*)buffer) + count) );
		}
	}
	else if (swapSize == kSwap64)
	{
		for (uint32 count = 0 ; count < bufferSize ; count += 8) 
		{
			SWAP_64 ( * (((int64*)buffer) + count) );
		}
	}

	return true;
}

//-------------------------------------------------------------------------------------
void Buffer::take (Buffer& from)
{
	setSize (0);
	memSize = from.memSize;
	fillSize = from.fillSize;	
	buffer = from.buffer;
	from.buffer = nullptr;
	from.memSize = 0;
	from.fillSize = 0;
}

//-------------------------------------------------------------------------------------
int8* Buffer::pass ()
{
	int8* res = buffer;
	buffer = nullptr;
	memSize = 0;
	fillSize = 0;
	return res;
}

//-------------------------------------------------------------------------------------
bool Buffer::toWideString (int32 sourceCodePage)
{
	if (getFillSize () > 0)
	{
		if (str8 () [getFillSize () - 1] != 0) // multiByteToWideString only works with 0-terminated strings
			endString8 ();

		Buffer dest (getFillSize () * sizeof (char16));
		int32 result = String::multiByteToWideString (dest.str16 (), str8 (), dest.getFree () / sizeof (char16), sourceCodePage);
		if (result > 0)
		{
			dest.setFillSize ((result - 1) * sizeof (char16));
			take (dest);
			return true;
		}
		return false;
	}
	return true;
}

//-------------------------------------------------------------------------------------
bool Buffer::toMultibyteString (int32 destCodePage)
{
	if (getFillSize () > 0)
	{
		int32 textLength = getFillSize () / sizeof (char16); // wideStringToMultiByte only works with 0-terminated strings
		if (str16 () [textLength - 1] != 0)
			endString16 ();

		Buffer dest (getFillSize ());
		int32 result = String::wideStringToMultiByte (dest.str8 (), str16 (), dest.getFree (), destCodePage);
		if (result > 0)
		{
			dest.setFillSize (result - 1);
			take (dest);
			return true;
		}
		return false;
	}
	return true;
}

//------------------------------------------------------------------------
} // namespace Steinberg
//------------------------------------------------------------------------
// Project     : SDK Base
// Version     : 1.0
//
// Category    : Helpers
// Filename    : base/source/fdebug.cpp
// Created by  : Steinberg, 1995
// Description : There are 2 levels of debugging messages:
//	             DEVELOPMENT               During development
//	             RELEASE                   Program is shipping.
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this
//     software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#include "base/source/fdebug.h"

#if SMTG_OS_WINDOWS
#include <windows.h>

bool AmIBeingDebugged ()
{
	return IsDebuggerPresent ();
}
#endif

#if SMTG_OS_LINUX
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
//--------------------------------------------------------------------------
bool AmIBeingDebugged ()
{
	// TODO: check if GDB or LLDB is attached
	return true;
}
#endif

#if SMTG_OS_MACOS

#include <stdbool.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <unistd.h>

//------------------------------------------------------------------------
// from Technical Q&A QA1361 (http://developer.apple.com/qa/qa2004/qa1361.html)
//------------------------------------------------------------------------
bool AmIBeingDebugged ()
// Returns true if the current process is being debugged (either
// running under the debugger or has a debugger attached post facto).
{
	int mib[4];
	struct kinfo_proc info;
	size_t size;

	// Initialize the flags so that, if sysctl fails for some bizarre
	// reason, we get a predictable result.

	info.kp_proc.p_flag = 0;

	// Initialize mib, which tells sysctl the info we want, in this case
	// we're looking for information about a specific process ID.

	mib[0] = CTL_KERN;
	mib[1] = KERN_PROC;
	mib[2] = KERN_PROC_PID;
	mib[3] = getpid ();

	// Call sysctl.

	size = sizeof (info);
	sysctl (mib, sizeof (mib) / sizeof (*mib), &info, &size, NULL, 0);

	// We're being debugged if the P_TRACED flag is set.
	return ((info.kp_proc.p_flag & P_TRACED) != 0);
}

#endif // SMTG_OS_MACOS

#if DEVELOPMENT

#include <cassert>
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
#include <mutex>

#if SMTG_OS_WINDOWS
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0400
#endif
#if _MSC_VER
#include <intrin.h>
#endif
#define vsnprintf _vsnprintf
#define snprintf _snprintf

#elif SMTG_OS_MACOS
#include <errno.h>
#include <mach/mach_init.h>
#include <mach/mach_time.h>
#include <new>
#include <signal.h>

#define THREAD_ALLOC_WATCH 0 // check allocations on specific threads

#if THREAD_ALLOC_WATCH
mach_port_t watchThreadID = 0;
#endif

#endif

AssertionHandler gAssertionHandler = nullptr;
AssertionHandler gPreAssertionHook = nullptr;
DebugPrintLogger gDebugPrintLogger = nullptr;

//--------------------------------------------------------------------------
static const int kDebugPrintfBufferSize = 10000;
static bool neverDebugger = false; // so I can switch it off in the debugger...

static std::once_flag neverDebuggerEnvCheckFlag {};

//--------------------------------------------------------------------------
static void initNeverDebugger ()
{
	std::call_once (neverDebuggerEnvCheckFlag, [] () {
		// add this environment variable to not stop in the debugger on ASSERT
		if (std::getenv ("SMTG_DEBUG_IGNORE_ASSERT"))
		{
			neverDebugger = true;
		}
	});
}

//--------------------------------------------------------------------------
static void printDebugString (const char* string)
{
	if (!string)
		return;

	if (gDebugPrintLogger)
	{
		gDebugPrintLogger (string);
	}
	else
	{
#if SMTG_OS_MACOS || defined(__MINGW32__)
		fprintf (stderr, "%s", string);
#elif SMTG_OS_WINDOWS
		OutputDebugStringA (string);
#endif
	}
}

//--------------------------------------------------------------------------
//	printf style debugging output
//--------------------------------------------------------------------------
void FDebugPrint (const char* format, ...)
{
	char string[kDebugPrintfBufferSize];
	va_list marker;
	va_start (marker, format);
	vsnprintf (string, kDebugPrintfBufferSize, format, marker);

	printDebugString (string);
}

//--------------------------------------------------------------------------
//	printf style debugging output
//--------------------------------------------------------------------------
void FDebugBreak (const char* format, ...)
{
	char string[kDebugPrintfBufferSize];
	va_list marker;
	va_start (marker, format);
	vsnprintf (string, kDebugPrintfBufferSize, format, marker);

	printDebugString (string);

	// The Pre-assertion hook is always called, even if we're not running in the debugger,
	// so that we can log asserts without displaying them
	if (gPreAssertionHook)
	{
		gPreAssertionHook (string);
	}

	initNeverDebugger ();
	if (neverDebugger)
		return;
	if (AmIBeingDebugged ())
	{
		// do not crash if no debugger present
		// If there  is an assertion handler defined then let this override the UI
		// and tell us whether we want to break into the debugger
		bool breakIntoDebugger = true;
		if (gAssertionHandler && gAssertionHandler (string) == false)
		{
			breakIntoDebugger = false;
		}

		if (breakIntoDebugger)
		{
#if SMTG_OS_WINDOWS	&& _MSC_VER
			__debugbreak (); // intrinsic version of DebugBreak()
#elif SMTG_OS_MACOS && __arm64__
			raise (SIGSTOP);

#elif __ppc64__ || __ppc__ || __arm__
			kill (getpid (), SIGINT);
#elif __i386__ || __x86_64__
			{
				__asm__ volatile ("int3");
			}
#endif
		}
	}
}

//--------------------------------------------------------------------------
void FPrintLastError (const char* file, int line)
{
#if SMTG_OS_WINDOWS
	LPVOID lpMessageBuffer = nullptr;
	FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, nullptr,
	                GetLastError (), MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
	                (LPSTR)&lpMessageBuffer, 0, nullptr);
	FDebugPrint ("%s(%d) : %s\n", file, line, lpMessageBuffer);
	LocalFree (lpMessageBuffer);
#endif

#if SMTG_OS_MACOS
#if !__MACH__
	extern int errno;
#endif
	FDebugPrint ("%s(%d) : Errno %d\n", file, line, errno);
#endif
}

#if SMTG_OS_MACOS

//------------------------------------------------------------------------
void* operator new (size_t size, int, const char* file, int line)
{
#if THREAD_ALLOC_WATCH
	mach_port_t threadID = mach_thread_self ();
	if (watchThreadID == threadID)
	{
		FDebugPrint ("Watched Thread Allocation : %s (Line:%d)\n", file ? file : "Unknown", line);
	}
#endif
	try
	{
		return ::operator new (size);
	}
	catch (std::bad_alloc exception)
	{
		FDebugPrint ("bad_alloc exception : %s (Line:%d)", file ? file : "Unknown", line);
	}
	return (void*)-1;
}

//------------------------------------------------------------------------
void* operator new[] (size_t size, int, const char* file, int line)
{
#if THREAD_ALLOC_WATCH
	mach_port_t threadID = mach_thread_self ();
	if (watchThreadID == threadID)
	{
		FDebugPrint ("Watched Thread Allocation : %s (Line:%d)\n", file ? file : "Unknown", line);
	}
#endif
	try
	{
		return ::operator new[] (size);
	}
	catch (std::bad_alloc exception)
	{
		FDebugPrint ("bad_alloc exception : %s (Line:%d)", file ? file : "Unknown", line);
	}
	return (void*)-1;
}

//------------------------------------------------------------------------
void operator delete (void* p, int, const char* file, int line)
{
	(void)file;
	(void)line;
	::operator delete (p);
}

//------------------------------------------------------------------------
void operator delete[] (void* p, int, const char* file, int line)
{
	(void)file;
	(void)line;
	::operator delete[] (p);
}

#endif // SMTG_OS_MACOS

#endif // DEVELOPMENT

static bool smtg_unit_testing_active = false; // ugly hack to unit testing ...

//------------------------------------------------------------------------
bool isSmtgUnitTesting ()
{
	return smtg_unit_testing_active;
}

//------------------------------------------------------------------------
void setSmtgUnitTesting ()
{
	smtg_unit_testing_active = true;
}
//------------------------------------------------------------------------
// Project     : SDK Base
// Version     : 1.0
//
// Category    : Helpers
// Filename    : base/source/fdynlib.cpp
// Created by  : Steinberg, 1998
// Description : Dynamic library loading
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
// 
//   * Redistributions of source code must retain the above copyright notice, 
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation 
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this 
//     software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#include "base/source/fdynlib.h"
#include "pluginterfaces/base/fstrdefs.h"
#include "base/source/fstring.h"

#if SMTG_OS_WINDOWS
#include <windows.h>

#elif SMTG_OS_MACOS
#include <mach-o/dyld.h>
#include <CoreFoundation/CoreFoundation.h>

#if !SMTG_OS_IOS
static const Steinberg::tchar kUnixDelimiter = STR ('/');
#endif
#endif

namespace Steinberg {

#if SMTG_OS_MACOS
#include <dlfcn.h>

// we ignore for the moment that the NSAddImage functions are deprecated
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"

static bool CopyProcessPath (Steinberg::String& name)
{
	Dl_info info;
	if (dladdr ((const void*)CopyProcessPath, &info))
	{
		if (info.dli_fname)
		{
			name.assign (info.dli_fname);
			#ifdef UNICODE
			name.toWideString ();
			#endif
			return true;
		}
	}
	return false;
}

#endif

//------------------------------------------------------------------------
// FDynLibrary
//------------------------------------------------------------------------
FDynLibrary::FDynLibrary (const tchar* n, bool addExtension)
: isloaded (false)
, instance (nullptr)
{
	if (n)
		init (n, addExtension);
}

//------------------------------------------------------------------------
FDynLibrary::~FDynLibrary ()
{
	unload ();
}

//------------------------------------------------------------------------
bool FDynLibrary::init (const tchar* n, bool addExtension)
{
	if (isLoaded ())
		return true;

	Steinberg::String name (n);
		
#if SMTG_OS_WINDOWS
	if (addExtension)
		name.append (STR (".dll"));

	instance = LoadLibrary (name);
	if (instance)
		isloaded = true;
		
#elif SMTG_OS_MACOS
	isBundle = false;
	// first check if it is a bundle
	if (addExtension)
		name.append (STR (".bundle"));
	if (name.getChar16 (0) != STR('/')) // no absoltue path
	{
		Steinberg::String p;
		if (CopyProcessPath (p))
		{
			Steinberg::int32 index = p.findLast (STR ('/'));
			p.remove (index+1);
			name = p + name;
		}
	}
	CFStringRef fsString = (CFStringRef)name.toCFStringRef ();
	CFURLRef url = CFURLCreateWithFileSystemPath (NULL, fsString, kCFURLPOSIXPathStyle, true);
	if (url)
	{
		CFBundleRef bundle = CFBundleCreate (NULL, url);
		if (bundle)
		{
			if (CFBundleLoadExecutable (bundle))
			{
				isBundle = true;
				instance = (void*)bundle;
			}
			else
				CFRelease (bundle);
		}
		CFRelease (url);
	}
	CFRelease (fsString);

	name.assign (n);

#if !SMTG_OS_IOS
	if (!isBundle)
	{
		// now we check for a dynamic library
		firstSymbol = NULL;
		if (addExtension)
		{
			name.append (STR (".dylib"));
		}
		// Only if name is a relative path we use the Process Path as root:
		if (name[0] != kUnixDelimiter)
		{
			Steinberg::String p;
			if (CopyProcessPath (p))
			{
				Steinberg::int32 index = p.findLast (STR ("/"));
				p.remove (index+1);
				p.append (name);
				p.toMultiByte (Steinberg::kCP_Utf8);
				instance = (void*) NSAddImage (p, NSADDIMAGE_OPTION_RETURN_ON_ERROR);
			}
		}
		// Last but not least let the system search for it
		// 
		if (instance == 0)
		{
			name.toMultiByte (Steinberg::kCP_Utf8);
			instance = (void*) NSAddImage (name, NSADDIMAGE_OPTION_RETURN_ON_ERROR);
		}
	}
#endif // !SMTG_OS_IOS

	if (instance)
		isloaded = true;

#endif

	return isloaded;
}

//------------------------------------------------------------------------
bool FDynLibrary::unload ()
{
	if (!isLoaded ())
		return false;

#if SMTG_OS_WINDOWS
	FreeLibrary ((HINSTANCE)instance);
	
#elif SMTG_OS_MACOS
	if (isBundle)
	{
		if (CFGetRetainCount ((CFTypeRef)instance) == 1)
			CFBundleUnloadExecutable ((CFBundleRef)instance);
		CFRelease ((CFBundleRef)instance);
	}
	else
	{
		// we don't use this anymore as the darwin dyld can't unload dynamic libraries yet and may crash
/*		if (firstSymbol)
		{
			NSModule module = NSModuleForSymbol ((NSSymbol)firstSymbol);
			if (module)
				NSUnLinkModule (module, NSUNLINKMODULE_OPTION_NONE);
		}*/
	}
#endif
	instance = nullptr;
	isloaded = false;
	return true;
}

//------------------------------------------------------------------------
void* FDynLibrary::getProcAddress (const char* name)
{
	if (!isloaded)
		return nullptr;
	
#if SMTG_OS_WINDOWS
	return (void*)GetProcAddress ((HINSTANCE)instance, name);
	
#elif SMTG_OS_MACOS
	if (isBundle)
	{
		CFStringRef functionName = CFStringCreateWithCString (NULL, name, kCFStringEncodingASCII);
		void* result = CFBundleGetFunctionPointerForName ((CFBundleRef)instance, functionName);
		CFRelease (functionName);
		return result;
	}
#if !SMTG_OS_IOS
	else
	{
		char* symbolName = (char*) malloc (strlen (name) + 2);
		strcpy (symbolName, "_");
		strcat (symbolName, name);
		NSSymbol symbol;
		symbol = NSLookupSymbolInImage ((const struct mach_header*)instance, symbolName, NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_NOW | NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
		free (symbolName);
		if (symbol)
		{
			if (firstSymbol == NULL)
				firstSymbol = symbol;
			return NSAddressOfSymbol (symbol);
		}
	}
#endif // !SMTG_OS_IOS

	return nullptr;
#else
	return nullptr;
#endif
}

//------------------------------------------------------------------------
} // namespace Steinberg
//------------------------------------------------------------------------
// Flags       : clang-format SMTGSequencer
// Project     : SDK Base
// Version     : 1.0
//
// Category    : Helpers
// Filename    : base/source/fobject.cpp
// Created by  : Steinberg, 2008
// Description : Basic Object implementing FUnknown
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this
//     software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#include "base/source/fobject.h"
#include "base/thread/include/flock.h"

#include <vector>
#define SMTG_VALIDATE_DEPENDENCY_COUNT DEVELOPMENT // validating dependencyCount

#if SMTG_DEPENDENCY_COUNT
#include "base/source/updatehandler.h"
#define SMTG_DEPENDENCY_CHECK_LEVEL 1 // 1 => minimal assert, 2 => full assert
#endif // SMTG_DEPENDENCY_COUNT

namespace Steinberg {

IUpdateHandler* FObject::gUpdateHandler = nullptr;

//------------------------------------------------------------------------
const FUID FObject::iid;

//------------------------------------------------------------------------
struct FObjectIIDInitializer
{
	// the object iid is always generated so that different components
	// only can cast to their own objects
	// this initializer must be after the definition of FObject::iid, otherwise
	//  the default constructor of FUID will clear the generated iid
	FObjectIIDInitializer () { const_cast<FUID&> (FObject::iid).generate (); }
} gFObjectIidInitializer;

//------------------------------------------------------------------------
FObject::~FObject ()
{
#if SMTG_DEPENDENCY_COUNT && DEVELOPMENT
	static bool localNeverDebugger = false;
#endif

#if DEVELOPMENT
	if (refCount > 1)
		FDebugPrint ("Refcount is %d when trying to delete %s\n", refCount, isA ());
#endif

#if SMTG_DEPENDENCY_COUNT
#if SMTG_DEPENDENCY_CHECK_LEVEL >= 1
	if (gUpdateHandler)
	{
#if DEVELOPMENT
		SMTG_ASSERT (dependencyCount == 0 || localNeverDebugger);
#endif // DEVELOPMENT
	}
#endif
#endif // SMTG_DEPENDENCY_COUNT

#if SMTG_VALIDATE_DEPENDENCY_COUNT
	if (!gUpdateHandler || gUpdateHandler != UpdateHandler::instance (false))
		return;

	auto updateHandler = UpdateHandler::instance ();
	if (!updateHandler || updateHandler == this)
		return;

	SMTG_ASSERT ((updateHandler->checkDeferred (this) == false || localNeverDebugger) &&
	             "'this' has scheduled a deferUpdate that was not yet delivered");

	if (updateHandler->hasDependencies (this))
	{
		SMTG_ASSERT (
		    (false || localNeverDebugger) &&
		    "Another object is still dependent on 'this'. This leads to zombie entries in the dependency map that can later crash.");
		FDebugPrint ("Object still has dependencies %x %s\n", this, this->isA ());
		updateHandler->printForObject (this);
	}
#endif // SMTG_VALIDATE_DEPENDENCY_COUNT
}

//------------------------------------------------------------------------
uint32 PLUGIN_API FObject::addRef ()
{
	return FUnknownPrivate::atomicAdd (refCount, 1);
}

//------------------------------------------------------------------------
uint32 PLUGIN_API FObject::release ()
{
	if (FUnknownPrivate::atomicAdd (refCount, -1) == 0)
	{
		refCount = -1000;
		delete this;
		return 0;
	}
	return refCount;
}

//------------------------------------------------------------------------
tresult PLUGIN_API FObject::queryInterface (const TUID _iid, void** obj)
{
	QUERY_INTERFACE (_iid, obj, FUnknown::iid, FUnknown)
	QUERY_INTERFACE (_iid, obj, IDependent::iid, IDependent)
	QUERY_INTERFACE (_iid, obj, FObject::iid, FObject)
	*obj = nullptr;
	return kNoInterface;
}

//------------------------------------------------------------------------
void FObject::addDependent (IDependent* dep)
{
	if (!gUpdateHandler)
		return;

	gUpdateHandler->addDependent (unknownCast (), dep);
#if SMTG_DEPENDENCY_COUNT
	dependencyCount++;
#endif
}

//------------------------------------------------------------------------
void FObject::removeDependent (IDependent* dep)
{
#if SMTG_DEPENDENCY_COUNT && DEVELOPMENT
	static bool localNeverDebugger = false;
#endif

	if (!gUpdateHandler)
		return;

#if SMTG_DEPENDENCY_COUNT
	if (gUpdateHandler != UpdateHandler::instance (false))
	{
		gUpdateHandler->removeDependent (unknownCast (), dep);
		dependencyCount--;
		return;
	}
#if SMTG_DEPENDENCY_CHECK_LEVEL > 1
	SMTG_ASSERT ((dependencyCount > 0 || localNeverDebugger) &&
	             "All dependencies have already been removed - mmichaelis 7/2021");
#endif
	size_t removeCount;
	UpdateHandler::instance ()->removeDependent (unknownCast (), dep, removeCount);
	if (removeCount == 0)
	{
#if SMTG_DEPENDENCY_CHECK_LEVEL > 1
		SMTG_ASSERT (localNeverDebugger && "No dependency to remove - ygrabit 8/2021");
#endif
	}
	else
	{
		SMTG_ASSERT ((removeCount == 1 || localNeverDebugger) &&
		             "Duplicated dependencies established - mmichaelis 7/2021");
	}
	dependencyCount -= (int16)removeCount;
#else
	gUpdateHandler->removeDependent (unknownCast (), dep);
#endif // SMTG_DEPENDENCY_COUNT
}

//------------------------------------------------------------------------
void FObject::changed (int32 msg)
{
	if (gUpdateHandler)
		gUpdateHandler->triggerUpdates (unknownCast (), msg);
	else
		updateDone (msg);
}

//------------------------------------------------------------------------
void FObject::deferUpdate (int32 msg)
{
	if (gUpdateHandler)
		gUpdateHandler->deferUpdates (unknownCast (), msg);
	else
		updateDone (msg);
}

//------------------------------------------------------------------------
/** Automatic creation and destruction of singleton instances. */
//------------------------------------------------------------------------
namespace Singleton {
using ObjectVector = std::vector<FObject**>;
ObjectVector* singletonInstances = nullptr;
bool singletonsTerminated = false;
Steinberg::Base::Thread::FLock* singletonsLock;

bool isTerminated ()
{
	return singletonsTerminated;
}

void lockRegister ()
{
	if (!singletonsLock) // assume first call not from multiple threads
		singletonsLock = NEW Steinberg::Base::Thread::FLock;
	singletonsLock->lock ();
}

void unlockRegister ()
{
	singletonsLock->unlock ();
}

void registerInstance (FObject** o)
{
	SMTG_ASSERT (singletonsTerminated == false)
	if (singletonsTerminated == false)
	{
		if (singletonInstances == nullptr)
			singletonInstances = NEW std::vector<FObject**>;
		singletonInstances->push_back (o);
	}
}

struct Deleter
{
	~Deleter ()
	{
		singletonsTerminated = true;
		if (singletonInstances)
		{
			for (Steinberg::FObject** obj : *singletonInstances)
			{
				(*obj)->release ();
				*obj = nullptr;
				obj = nullptr;
			}

			delete singletonInstances;
			singletonInstances = nullptr;
		}
		delete singletonsLock;
		singletonsLock = nullptr;
	}
} deleter;
}

//------------------------------------------------------------------------
} // namespace Steinberg
//------------------------------------------------------------------------
// Project     : SDK Base
// Version     : 1.0
//
// Category    : Helpers
// Filename    : base/source/fstreamer.cpp
// Created by  : Steinberg, 15.12.2005
// Description : Extract of typed stream i/o methods from FStream
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
// 
//   * Redistributions of source code must retain the above copyright notice, 
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation 
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this 
//     software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#include "fstreamer.h"

#include "base/source/fstring.h"
#include "base/source/fbuffer.h"
#include "pluginterfaces/base/ibstream.h"

#ifndef UNICODE
#include "pluginterfaces/base/futils.h"
#endif

namespace Steinberg {

//------------------------------------------------------------------------
// IBStreamer
//------------------------------------------------------------------------
IBStreamer::IBStreamer (IBStream* stream, int16 _byteOrder)
: FStreamer (_byteOrder)
, stream (stream)
{}

//------------------------------------------------------------------------
TSize IBStreamer::readRaw (void* buffer, TSize size)
{
	int32 numBytesRead = 0;
	stream->read (buffer, (int32)size, &numBytesRead);
	return numBytesRead;
}

//------------------------------------------------------------------------
TSize IBStreamer::writeRaw (const void* buffer, TSize size)
{
	int32 numBytesWritten = 0;
	stream->write ((void*)buffer, (int32)size, &numBytesWritten);
	return numBytesWritten;
}

//------------------------------------------------------------------------
int64 IBStreamer::seek (int64 pos, FSeekMode mode)
{
	int64 result = -1;
	stream->seek (pos, mode, &result);
	return result;
}

//------------------------------------------------------------------------
int64 IBStreamer::tell ()
{
	int64 pos = 0;
	stream->tell (&pos);
	return pos;
}

//------------------------------------------------------------------------
// FStreamSizeHolder Implementation
//------------------------------------------------------------------------
FStreamSizeHolder::FStreamSizeHolder (FStreamer &s)
: stream (s), sizePos (-1)
{}

//------------------------------------------------------------------------
void FStreamSizeHolder::beginWrite ()
{
	sizePos = stream.tell ();
	stream.writeInt32 (0L);
}

//------------------------------------------------------------------------
int32 FStreamSizeHolder::endWrite ()
{
	if (sizePos < 0)
		return 0;
	
	int64 currentPos = stream.tell ();

	stream.seek (sizePos, kSeekSet);
	int32 size = int32 (currentPos - sizePos - sizeof (int32));
	stream.writeInt32 (size);
	
	stream.seek (currentPos, kSeekSet);
	return size;
}

//------------------------------------------------------------------------
int32 FStreamSizeHolder::beginRead ()
{
	sizePos = stream.tell ();
	int32 size = 0;
	stream.readInt32 (size);
	sizePos += size + sizeof (int32);
	return size;
}

//------------------------------------------------------------------------
void FStreamSizeHolder::endRead ()
{
	if (sizePos >= 0)
		stream.seek (sizePos, kSeekSet);
}

//------------------------------------------------------------------------
// FStreamer
//------------------------------------------------------------------------
FStreamer::FStreamer (int16 _byteOrder)
: byteOrder (_byteOrder)
{}

// int8 / char -----------------------------------------------------------
//------------------------------------------------------------------------
bool FStreamer::writeChar8 (char8 c)
{
	return writeRaw ((void*)&c, sizeof (char8)) == sizeof (char8);
}

//------------------------------------------------------------------------
bool FStreamer::readChar8 (char8& c)
{
	return readRaw ((void*)&c, sizeof (char8)) == sizeof (char8);
}

//------------------------------------------------------------------------
bool FStreamer::writeUChar8 (unsigned char c)
{
	return writeRaw ((void*)&c, sizeof (unsigned char)) == sizeof (unsigned char);
}

//------------------------------------------------------------------------
bool FStreamer::readUChar8 (unsigned char& c)
{
	return readRaw ((void*)&c, sizeof (unsigned char)) == sizeof (unsigned char);
}

//------------------------------------------------------------------------
bool FStreamer::writeChar16 (char16 c)
{
	if (BYTEORDER != byteOrder)
		SWAP_16 (c);
	return writeRaw ((void*)&c, sizeof (char16)) == sizeof (char16);
}

//------------------------------------------------------------------------
bool FStreamer::readChar16 (char16& c)
{
	if (readRaw ((void*)&c, sizeof (char16)) == sizeof (char16))
	{
		if (BYTEORDER != byteOrder)
			SWAP_16 (c);
		return true;
	}
	c = 0;
	return false;
}

//------------------------------------------------------------------------
bool FStreamer::writeInt8 (int8 c)
{
	return writeRaw ((void*)&c, sizeof (int8)) == sizeof (int8);
}

//------------------------------------------------------------------------
bool FStreamer::readInt8 (int8& c)
{
	return readRaw ((void*)&c, sizeof (int8)) == sizeof (int8);
}

//------------------------------------------------------------------------
bool FStreamer::writeInt8u (uint8 c)
{
	return writeRaw ((void*)&c, sizeof (uint8)) == sizeof (uint8);
}

//------------------------------------------------------------------------
bool FStreamer::readInt8u (uint8& c)
{
	return readRaw ((void*)&c, sizeof (uint8)) == sizeof (uint8);
}

// int16 -----------------------------------------------------------------
//------------------------------------------------------------------------
bool FStreamer::writeInt16 (int16 i)
{
	if (BYTEORDER != byteOrder)
		SWAP_16 (i);
	return writeRaw ((void*)&i, sizeof (int16)) == sizeof (int16);
}

//------------------------------------------------------------------------
bool FStreamer::readInt16 (int16& i)
{
	if (readRaw ((void*)&i, sizeof (int16)) == sizeof (int16))
	{
		if (BYTEORDER != byteOrder)
			SWAP_16 (i);
		return true;
	}
	i = 0;
	return false;
}

//------------------------------------------------------------------------
bool FStreamer::writeInt16Array (const int16* array, int32 count)
{
	for (int32 i = 0; i < count; i++)
	{
		if (!writeInt16 (array[i]))
			return false;
	}
	return true;
}

//------------------------------------------------------------------------
bool FStreamer::readInt16Array (int16* array, int32 count)
{
	for (int32 i = 0; i < count; i++)
	{
		if (!readInt16 (array[i]))
			return false;
	}
	return true;
}

//------------------------------------------------------------------------
bool FStreamer::writeInt16u (uint16 i)
{
	if (BYTEORDER != byteOrder)
		SWAP_16 (i);
	return writeRaw ((void*)&i, sizeof (uint16)) == sizeof (uint16);
}

//------------------------------------------------------------------------
bool FStreamer::readInt16u (uint16& i)
{
	if (readRaw ((void*)&i, sizeof (uint16)) == sizeof (uint16))
	{
		if (BYTEORDER != byteOrder)
			SWAP_16 (i);
		return true;
	}
	i = 0;
	return false;
}

//------------------------------------------------------------------------
bool FStreamer::writeInt16uArray (const uint16* array, int32 count)
{
	for (int32 i = 0; i < count; i++)
	{
		if (!writeInt16u (array[i]))
			return false;
	}
	return true;
}

//------------------------------------------------------------------------
bool FStreamer::readInt16uArray (uint16* array, int32 count)
{
	for (int32 i = 0; i < count; i++)
	{
		if (!readInt16u (array[i]))
			return false;
	}
	return true;
}

// int32 -----------------------------------------------------------------
//------------------------------------------------------------------------
bool FStreamer::writeInt32 (int32 i)
{
	if (BYTEORDER != byteOrder)
		SWAP_32 (i);
	return writeRaw ((void*)&i, sizeof (int32)) == sizeof (int32);
}

//------------------------------------------------------------------------
bool FStreamer::readInt32 (int32& i)
{
	if (readRaw ((void*)&i, sizeof (int32)) == sizeof (int32))
	{
		if (BYTEORDER != byteOrder)
			SWAP_32 (i);
		return true;
	}
	i = 0;
	return false;
}

//------------------------------------------------------------------------
bool FStreamer::writeInt32Array (const int32* array, int32 count)
{
	for (int32 i = 0; i < count; i++)
	{
		if (!writeInt32 (array[i]))
			return false;
	}
	return true;
}

//------------------------------------------------------------------------
bool FStreamer::readInt32Array (int32* array, int32 count)
{
	for (int32 i = 0; i < count; i++)
	{
		if (!readInt32 (array[i]))
			return false;
	}
	return true;
}

//------------------------------------------------------------------------
bool FStreamer::writeInt32u (uint32 i)
{
	if (BYTEORDER != byteOrder)
		SWAP_32 (i);
	return writeRaw ((void*)&i, sizeof (uint32)) == sizeof (uint32);
}

//------------------------------------------------------------------------
bool FStreamer::readInt32u (uint32& i)
{
	if (readRaw ((void*)&i, sizeof (uint32)) == sizeof (uint32))
	{
		if (BYTEORDER != byteOrder)
			SWAP_32 (i);
		return true;
	}
	i = 0;
	return false;
}

//------------------------------------------------------------------------
bool FStreamer::writeInt32uArray (const uint32* array, int32 count)
{
	for (int32 i = 0; i < count; i++)
	{
		if (!writeInt32u (array[i]))
			return false;
	}
	return true;
}

//------------------------------------------------------------------------
bool FStreamer::readInt32uArray (uint32* array, int32 count)
{
	for (int32 i = 0; i < count; i++)
	{
		if (!readInt32u (array[i]))
			return false;
	}
	return true;
}

// int64 -----------------------------------------------------------------
//------------------------------------------------------------------------
bool FStreamer::writeInt64 (int64 i)
{
	if (BYTEORDER != byteOrder)
		SWAP_64 (i);
	return writeRaw ((void*)&i, sizeof (int64)) == sizeof (int64);
}

//------------------------------------------------------------------------
bool FStreamer::readInt64 (int64& i)
{
	if (readRaw ((void*)&i, sizeof (int64)) == sizeof (int64))
	{
		if (BYTEORDER != byteOrder)
			SWAP_64 (i);
		return true;
	}
	i = 0;
	return false;
}

//------------------------------------------------------------------------
bool FStreamer::writeInt64Array (const int64* array, int32 count)
{
	for (int32 i = 0; i < count; i++)
	{
		if (!writeInt64 (array[i]))
			return false;
	}
	return true;
}

//------------------------------------------------------------------------
bool FStreamer::readInt64Array (int64* array, int32 count)
{
	for (int32 i = 0; i < count; i++)
	{
		if (!readInt64 (array[i]))
			return false;
	}
	return true;
}

//------------------------------------------------------------------------
bool FStreamer::writeInt64u (uint64 i)
{
	if (BYTEORDER != byteOrder)
		SWAP_64 (i);
	return writeRaw ((void*)&i, sizeof (uint64)) == sizeof (uint64);
}

//------------------------------------------------------------------------
bool FStreamer::readInt64u (uint64& i)
{
	if (readRaw ((void*)&i, sizeof (uint64)) == sizeof (uint64))
	{
		if (BYTEORDER != byteOrder)
			SWAP_64 (i);
		return true;
	}
	i = 0;
	return false;
}

//------------------------------------------------------------------------
bool FStreamer::writeInt64uArray (const uint64* array, int32 count)
{
	for (int32 i = 0; i < count; i++)
	{
		if (!writeInt64u (array[i]))
			return false;
	}
	return true;
}

//------------------------------------------------------------------------
bool FStreamer::readInt64uArray (uint64* array, int32 count)
{
	for (int32 i = 0; i < count; i++)
	{
		if (!readInt64u (array[i]))
			return false;
	}
	return true;
}

// float / double --------------------------------------------------------
//------------------------------------------------------------------------
bool FStreamer::writeFloat (float f)
{
	if (BYTEORDER != byteOrder)
		SWAP_32 (f);
	return writeRaw ((void*)&f, sizeof (float)) == sizeof (float);
}

//------------------------------------------------------------------------
bool FStreamer::readFloat (float& f)
{
	if (readRaw ((void*)&f, sizeof (float)) == sizeof (float))
	{
		if (BYTEORDER != byteOrder)
			SWAP_32 (f);
		return true;
	}
	f = 0.f;
	return false;
}

//------------------------------------------------------------------------
bool FStreamer::writeFloatArray (const float* array, int32 count)
{
	for (int32 i = 0; i < count; i++)
	{
		if (!writeFloat (array[i]))
			return false;
	}
	return true;
}

//------------------------------------------------------------------------
bool FStreamer::readFloatArray (float* array, int32 count)
{
	for (int32 i = 0; i < count; i++)
	{
		if (!readFloat (array[i]))
			return false;
	}
	return true;
}

//------------------------------------------------------------------------
bool FStreamer::writeDouble (double d)
{
	if (BYTEORDER != byteOrder)
		SWAP_64 (d);
	return writeRaw ((void*)&d, sizeof (double)) == sizeof (double);
}

//------------------------------------------------------------------------
bool FStreamer::readDouble (double& d)
{
	if (readRaw ((void*)&d, sizeof (double)) == sizeof (double))
	{
		if (BYTEORDER != byteOrder)
			SWAP_64 (d);
		return true;
	}
	d = 0.0;
	return false;
}

//------------------------------------------------------------------------
bool FStreamer::writeDoubleArray (const double* array, int32 count)
{
	for (int32 i = 0; i < count; i++)
	{
		if (!writeDouble (array[i]))
			return false;
	}
	return true;
}

//------------------------------------------------------------------------
bool FStreamer::readDoubleArray (double* array, int32 count)
{
	for (int32 i = 0; i < count; i++)
	{
		if (!readDouble (array[i]))
			return false;
	}
	return true;
}

//------------------------------------------------------------------------
bool FStreamer::readBool (bool& b)
{
	int16 v = 0;
	bool res = readInt16 (v);
	b = (v != 0);
	return res;
}

//------------------------------------------------------------------------
bool FStreamer::writeBool (bool b)
{
	return writeInt16 ((int16)b);
}

//------------------------------------------------------------------------
TSize FStreamer::writeString8 (const char8* ptr, bool terminate)
{
	TSize size = strlen (ptr);
	if (terminate) // write \0
		size++;

	return writeRaw ((void*)ptr, size);
}

//------------------------------------------------------------------------
TSize FStreamer::readString8 (char8* ptr, TSize size)
{
	if (size < 1 || ptr == nullptr)
		return 0;

	TSize i = 0;
	char8 c = 0;
	while (i < size)
	{
		if (readRaw ((void*)&c, sizeof (char)) != sizeof (char))
			break;
		ptr[i] = c;
		if (c == '\n' || c == '\0')
			break;
		i++;
	}
	// remove at end \n (LF) or \r\n (CR+LF)
	if (c == '\n')
	{
		if (i > 0 && ptr[i - 1] == '\r')
			i--;
	}
	if (i >= size)
		i = size - 1;
	ptr[i] = 0;
	
	return i;
}

//------------------------------------------------------------------------
bool FStreamer::writeStringUtf8 (const tchar* ptr)
{
	bool isUtf8 = false;

	String str (ptr);
	if (str.isAsciiString () == false) 
	{
		str.toMultiByte (kCP_Utf8);
		isUtf8 = true;
	}
	else
	{
		str.toMultiByte ();
	}

	if (isUtf8) 
		if (writeRaw (kBomUtf8, kBomUtf8Length) != kBomUtf8Length)
			return false;

	TSize size = str.length () + 1;
	if (writeRaw (str.text8 (), size) != size)
		return false;

	return true;
}

//------------------------------------------------------------------------
int32 FStreamer::readStringUtf8 (tchar* ptr, int32 nChars)
{
	char8 c = 0;

	ptr [0] = 0;

	Buffer tmp;
	tmp.setDelta (1024);

	while (true)
	{
		if (readRaw ((void*)&c, sizeof (char)) != sizeof (char))
			break;
		tmp.put (c);
		if (c == '\0')
			break;
	}

	char8* source = tmp.str8 ();
	uint32 codePage = kCP_Default; // for legacy take default page if no utf8 bom is present...
	if (tmp.getFillSize () > 2)
	{
		if (memcmp (source, kBomUtf8, kBomUtf8Length) == 0)
		{
			codePage = kCP_Utf8;
			source += 3;
		}
	}

	if (tmp.getFillSize () > 1)
	{
#ifdef UNICODE
		ConstString::multiByteToWideString (ptr, source, nChars, codePage);
#else
		if (codePage == kCP_Utf8)
		{
			Buffer wideBuffer (tmp.getFillSize () * 3);
			ConstString::multiByteToWideString (wideBuffer.wcharPtr (), source, wideBuffer.getSize () / 2, kCP_Utf8);
			ConstString::wideStringToMultiByte (ptr, wideBuffer.wcharPtr (), nChars);		
		}
		else
		{
			memcpy (ptr, source, Min<TSize> (nChars, tmp.getFillSize ()));
		}
#endif
	}

	ptr[nChars - 1] = 0;
	return ConstString (ptr).length ();
}

//------------------------------------------------------------------------
bool FStreamer::writeStr8 (const char8* s)
{
	int32 length = (s) ? (int32) strlen (s) + 1 : 0;
	if (!writeInt32 (length))
		return false;

	if (length > 0)
		return writeRaw (s, sizeof (char8) * length) == static_cast<TSize>(sizeof (char8) * length);

	return true;
}

//------------------------------------------------------------------------
int32 FStreamer::getStr8Size (const char8* s) 
{
	return sizeof (int32) + (int32)strlen (s) + 1;
}

//------------------------------------------------------------------------
char8* FStreamer::readStr8 ()
{
	int32 length;
	if (!readInt32 (length))
		return nullptr;
	
	// check corruption
	if (length > 262144)
		return nullptr;

	char8* s = (length > 0) ? NEWVEC char8[length] : nullptr;
	if (s)
		readRaw (s, length * sizeof (char8));
	return s;
}

//------------------------------------------------------------------------
bool FStreamer::skip (uint32 bytes)
{
    int8 tmp;
	while (bytes-- > 0) 
	{
		if (readInt8 (tmp) == false)
			return false;	
    }
	return true;
}

//------------------------------------------------------------------------
bool FStreamer::pad (uint32 bytes)
{
    while (bytes-- > 0) 
	{
		if (writeInt8 (0) == false)
			return false;	
	}
	return true;
}

//------------------------------------------------------------------------
} // namespace Steinberg
//------------------------------------------------------------------------
// Project     : SDK Base
// Version     : 1.0
//
// Category    : Helpers
// Filename    : base/source/fstring.cpp
// Created by  : Steinberg, 2008
// Description : String class
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
// 
//   * Redistributions of source code must retain the above copyright notice, 
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation 
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this 
//     software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#include "base/source/fstring.h"
#include "base/source/fdebug.h"
#include "pluginterfaces/base/futils.h"
#include "pluginterfaces/base/fvariant.h"

#include <cstdlib>
#include <cctype>
#include <cstdio>
#include <cstdarg>
#include <utility>
#include <complex>
#include <cmath>
#include <algorithm>
#include <cassert>

#if SMTG_OS_WINDOWS
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#ifdef _MSC_VER
#pragma warning (disable : 4244)
#pragma warning (disable : 4267)
#pragma warning (disable : 4996)

#if DEVELOPMENT
#include <crtdbg.h>

#define malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__)
#define realloc(p,s) _realloc_dbg(p,s,  _NORMAL_BLOCK, __FILE__, __LINE__)
#define free(p) _free_dbg(p, _NORMAL_BLOCK)

#endif // DEVELOPMENT
#endif // _MSC_VER
#endif // SMTG_OS_WINDOWS

#ifndef kPrintfBufferSize
#define kPrintfBufferSize 4096
#endif

#if SMTG_OS_MACOS
#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFString.h>
#include <CoreFoundation/CFStringEncodingExt.h>
#include <wchar.h>

#if defined (__GNUC__) && (__GNUC__ >= 4) && !__LP64__
// on 32 bit Mac OS X we can safely ignore the format warnings as sizeof(int) == sizeof(long)
#pragma GCC diagnostic ignored "-Wformat"
#endif

#define SMTG_ENABLE_DEBUG_CFALLOCATOR 0
#define SMTG_DEBUG_CFALLOCATOR	(DEVELOPMENT && SMTG_ENABLE_DEBUG_CFALLOCATOR)

#if SMTG_DEBUG_CFALLOCATOR
#include <libkern/OSAtomic.h>
#include <dlfcn.h>
#endif

namespace Steinberg {
#if SMTG_DEBUG_CFALLOCATOR
static CFAllocatorRef kCFAllocator = NULL;

struct CFStringDebugAllocator : CFAllocatorContext
{
	CFStringDebugAllocator ()
	{
		version = 0;
		info = this;
		retain = nullptr;
		release = nullptr;
		copyDescription = nullptr;
		allocate = allocateCallBack;
		reallocate = reallocateCallBack;
		deallocate = deallocateCallBack;
		preferredSize = preferredSizeCallBack;

		numAllocations = allocationSize = numDeallocations = 0;
		cfAllocator = CFAllocatorCreate (kCFAllocatorUseContext, this);

		Dl_info info;
		if (dladdr ((const void*)CFStringDebugAllocator::allocateCallBack, &info))
		{
			moduleName = info.dli_fname;
		}
		kCFAllocator = cfAllocator;
	}

	~CFStringDebugAllocator ()
	{
		kCFAllocator = kCFAllocatorDefault;
		CFRelease (cfAllocator);
		FDebugPrint ("CFStringDebugAllocator (%s):\n", moduleName.text8 ());
		FDebugPrint ("\tNumber of allocations  : %u\n", numAllocations);
		FDebugPrint ("\tNumber of deallocations: %u\n", numDeallocations);
		FDebugPrint ("\tAllocated Bytes        : %u\n", allocationSize);
	}

	String moduleName;
	CFAllocatorRef cfAllocator;
	volatile int64_t numAllocations;
	volatile int64_t numDeallocations;
	volatile int64_t allocationSize;

	void* doAllocate (CFIndex allocSize, CFOptionFlags hint)
	{
		void* ptr = CFAllocatorAllocate (kCFAllocatorDefault, allocSize, hint);
		OSAtomicIncrement64 (&numAllocations);
		OSAtomicAdd64 (allocSize, &allocationSize);
		return ptr;
	}
	void* doReallocate (void* ptr, CFIndex newsize, CFOptionFlags hint)
	{
		void* newPtr = CFAllocatorReallocate (kCFAllocatorDefault, ptr, newsize, hint);
		return newPtr;
	}
	void doDeallocate (void* ptr)
	{
		CFAllocatorDeallocate (kCFAllocatorDefault, ptr);
		OSAtomicIncrement64 (&numDeallocations);
	}
	CFIndex getPreferredSize (CFIndex size, CFOptionFlags hint)
	{
		return CFAllocatorGetPreferredSizeForSize (kCFAllocatorDefault, size, hint);
	}

	static void* allocateCallBack (CFIndex allocSize, CFOptionFlags hint, void* info)
	{
		return static_cast<CFStringDebugAllocator*> (info)->doAllocate (allocSize, hint);
	}
	static void* reallocateCallBack (void* ptr, CFIndex newsize, CFOptionFlags hint, void* info)
	{
		return static_cast<CFStringDebugAllocator*> (info)->doReallocate (ptr, newsize, hint);
	}

	static void deallocateCallBack (void* ptr, void* info)
	{
		static_cast<CFStringDebugAllocator*> (info)->doDeallocate (ptr);
	}
	static CFIndex preferredSizeCallBack (CFIndex size, CFOptionFlags hint, void* info)
	{
		return static_cast<CFStringDebugAllocator*> (info)->getPreferredSize (size, hint);
	}
};
static CFStringDebugAllocator gDebugAllocator;
#else

static const CFAllocatorRef kCFAllocator = ::kCFAllocatorDefault;
#endif // SMTG_DEBUG_CFALLOCATOR
}

//-----------------------------------------------------------------------------
static void* toCFStringRef (const Steinberg::char8* source, Steinberg::uint32 encoding)
{
	if (encoding == 0xFFFF)
		encoding = kCFStringEncodingASCII;
	if (source)
		return (void*)CFStringCreateWithCString (Steinberg::kCFAllocator, source, encoding);
	else
		return (void*)CFStringCreateWithCString (Steinberg::kCFAllocator, "", encoding);
}

//-----------------------------------------------------------------------------
static bool fromCFStringRef (Steinberg::char8* dest, Steinberg::int32 destSize, const void* cfStr, Steinberg::uint32 encoding)
{
	CFIndex usedBytes;
	CFRange range = {0, CFStringGetLength ((CFStringRef)cfStr)};
	bool result = CFStringGetBytes ((CFStringRef)cfStr, range, encoding, '?', false, (UInt8*)dest, destSize, &usedBytes);
	dest[usedBytes] = 0;
	return result;
}
#endif // SMTG_OS_MACOS

#if SMTG_OS_WINDOWS
//-----------------------------------------------------------------------------
static inline int stricmp16 (const Steinberg::tchar* s1, const Steinberg::tchar* s2)
{
	return wcsicmp (Steinberg::wscast (s1), Steinberg::wscast (s2));
}

//-----------------------------------------------------------------------------
static inline int strnicmp16 (const Steinberg::tchar* s1, const Steinberg::tchar* s2, size_t l)
{
	return wcsnicmp (Steinberg::wscast (s1), Steinberg::wscast (s2), l);
}

//-----------------------------------------------------------------------------
static inline int vsnwprintf (Steinberg::char16* buffer, size_t bufferSize,
                              const Steinberg::char16* format, va_list args)
{
	return _vsnwprintf (Steinberg::wscast (buffer), bufferSize, Steinberg::wscast (format), args);
}

//-----------------------------------------------------------------------------
static inline Steinberg::int32 sprintf16 (Steinberg::char16* str, const Steinberg::char16* format, ...)
{
	va_list marker;
	va_start (marker, format);
	return vsnwprintf (str, -1, format, marker);
}

#elif SMTG_OS_LINUX
#include <codecvt>
#include <locale>
#include <cstring>
#include <string>
#include <limits>
#include <cassert>
#include <wchar.h>

using ConverterFacet = std::codecvt_utf8_utf16<char16_t>;
using Converter = std::wstring_convert<ConverterFacet, char16_t>;

//------------------------------------------------------------------------
static ConverterFacet& converterFacet ()
{
	static ConverterFacet gFacet;
	return gFacet;
}

//------------------------------------------------------------------------
static Converter& converter ()
{
	static Converter gConverter;
	return gConverter;
}

//-----------------------------------------------------------------------------
static inline int stricasecmp (const Steinberg::char8* s1, const Steinberg::char8* s2)
{
	return ::strcasecmp (s1, s2);
}

//-----------------------------------------------------------------------------
static inline int strnicasecmp (const Steinberg::char8* s1, const Steinberg::char8* s2, size_t n)
{
	return ::strncasecmp (s1, s2, n);
}

//-----------------------------------------------------------------------------
static inline int stricmp16 (const Steinberg::char16* s1, const Steinberg::char16* s2)
{
	auto str1 = converter ().to_bytes (s1);
	auto str2 = converter ().to_bytes (s2);
	return stricasecmp (str1.data (), str2.data ());
}

//-----------------------------------------------------------------------------
static inline int strnicmp16 (const Steinberg::char16* s1, const Steinberg::char16* s2, int n)
{
	auto str1 = converter ().to_bytes (s1);
	auto str2 = converter ().to_bytes (s2);
	return strnicasecmp (str1.data (), str2.data (), n);
}

//-----------------------------------------------------------------------------
static inline int sprintf16 (Steinberg::char16* wcs, const Steinberg::char16* format, ...)
{
	assert (false && "DEPRECATED No Linux implementation");
	return 0;
}

//-----------------------------------------------------------------------------
static inline int vsnwprintf (Steinberg::char16* wcs, size_t maxlen,
							  const Steinberg::char16* format, va_list args)
{
	Steinberg::char8 str8[kPrintfBufferSize];
	auto format_utf8 = converter ().to_bytes(format);
	auto len = vsnprintf (str8, kPrintfBufferSize, format_utf8.data (), args);

	auto tmp_str = converter ().from_bytes (str8, str8 + len);
	auto target_len = std::min (tmp_str.size (), maxlen - 1);
	tmp_str.copy (wcs, target_len);
	wcs[target_len] = '\0';

	return tmp_str.size ();
}

//-----------------------------------------------------------------------------
static inline Steinberg::char16* strrchr16 (const Steinberg::char16* str, Steinberg::char16 c)
{
	assert (false && "DEPRECATED No Linux implementation");
	return nullptr;
}

#elif SMTG_OS_MACOS
#define tstrtoi64 strtoll
#define stricmp strcasecmp
#define strnicmp strncasecmp

//-----------------------------------------------------------------------------
static inline Steinberg::int32 strnicmp16 (const Steinberg::char16* str1, const Steinberg::char16* str2, size_t size)
{
	if (size == 0)
		return 0;

	CFIndex str1Len = Steinberg::strlen16 (str1);
	CFIndex str2Len = Steinberg::strlen16 (str2);
	if (static_cast<CFIndex> (size) < str2Len) // range is not applied to second string
		str2Len = size;
	CFStringRef cfStr1 = CFStringCreateWithCharactersNoCopy (Steinberg::kCFAllocator, (UniChar*)str1, str1Len, kCFAllocatorNull);
	CFStringRef cfStr2 = CFStringCreateWithCharactersNoCopy (Steinberg::kCFAllocator, (UniChar*)str2, str2Len, kCFAllocatorNull);
	CFComparisonResult result = CFStringCompareWithOptions (cfStr1, cfStr2, CFRangeMake (0, size), kCFCompareCaseInsensitive);
	CFRelease (cfStr1);
	CFRelease (cfStr2);
	switch (result)
	{
		case kCFCompareEqualTo:	return 0;
		case kCFCompareLessThan: return -1;
		case kCFCompareGreaterThan: 
		default: return 1;
	};
}

//-----------------------------------------------------------------------------
static inline Steinberg::int32 stricmp16 (const Steinberg::char16* str1, CFIndex str1Len, const Steinberg::char16* str2, CFIndex str2Len)
{
	CFStringRef cfStr1 = CFStringCreateWithCharactersNoCopy (Steinberg::kCFAllocator, (UniChar*)str1, str1Len, kCFAllocatorNull);
	CFStringRef cfStr2 = CFStringCreateWithCharactersNoCopy (Steinberg::kCFAllocator, (UniChar*)str2, str2Len, kCFAllocatorNull);
	CFComparisonResult result = CFStringCompare (cfStr1, cfStr2, kCFCompareCaseInsensitive);
	CFRelease (cfStr1);
	CFRelease (cfStr2);
	switch (result)
	{
		case kCFCompareEqualTo:	return 0;
		case kCFCompareLessThan: return -1;
		case kCFCompareGreaterThan: 
		default: return 1;
	};
}

//-----------------------------------------------------------------------------
static inline Steinberg::int32 stricmp16 (const Steinberg::ConstString& str1, const Steinberg::ConstString& str2)
{
	return stricmp16 (str1.text16 (), str1.length (), str2.text16 (), str2.length ());
}

//-----------------------------------------------------------------------------
static inline Steinberg::int32 stricmp16 (const Steinberg::char16* str1, const Steinberg::char16* str2)
{
	CFIndex str1Len = Steinberg::strlen16 (str1);
	CFIndex str2Len = Steinberg::strlen16 (str2);
	return stricmp16 (str1, str1Len, str2, str2Len);
}

//-----------------------------------------------------------------------------
static inline Steinberg::char16* strrchr16 (const Steinberg::char16* str, Steinberg::char16 c)
{
	Steinberg::int32 len = Steinberg::ConstString (str).length ();
	while (len > 0)
	{
		if (str[len] == c)
			return const_cast<Steinberg::char16*>(str + len);
		len--;
	}
	return 0;
}

//-----------------------------------------------------------------------------
static inline Steinberg::int32 vsnwprintf (Steinberg::char16* str, Steinberg::int32 size, const Steinberg::char16* format, va_list ap)
{
	// wrapped using CoreFoundation's CFString
	CFMutableStringRef formatString = (CFMutableStringRef)Steinberg::ConstString (format).toCFStringRef (0xFFFF, true);
	CFStringFindAndReplace (formatString, CFSTR("%s"), CFSTR("%S"), CFRangeMake (0, CFStringGetLength (formatString)), 0);
	CFStringRef resultString = CFStringCreateWithFormatAndArguments (Steinberg::kCFAllocator, 0, formatString, ap);
	CFRelease (formatString);
	if (resultString)
	{
		Steinberg::String res;
		res.fromCFStringRef (resultString);
		res.copyTo16 (str, 0, size);
		CFRelease (resultString);
		return 0;
	}
	return 1;
}

//-----------------------------------------------------------------------------
static inline Steinberg::int32 sprintf16 (Steinberg::char16* str, const Steinberg::char16* format, ...)
{
	va_list marker;
	va_start (marker, format);
	return vsnwprintf (str, -1, format, marker);
}

#endif // SMTG_OS_LINUX

/*
UTF-8                EF BB BF 
UTF-16 Big Endian    FE FF 
UTF-16 Little Endian FF FE 
UTF-32 Big Endian    00 00 FE FF 
UTF-32 Little Endian FF FE 00 00 
*/

namespace Steinberg {

//-----------------------------------------------------------------------------
static inline bool isCaseSensitive (ConstString::CompareMode mode)
{
	return mode == ConstString::kCaseSensitive;
}

//-----------------------------------------------------------------------------
//	ConstString
//-----------------------------------------------------------------------------
ConstString::ConstString (const char8* str, int32 length)
: buffer8 ((char8*)str)
, len (length < 0 ? (str ? static_cast<uint32> (strlen (str)) : 0) : length)
, isWide (0) 
{
}

//-----------------------------------------------------------------------------
ConstString::ConstString (const char16* str, int32 length)
: buffer16 ((char16*)str)
, len (length < 0 ? (str ? strlen16 (str) : 0) : length)
, isWide (1) 
{
}

//-----------------------------------------------------------------------------
ConstString::ConstString (const ConstString& str, int32 offset, int32 length)
: buffer (str.buffer)
, len (length < 0 ? (str.len - (offset > 0 ? offset : 0)) : length)
, isWide (str.isWide)
{
	if (offset > 0)
	{
		if (isWide)
			buffer16 += offset;
		else
			buffer8 += offset;
	}
}

//-----------------------------------------------------------------------------
ConstString::ConstString (const FVariant& var)
: buffer (nullptr)
, len (0)
, isWide (0) 
{
	switch (var.getType ())
	{
		case FVariant::kString8:
			buffer8 = (char8*)var.getString8 ();
			len = buffer8 ? strlen8 (buffer8) : 0;
			isWide = false;
			break;

		case FVariant::kString16:
			buffer16 = (char16*)var.getString16 ();
			len = buffer16 ? strlen16 (buffer16) : 0;
			isWide = true;
			break;
	}
}

//-----------------------------------------------------------------------------
ConstString::ConstString ()
: buffer (nullptr)
, len (0)
, isWide (0) 
{
}

//-----------------------------------------------------------------------------
bool ConstString::testChar8 (uint32 index, char8 c) const
{
	if (index >= len)
		return c == 0;
	if (isWide)
	{
		// make c wide
		char8 src[] = {c, 0};
		char16 dest[2] = {0};
		if (multiByteToWideString (dest, src, 2) > 0)
			return buffer16[index] == dest[0];
		return false;
	}
	return buffer8[index] == c;
}

//-----------------------------------------------------------------------------
bool ConstString::testChar16 (uint32 index, char16 c) const
{
	if (index >= len)
		return c == 0;
	if (!isWide)
	{
		// make c ansi
		char16 src[] = {c, 0};
		char8 dest[8] = {0};
		if (wideStringToMultiByte (dest, src, 2) > 0 && dest[1] == 0)
			return buffer8[index] == dest[0];
		return false;
	}
	return buffer16[index] == c;
}

//-----------------------------------------------------------------------------
bool ConstString::extract (String& result, uint32 idx, int32 n) const
{
	// AddressSanitizer : when extracting part of "this" on itself, it can lead to heap-use-after-free.
	SMTG_ASSERT (this != static_cast<ConstString*> (&result))
	
	if (len == 0|| idx >= len)
		return false;

	if ((idx + n > len) || n < 0)
		n = len - idx;

	if (isWide)
		result.assign (buffer16 + idx, n);
	else
		result.assign (buffer8 + idx, n);

	return true;
}

//-----------------------------------------------------------------------------
int32 ConstString::copyTo8 (char8* str, uint32 idx, int32 n) const
{
	if (!str)
		return 0;

	if (isWide)
	{
		String tmp (text16 ());
		if (tmp.toMultiByte () == false)
			return 0;
		return tmp.copyTo8 (str, idx, n);
	}

	if (isEmpty () || idx >= len || !buffer8)
	{
		str[0] = 0;
		return 0;
	}

	if ((idx + n > len) || n < 0)
		n = len - idx;

	memcpy (str, &(buffer8[idx]), n * sizeof (char8));
	str[n] = 0;
	return n;
}

//-----------------------------------------------------------------------------
int32 ConstString::copyTo16 (char16* str, uint32 idx, int32 n) const
{
	if (!str)
		return 0;

	if (!isWide)
	{
		String tmp (text8 ());
		if (tmp.toWideString () == false)
			return 0;
		return tmp.copyTo16 (str, idx, n);
	}
	
	if (isEmpty () || idx >= len || !buffer16)
	{
		str[0] = 0;
		return 0;
	}

	if ((idx + n > len) || n < 0)
		n = len - idx;
	
	memcpy (str, &(buffer16[idx]), n * sizeof (char16));
	str[n] = 0;
	return n;
}

//-----------------------------------------------------------------------------
int32 ConstString::copyTo (tchar* str, uint32 idx, int32 n) const
{
#ifdef UNICODE
	return copyTo16 (str, idx, n);
#else
	return copyTo8 (str, idx, n);
#endif
}

//-----------------------------------------------------------------------------
void ConstString::copyTo (IStringResult* result) const
{
	if (isWideString () == false)
	{
		result->setText (text8 ());	
	}
	else
	{
		FUnknownPtr<IString> iStr (result);
		if (iStr)
		{
			iStr->setText16 (text16 ());
		}
		else
		{
			String tmp (*this);
			tmp.toMultiByte ();
			result->setText (tmp.text8 ());
		}
	}
}

//-----------------------------------------------------------------------------
void ConstString::copyTo (IString& string) const
{
	if (isWideString ())
		string.setText16 (text16 ());
	else
		string.setText8 (text8 ());
}



//-----------------------------------------------------------------------------
int32 ConstString::compare (const ConstString& str, int32 n, CompareMode mode) const
{
	if (n == 0)
		return 0;

	if (str.isEmpty ())
	{
		if (isEmpty ())
			return 0;
		return 1;
	}
	if (isEmpty ())
		return -1;

	if (!isWide && !str.isWide)
	{
		if (n < 0)
		{
			if (isCaseSensitive (mode))
				return strcmp (*this, str);
			return stricmp (*this, str);
		}
		if (isCaseSensitive (mode))
			return strncmp (*this, str, n);
		return strnicmp (*this, str, n);
	}
	if (isWide && str.isWide)
	{
		if (n < 0)
		{
			if (isCaseSensitive (mode))
				return strcmp16 (*this, str);
			return stricmp16 (*this, str);
		}
		if (isCaseSensitive (mode))
			return strncmp16 (*this, str, n);
		return strnicmp16 (*this, str, n);
	}
	return compareAt (0, str, n, mode);
}

//-----------------------------------------------------------------------------
int32 ConstString::compare (const ConstString& str, CompareMode mode) const
{
	return compare (str, -1, mode);
}

//-----------------------------------------------------------------------------
int32 ConstString::compareAt (uint32 index, const ConstString& str, int32 n, CompareMode mode) const
{
	if (n == 0)
		return 0;

	if (str.isEmpty ())
	{
		if (isEmpty ())
			return 0;
		return 1;
	}
	if (isEmpty ())
		return -1;

	if (!isWide && !str.isWide)
	{
		char8* toCompare = buffer8;
		if (index > 0)
		{
			if (index >= len)
			{
				if (str.isEmpty ())
					return 0;
				return -1;
			}
			toCompare += index;
		}

		if (n < 0)
		{
			if (isCaseSensitive (mode))
				return strcmp (toCompare, str);
			return stricmp (toCompare, str);
		}
		if (isCaseSensitive (mode))
			return strncmp (toCompare, str, n);
		return strnicmp (toCompare, str, n);
	}
	if (isWide && str.isWide)
	{
		char16* toCompare = buffer16;
		if (index > 0)
		{
			if (index >= len)
			{
				if (str.isEmpty ())
					return 0;
				return -1;
			}
			toCompare += index;
		}

		if (n < 0)
		{
			if (isCaseSensitive (mode))
				return strcmp16 (toCompare, str.text16 ());
			return stricmp16 (toCompare, str.text16 ());
		}
		if (isCaseSensitive (mode))
			return strncmp16 (toCompare, str.text16 (), n);
		return strnicmp16 (toCompare, str.text16 (), n);
	}
	
	if (isWide)
	{
		String tmp (str.text8 ());
		if (tmp.toWideString () == false)
			return -1;
		return compareAt (index, tmp, n, mode);
	}
		
	String tmp (text8 ());
	if (tmp.toWideString () == false)
		return 1;
	return tmp.compareAt (index, str, n, mode);
}

//------------------------------------------------------------------------
Steinberg::int32 ConstString::naturalCompare (const ConstString& str, CompareMode mode /*= kCaseSensitive*/) const
{
	if (str.isEmpty ())
	{
		if (isEmpty ())
			return 0;
		return 1;
	}
	if (isEmpty ())
		return -1;

	if (!isWide && !str.isWide)
		return strnatcmp8 (buffer8, str.text8 (), isCaseSensitive (mode));
	if (isWide && str.isWide)
		return strnatcmp16 (buffer16, str.text16 (), isCaseSensitive (mode));
	
	if (isWide)
	{
		String tmp (str.text8 ());
		tmp.toWideString ();
		return strnatcmp16 (buffer16, tmp.text16 (), isCaseSensitive (mode));
	}
	String tmp (text8 ());
	tmp.toWideString ();
	return strnatcmp16 (tmp.text16 (), str.text16 (), isCaseSensitive (mode));
}

//-----------------------------------------------------------------------------
bool ConstString::startsWith (const ConstString& str, CompareMode mode /*= kCaseSensitive*/) const
{
	if (str.isEmpty ())
	{
		return isEmpty ();
	}
	if (isEmpty ())
	{
		return false;
	}
	if (length () < str.length ())
	{
		return false;
	}
	if (!isWide && !str.isWide)
	{
		if (isCaseSensitive (mode))
			return strncmp (buffer8, str.buffer8, str.length ()) == 0;
		return strnicmp (buffer8, str.buffer8, str.length ()) == 0;
	}
	if (isWide && str.isWide)
	{
		if (isCaseSensitive (mode))
			return strncmp16 (buffer16, str.buffer16, str.length ()) == 0;
		return strnicmp16 (buffer16, str.buffer16, str.length ()) == 0;
	}
	if (isWide)
	{
		String tmp (str.text8 ());
		tmp.toWideString ();
		if (tmp.length () > length ())
			return false;
		if (isCaseSensitive (mode))
			return strncmp16 (buffer16, tmp.buffer16, tmp.length ()) == 0;
		return strnicmp16 (buffer16, tmp.buffer16, tmp.length ()) == 0;
	}
	String tmp (text8 ());
	tmp.toWideString ();
	if (str.length () > tmp.length ())
		return false;
	if (isCaseSensitive (mode))
		return strncmp16 (tmp.buffer16, str.buffer16, str.length ()) == 0;
	return strnicmp16 (tmp.buffer16, str.buffer16, str.length ()) == 0;
}

//-----------------------------------------------------------------------------
bool ConstString::endsWith (const ConstString& str, CompareMode mode /*= kCaseSensitive*/) const
{
	if (str.isEmpty ())
	{
		return isEmpty ();
	}
	if (isEmpty ())
	{
		return false;
	}
	if (length () < str.length ())
	{
		return false;
	}
	if (!isWide && !str.isWide)
	{
		if (isCaseSensitive (mode))
			return strncmp (buffer8 + (length () - str.length ()), str.buffer8, str.length ()) == 0;
		return strnicmp (buffer8 + (length () - str.length ()), str.buffer8, str.length ()) == 0;
	}
	if (isWide && str.isWide)
	{
		if (isCaseSensitive (mode))
			return strncmp16 (buffer16 + (length () - str.length ()), str.buffer16, str.length ()) == 0;
		return strnicmp16 (buffer16 + (length () - str.length ()), str.buffer16, str.length ()) == 0;
	}
	if (isWide)
	{
		String tmp (str.text8 ());
		tmp.toWideString ();
		if (tmp.length () > length ())
			return false;
		if (isCaseSensitive (mode))
			return strncmp16 (buffer16 + (length () - tmp.length ()), tmp.buffer16, tmp.length ()) == 0;
		return strnicmp16 (buffer16 + (length () - tmp.length ()), tmp.buffer16, tmp.length ()) == 0;
	}
	String tmp (text8 ());
	tmp.toWideString ();
	if (str.length () > tmp.length ())
		return false;
	if (isCaseSensitive (mode))
		return strncmp16 (tmp.buffer16 + (tmp.length () - str.length ()), str.buffer16, str.length ()) == 0;
	return strnicmp16 (tmp.buffer16 + (tmp.length () - str.length ()), str.buffer16, str.length ()) == 0;
}

//-----------------------------------------------------------------------------
bool ConstString::contains (const ConstString& str, CompareMode m) const
{
	return findFirst (str, -1, m) != -1;
}

//-----------------------------------------------------------------------------
int32 ConstString::findNext (int32 startIndex, const ConstString& str, int32 n, CompareMode mode, int32 endIndex) const
{
	uint32 endLength = len;
	if (endIndex > -1 && (uint32)endIndex < len)
		endLength = endIndex + 1;

	if (isWide && str.isWide)
	{
		if (startIndex < 0)
			startIndex = 0;

		uint32 stringLength = str.length ();
		n = n < 0 ? stringLength : Min<uint32> (n, stringLength);

		if (n > 0)
		{
			uint32 i = 0;

			if (isCaseSensitive (mode))
			{
				for (i = startIndex; i < endLength; i++)
					if (strncmp16 (buffer16 + i, str, n) == 0)
						return i;
			}
			else
			{
				for (i = startIndex; i < endLength; i++)
					if (strnicmp16 (buffer16 + i, str, n) == 0)
						return i;
			}
		}
		return -1;
	}
	if (!isWide && !str.isWide)
	{
		uint32 stringLength = str.length ();
		n = n < 0 ? stringLength : Min<uint32> (n, stringLength);

		if (startIndex < 0)
			startIndex = 0;

		if (n > 0)
		{
			uint32 i = 0;

			if (isCaseSensitive (mode))
			{
				for (i = startIndex; i < endLength; i++)
					if (strncmp (buffer8 + i, str, n) == 0)
						return i;
			}
			else
			{
				for (i = startIndex; i < endLength; i++)
					if (strnicmp (buffer8 + i, str, n) == 0)
						return i;
			}
		}
		return -1;
	}
	String tmp;
	if (isWide)
	{
		tmp = str.text8 ();
		tmp.toWideString ();
		return findNext (startIndex, tmp, n , mode, endIndex);
	}
	tmp = text8 ();
	tmp.toWideString ();
	return tmp.findNext (startIndex, str, n, mode, endIndex);
}

//------------------------------------------------------------------------------------------------
int32 ConstString::findNext (int32 startIndex, char8 c, CompareMode mode, int32 endIndex) const
{
	uint32 endLength = len;
	if (endIndex > -1 && (uint32)endIndex < len)
		endLength = endIndex + 1;

	if (isWide)
	{
		char8 src[] = {c, 0};
		char16 dest[8] = {0};
		if (multiByteToWideString (dest, src, 2) > 0)
			return findNext (startIndex, dest[0], mode, endIndex);
		return -1;
	}

	if (startIndex < 0)
		startIndex = 0;
	uint32 i;

	if (isCaseSensitive (mode))
	{
		for (i = startIndex; i < endLength; i++)
		{
			if (buffer8[i] == c)
				return i;
		}
	}
	else
	{
		c = toLower (c);
		for (i = startIndex; i < endLength; i++)
		{
			if (toLower (buffer8[i]) == c)
				return i;
		}
	}
	return -1;
}

//-----------------------------------------------------------------------------
int32 ConstString::findNext (int32 startIndex, char16 c, CompareMode mode, int32 endIndex) const
{
	uint32 endLength = len;
	if (endIndex > -1 && (uint32)endIndex < len)
		endLength = endIndex + 1;

	if (!isWide)
	{
		char16 src[] = {c, 0};
		char8 dest[8] = {0};
		if (wideStringToMultiByte (dest, src, 2) > 0 && dest[1] == 0)
			return findNext (startIndex, dest[0], mode, endIndex);

		return -1;
	}

	uint32 i;
	if (startIndex < 0)
		startIndex = 0;

	if (isCaseSensitive (mode))
	{
		for (i = startIndex; i < endLength; i++)
		{
			if (buffer16[i] == c)
				return i;
		}
	}
	else
	{
		c = toLower (c);
		for (i = startIndex; i < endLength; i++)
		{
			if (toLower (buffer16[i]) == c)
				return i;
		}
	}
	return -1;
}

//-----------------------------------------------------------------------------
int32 ConstString::findPrev (int32 startIndex, char8 c, CompareMode mode) const
{
	if (len == 0)
		return -1;

	if (isWide)
	{
		char8 src[] = {c, 0};
		char16 dest[8] = {0};
		if (multiByteToWideString (dest, src, 2) > 0)
			return findPrev (startIndex, dest[0], mode);
		return -1;
	}

	if (startIndex < 0 || startIndex > (int32)len)
		startIndex = len;

	int32 i;

	if (isCaseSensitive (mode))
	{
		for (i = startIndex; i >= 0; i--)
		{
			if (buffer8[i] == c)
				return i;
		}
	}
	else
	{
		c = toLower (c);
		for (i = startIndex; i >= 0; i--)
		{
			if (toLower (buffer8[i]) == c)
				return i;
		}
	}
	return -1;
}

//-----------------------------------------------------------------------------
int32 ConstString::findPrev (int32 startIndex, char16 c, CompareMode mode) const
{
	if (len == 0)
		return -1;

	if (!isWide)
	{
		char16 src[] = {c, 0};
		char8 dest[8] = {0};
		if (wideStringToMultiByte (dest, src, 2) > 0 && dest[1] == 0)
			return findPrev (startIndex, dest[0], mode);

		return -1;
	}

	if (startIndex < 0 || startIndex > (int32)len)
		startIndex = len;

	int32 i;

	if (isCaseSensitive (mode))
	{
		for (i = startIndex; i >= 0; i--)
		{
			if (buffer16[i] == c)
				return i;
		}
	}
	else
	{
		c = toLower (c);
		for (i = startIndex; i >= 0; i--)
		{
			if (toLower (buffer16[i]) == c)
				return i;
		}
	}
	return -1;
}

//-----------------------------------------------------------------------------
int32 ConstString::findPrev (int32 startIndex, const ConstString& str, int32 n, CompareMode mode) const
{
	if (isWide && str.isWide)
	{
		uint32 stringLength = str.length ();
		n = n < 0 ? stringLength : Min<uint32> (n, stringLength);

		if (startIndex < 0 || startIndex >= (int32)len)
			startIndex = len - 1;

		if (n > 0)
		{
			int32 i = 0;

			if (isCaseSensitive (mode))
			{
				for (i = startIndex; i >= 0; i--)
					if (strncmp16 (buffer16 + i, str, n) == 0)
						return i;
			}
			else
			{
				for (i = startIndex; i >= 0; i--)
					if (strnicmp16 (buffer16 + i, str, n) == 0)
						return i;
			}
		}
		return -1;
	}
	if (!isWide && !str.isWide)
	{
		uint32 stringLength = str.length ();
		n = n < 0 ? stringLength : Min<uint32> (n, stringLength);

		if (startIndex < 0 || startIndex >= (int32)len)
			startIndex = len - 1;

		if (n > 0)
		{
			int32 i = 0;

			if (isCaseSensitive (mode))
			{
				for (i = startIndex; i >= 0; i--)
					if (strncmp (buffer8 + i, str, n) == 0)
						return i;
			}
			else
			{
				for (i = startIndex; i >= 0; i--)
					if (strnicmp (buffer8 + i, str, n) == 0)
						return i;
			}
		}
		return -1;
	}
	if (isWide)
	{
		String tmp (str.text8 ());
		tmp.toWideString ();
		return findPrev (startIndex, tmp, n, mode);
	}
	String tmp (text8 ());
	tmp.toWideString ();
	return tmp.findPrev (startIndex, str, n, mode);
}

//-----------------------------------------------------------------------------
int32 ConstString::countOccurences (char8 c, uint32 startIndex, CompareMode mode) const
{
	if (isWide)
	{
		char8 src[] = {c, 0};
		char16 dest[8] = {0};
		if (multiByteToWideString (dest, src, 2) > 0)
			return countOccurences (dest[0], startIndex, mode);
		return -1;
	}

	int32 result = 0;
	int32 next = startIndex;
	while (true)
	{
		next = findNext (next, c, mode);
		if (next >= 0)
		{
			next++;
			result++;
		}
		else
			break;
	}
	return result;
}

//-----------------------------------------------------------------------------
int32 ConstString::countOccurences (char16 c, uint32 startIndex, CompareMode mode) const
{
	if (!isWide)
	{
		char16 src[] = {c, 0};
		char8 dest[8] = {0};
		if (wideStringToMultiByte (dest, src, 2) > 0 && dest[1] == 0)
			return countOccurences (dest[0], startIndex, mode);

		return -1;
	}
	int32 result = 0;
	int32 next = startIndex;
	while (true)
	{
		next = findNext (next, c, mode);
		if (next >= 0)
		{
			next++;
			result++;
		}
		else
			break;
	}
	return result;
}

//-----------------------------------------------------------------------------
int32 ConstString::getFirstDifferent (const ConstString& str, CompareMode mode) const
{
	if (str.isWide != isWide)
	{
		if (isWide)
		{
			String tmp (str.text8 ());
			if (tmp.toWideString () == false)
				return -1;
			return getFirstDifferent (tmp, mode);
		}
		
		String tmp (text8 ());
		if (tmp.toWideString () == false)
			return -1;
		return tmp.getFirstDifferent (str, mode);
	}

	uint32 len1 = len;
	uint32 len2 = str.len;
	uint32 i;

	if (isWide)
	{
		if (isCaseSensitive (mode))
		{
			for (i = 0; i <= len1 && i <= len2; i++)
			{
				if (buffer16[i] != str.buffer16[i])
					return i;
			}
		}
		else
		{
			for (i = 0; i <= len1 && i <= len2; i++)
			{
				if (toLower (buffer16[i]) != toLower (str.buffer16[i]))
					return i;
			}
		}
	}
	else
	{
		if (isCaseSensitive (mode))
		{
			for (i = 0; i <= len1 && i <= len2; i++)
			{
				if (buffer8[i] != str.buffer8[i])
					return i;
			}
		}
		else
		{
			for (i = 0; i <= len1 && i <= len2; i++)
			{
				if (toLower (buffer8[i]) != toLower (str.buffer8[i]))
					return i;
			}
		}
	}
	return -1;
}

//-----------------------------------------------------------------------------
bool ConstString::scanInt64 (int64& value, uint32 offset, bool scanToEnd) const
{
	if (isEmpty () || offset >= len)
		return false;

	if (isWide)
		return scanInt64_16 (buffer16 + offset, value, scanToEnd);
	return scanInt64_8 (buffer8 + offset, value, scanToEnd);
}

//-----------------------------------------------------------------------------
bool ConstString::scanUInt64 (uint64& value, uint32 offset, bool scanToEnd) const
{
	if (isEmpty () || offset >= len)
		return false;

	if (isWide)
		return scanUInt64_16 (buffer16 + offset, value, scanToEnd);
	return scanUInt64_8 (buffer8 + offset, value, scanToEnd);
}

//-----------------------------------------------------------------------------
bool ConstString::scanHex (uint8& value, uint32 offset, bool scanToEnd) const
{
	if (isEmpty () || offset >= len)
		return false;

	if (isWide)
		return scanHex_16 (buffer16 + offset, value, scanToEnd);
	return scanHex_8 (buffer8 + offset, value, scanToEnd);
}

//-----------------------------------------------------------------------------
bool ConstString::scanInt32 (int32& value, uint32 offset, bool scanToEnd) const
{
	if (isEmpty () || offset >= len)
		return false;

	if (isWide)
		return scanInt32_16 (buffer16 + offset, value, scanToEnd);
	return scanInt32_8 (buffer8 + offset, value, scanToEnd);
}

//-----------------------------------------------------------------------------
bool ConstString::scanUInt32 (uint32& value, uint32 offset, bool scanToEnd) const
{
	if (isEmpty () || offset >= len)
		return false;

	if (isWide)
		return scanUInt32_16 (buffer16 + offset, value, scanToEnd);
	return scanUInt32_8 (buffer8 + offset, value, scanToEnd);
}

//-----------------------------------------------------------------------------
bool ConstString::scanInt64_8 (const char8* text, int64& value, bool scanToEnd)
{	
	while (text && text[0])
	{
		if (sscanf (text, "%" FORMAT_INT64A, &value) == 1)
			return true;
		if (scanToEnd == false)
			return false;
		text++;
	}
	return false;
}

//-----------------------------------------------------------------------------
bool ConstString::scanInt64_16 (const char16* text, int64& value, bool scanToEnd)
{
	if (text && text[0])
	{
		String str (text);
		str.toMultiByte (kCP_Default);
		return scanInt64_8 (str, value, scanToEnd);
	}
	return false;
}

//-----------------------------------------------------------------------------
bool ConstString::scanUInt64_8 (const char8* text, uint64& value, bool scanToEnd)
{
	while (text && text[0])
	{
		if (sscanf (text, "%" FORMAT_UINT64A, &value) == 1)
			return true;
		if (scanToEnd == false)
			return false;
		text++;
	}
	return false;
}

//-----------------------------------------------------------------------------
bool ConstString::scanUInt64_16 (const char16* text, uint64& value, bool scanToEnd)
{
	if (text && text[0])
	{
		String str (text);
		str.toMultiByte (kCP_Default);
		return scanUInt64_8 (str, value, scanToEnd);
	}
	return false;
}

//-----------------------------------------------------------------------------
bool ConstString::scanInt64 (const tchar* text, int64& value, bool scanToEnd)
{
#ifdef UNICODE
	return scanInt64_16 (text, value,scanToEnd);
#else
	return scanInt64_8 (text, value, scanToEnd);
#endif
}

//-----------------------------------------------------------------------------
bool ConstString::scanUInt64 (const tchar* text, uint64& value, bool scanToEnd)
{
#ifdef UNICODE
	return scanUInt64_16 (text, value, scanToEnd);
#else
	return scanUInt64_8 (text, value, scanToEnd);
#endif
}

//-----------------------------------------------------------------------------
bool ConstString::scanHex_8 (const char8* text, uint8& value, bool scanToEnd)
{
	while (text && text[0])
	{
		unsigned int v; // scanf expects an unsigned int for %x
		if (sscanf (text, "%x", &v) == 1)
		{
			value = (uint8)v;
			return true;
		}
		if (scanToEnd == false)
			return false;
		text++;
	}
	return false;
}

//-----------------------------------------------------------------------------
bool ConstString::scanHex_16 (const char16* text, uint8& value, bool scanToEnd)
{
	if (text && text[0])
	{
		String str (text);
		str.toMultiByte (kCP_Default); // scanf uses default codepage
		return scanHex_8 (str, value, scanToEnd);
	}
	return false;
}

//-----------------------------------------------------------------------------
bool ConstString::scanHex (const tchar* text, uint8& value, bool scanToEnd)
{
#ifdef UNICODE
	return scanHex_16 (text, value, scanToEnd);
#else
	return scanHex_8 (text, value, scanToEnd);
#endif
}

//-----------------------------------------------------------------------------
bool ConstString::scanFloat (double& value, uint32 offset, bool scanToEnd) const
{
	if (isEmpty () || offset >= len)
		return false;

	String str (*this);
	int32 pos = -1;
	if (isWide)
	{
		if ((pos = str.findNext (offset, STR(','))) >= 0 && ((uint32)pos) >= offset)
			str.setChar (pos, STR('.'));

		str.toMultiByte (kCP_Default); // scanf uses default codepage
	}
	else
	{
		if ((pos = str.findNext (offset, ',')) >= 0 && ((uint32)pos) >= offset)
			str.setChar (pos, '.');
	}

	const char8* txt = str.text8 () + offset;
	while (txt && txt[0])
	{
		if (sscanf (txt, "%lf", &value) == 1)
			return true;
		if (scanToEnd == false)
			return false;
		txt++;
	}
	return false;
}

//-----------------------------------------------------------------------------
char16 ConstString::toLower (char16 c)
{
	#if SMTG_OS_WINDOWS
		WCHAR temp[2] = {c, 0};
        ::CharLowerW (temp);
        return temp[0];
	#elif SMTG_OS_MACOS
		// only convert characters which in lowercase are also single characters
		UniChar characters [2] = {0};
		characters[0] = c;
		CFMutableStringRef str = CFStringCreateMutableWithExternalCharactersNoCopy (kCFAllocator, characters, 1, 2, kCFAllocatorNull);
		if (str)
		{
			CFStringLowercase (str, NULL);
			CFRelease (str);
			if (characters[1] == 0)
				return characters[0];
		}
		return c;
	#elif SMTG_OS_LINUX
	assert (false && "DEPRECATED No Linux implementation");
		return c;
	#else
		return towlower (c);
	#endif
}

//-----------------------------------------------------------------------------
char16 ConstString::toUpper (char16 c)
{
	#if SMTG_OS_WINDOWS
		WCHAR temp[2] = {c, 0};
        ::CharUpperW (temp);
        return temp[0];
	#elif SMTG_OS_MACOS
		// only convert characters which in uppercase are also single characters (don't translate a sharp-s which would result in SS)
		UniChar characters [2] = {0};
		characters[0] = c;
		CFMutableStringRef str = CFStringCreateMutableWithExternalCharactersNoCopy (kCFAllocator, characters, 1, 2, kCFAllocatorNull);
		if (str)
		{
			CFStringUppercase (str, NULL);
			CFRelease (str);
			if (characters[1] == 0)
				return characters[0];
		}
		return c;
    #elif SMTG_OS_LINUX
	assert (false && "DEPRECATED No Linux implementation");
		return c;
	#else
		return towupper (c);
	#endif
}

//-----------------------------------------------------------------------------
char8 ConstString::toLower (char8 c)
{
	if ((c >= 'A') && (c <= 'Z'))
		return c + ('a' - 'A');
	#if SMTG_OS_WINDOWS
		CHAR temp[2] = {c, 0};
        ::CharLowerA (temp);
        return temp[0];
	#else
		return static_cast<char8> (tolower (c));
	#endif
}

//-----------------------------------------------------------------------------
char8 ConstString::toUpper (char8 c)
{
	if ((c >= 'a') && (c <= 'z'))
		return c - ('a' - 'A');
	#if SMTG_OS_WINDOWS
		CHAR temp[2] = {c, 0};
        ::CharUpperA (temp);
        return temp[0];
	#else
		return static_cast<char8> (toupper (c));
	#endif
}

//-----------------------------------------------------------------------------
bool ConstString::isCharSpace (const char8 character)
{
	return isspace (character) != 0;
}

//-----------------------------------------------------------------------------
bool ConstString::isCharSpace (const char16 character)
{
	switch (character)
	{
		case 0x0020:
		case 0x00A0:
		case 0x2002:
		case 0x2003:
		case 0x2004:
		case 0x2005:
		case 0x2006:
		case 0x2007:
		case 0x2008:
		case 0x2009:
		case 0x200A:
		case 0x200B:
		case 0x202F:
		case 0x205F:
		case 0x3000:
			return true;
	}
	return false;
}

//-----------------------------------------------------------------------------
bool ConstString::isCharAlpha (const char8 character)
{
	return isalpha (character) != 0;
}

//-----------------------------------------------------------------------------
bool ConstString::isCharAlpha (const char16 character)
{
	return iswalpha (character) != 0;
}

//-----------------------------------------------------------------------------
bool ConstString::isCharAlphaNum (const char8 character)
{
	return isalnum (character) != 0;
}

//-----------------------------------------------------------------------------
bool ConstString::isCharAlphaNum (const char16 character)
{
	return iswalnum (character) != 0; // this may not work on macOSX when another locale is set inside the c-lib
}

//-----------------------------------------------------------------------------
bool ConstString::isCharDigit (const char8 character)
{
	return isdigit (character) != 0;
}

//-----------------------------------------------------------------------------
bool ConstString::isCharDigit (const char16 character)
{
	return iswdigit (character) != 0;	// this may not work on macOSX when another locale is set inside the c-lib
}

//-----------------------------------------------------------------------------
bool ConstString::isCharAscii (char8 character)
{
	return character >= 0;
}

//-----------------------------------------------------------------------------
bool ConstString::isCharAscii (char16 character)
{
	return character < 128;
}

//-----------------------------------------------------------------------------
bool ConstString::isCharUpper (char8 character)
{
	return toUpper (character) == character;
}

//-----------------------------------------------------------------------------
bool ConstString::isCharUpper (char16 character)
{
	return toUpper (character) == character;
}

//-----------------------------------------------------------------------------
bool ConstString::isCharLower (char8 character)
{
	return toLower (character) == character;
}

//-----------------------------------------------------------------------------
bool ConstString::isCharLower (char16 character)
{
	return toLower (character) == character;
}

//-----------------------------------------------------------------------------
bool ConstString::isDigit (uint32 index) const
{
	if (isEmpty () || index >= len)
		return false;

	if (isWide)
		return ConstString::isCharDigit (buffer16[index]);
	return ConstString::isCharDigit (buffer8[index]);
}

//-----------------------------------------------------------------------------
int32 ConstString::getTrailingNumberIndex (uint32 width) const
{
	if (isEmpty ())
		return -1;

	int32 endIndex = len - 1;
	int32 i = endIndex;
	while (isDigit ((uint32) i) && i >= 0)
		i--;

	// now either all are digits or i is on the first non digit
	if (i < endIndex)
	{
		if (width > 0 && (endIndex - i != static_cast<int32> (width)))
			return -1;

		return i + 1;
	}

	return -1;
}

//-----------------------------------------------------------------------------
int64 ConstString::getTrailingNumber (int64 fallback) const
{
	int32 index = getTrailingNumberIndex ();

	int64 number = 0;

	if (index >= 0)
		if (scanInt64 (number, index))
			return number;

	return fallback;
}



//-----------------------------------------------------------------------------
void ConstString::toVariant (FVariant& var) const
{
	if (isWide)
	{
		var.setString16 (buffer16);
	}
	else
	{
		var.setString8 (buffer8);
	}
}

//-----------------------------------------------------------------------------
bool ConstString::isAsciiString () const
{
	uint32 i;
	if (isWide)
	{
		for (i = 0; i < len; i++)
			if (ConstString::isCharAscii (buffer16 [i]) == false)
				return false;
	}
	else
	{
		for (i = 0; i < len; i++)
			if (ConstString::isCharAscii (buffer8 [i]) == false)
				return false;
	}
	return true;
}


#if SMTG_OS_MACOS
uint32 kDefaultSystemEncoding = kCFStringEncodingMacRoman;
//-----------------------------------------------------------------------------
static CFStringEncoding MBCodePageToCFStringEncoding (uint32 codePage)
{
	switch (codePage)
	{
		case kCP_ANSI:		return kDefaultSystemEncoding; // MacRoman or JIS
		case kCP_MAC_ROMAN:	return kCFStringEncodingMacRoman;
		case kCP_ANSI_WEL:	return kCFStringEncodingWindowsLatin1;
		case kCP_MAC_CEE:	return kCFStringEncodingMacCentralEurRoman;
		case kCP_Utf8:		return kCFStringEncodingUTF8;
		case kCP_ShiftJIS:	return kCFStringEncodingShiftJIS_X0213_00;
		case kCP_US_ASCII:	return kCFStringEncodingASCII;
	}
	return kCFStringEncodingASCII;
}
#endif

//-----------------------------------------------------------------------------
int32 ConstString::multiByteToWideString (char16* dest, const char8* source, int32 charCount, uint32 sourceCodePage)
{
	if (source == nullptr || source[0] == 0)
	{
		if (dest && charCount > 0)
		{
			dest[0] = 0;
		}
		return 0;
	}
	int32 result = 0;
#if SMTG_OS_WINDOWS
	result = MultiByteToWideChar (sourceCodePage, MB_ERR_INVALID_CHARS, source, -1, wscast (dest), charCount);
#endif

#if SMTG_OS_MACOS
	CFStringRef cfStr =
	    (CFStringRef)::toCFStringRef (source, MBCodePageToCFStringEncoding (sourceCodePage));
	if (cfStr)
	{
		CFRange range = {0, CFStringGetLength (cfStr)};
		CFIndex usedBytes;
		if (CFStringGetBytes (cfStr, range, kCFStringEncodingUnicode, ' ', false, (UInt8*)dest,
		                      charCount * 2, &usedBytes) > 0)
		{
			result = static_cast<int32> (usedBytes / 2 + 1);
			if (dest)
				dest[usedBytes / 2] = 0;
		}

		CFRelease (cfStr);
	}
#endif

#if SMTG_OS_LINUX
	if (sourceCodePage == kCP_ANSI || sourceCodePage == kCP_US_ASCII || sourceCodePage == kCP_Utf8)
	{
		if (dest == nullptr)
		{
			auto state = std::mbstate_t ();
			auto maxChars = charCount ? charCount : std::numeric_limits<int32>::max () - 1;
			result = converterFacet ().length (state, source, source + strlen (source), maxChars);
		}
		else
		{
			auto utf16Str = converter ().from_bytes (source);
			if (!utf16Str.empty ())
			{
				result = std::min<int32> (charCount, utf16Str.size ());
				memcpy (dest, utf16Str.data (), result * sizeof (char16));
				dest[result] = 0;
			}
		}
	}
	else
	{
		assert (false && "DEPRECATED No Linux implementation");
	}

#endif

	SMTG_ASSERT (result > 0)
	return result;
}

//-----------------------------------------------------------------------------
int32 ConstString::wideStringToMultiByte (char8* dest, const char16* wideString, int32 charCount, uint32 destCodePage)
{
#if SMTG_OS_WINDOWS
	return WideCharToMultiByte (destCodePage, 0, wscast (wideString), -1, dest, charCount, nullptr, nullptr);

#elif SMTG_OS_MACOS
	int32 result = 0;
	if (wideString != 0)
	{
		if (dest)
		{
			CFStringRef cfStr = CFStringCreateWithCharactersNoCopy (kCFAllocator, (const UniChar*)wideString, strlen16 (wideString), kCFAllocatorNull);
			if (cfStr)
			{
				if (fromCFStringRef (dest, charCount, cfStr, MBCodePageToCFStringEncoding (destCodePage)))
					result = static_cast<int32> (strlen (dest) + 1);
				CFRelease (cfStr);
			}
		}
		else
		{
			return static_cast<int32> (CFStringGetMaximumSizeForEncoding (strlen16 (wideString), MBCodePageToCFStringEncoding (destCodePage)));
		}
	}
	return result;

#elif SMTG_OS_LINUX
	int32 result = 0;
	if (destCodePage == kCP_Utf8)
	{
		if (dest == nullptr)
		{
			auto maxChars = charCount ? charCount : tstrlen (wideString);
			result = converterFacet ().max_length () * maxChars;
		}
		else
		{
			auto utf8Str = converter ().to_bytes (wideString);
			if (!utf8Str.empty ())
			{
				result = std::min<int32> (charCount, utf8Str.size ());
				memcpy (dest, utf8Str.data (), result * sizeof (char8));
				dest[result] = 0;
			}
		}
	}
	else if (destCodePage == kCP_ANSI || destCodePage == kCP_US_ASCII)
	{
		if (dest == nullptr)
		{
			result = strlen16 (wideString) + 1;
		}
		else
		{
			int32 i = 0;
			for (; i < charCount; ++i)
			{
				if (wideString[i] == 0)
					break;
				if (wideString[i] <= 0x007F)
					dest[i] = wideString[i];
				else
					dest[i] = '_';
			}
			dest[i] = 0;
			result = i;
		}
	}
	else
	{
		assert (false && "DEPRECATED No Linux implementation");
	}
	return result;

#else
	assert (false && "DEPRECATED No Linux implementation");
	return 0;
#endif

}

//-----------------------------------------------------------------------------
bool ConstString::isNormalized (UnicodeNormalization n)
{
	if (isWide == false)
		return false;

#if SMTG_OS_WINDOWS
#ifdef UNICODE
	if (n != kUnicodeNormC)
		return false;
	uint32 normCharCount = static_cast<uint32> (FoldString (MAP_PRECOMPOSED, wscast (buffer16), len, nullptr, 0));
	return (normCharCount == len);
#else
	return false; 
#endif

#elif SMTG_OS_MACOS
	if (n != kUnicodeNormC)
		return false;

	CFStringRef cfStr = (CFStringRef)toCFStringRef ();
	CFIndex charCount = CFStringGetLength (cfStr);
	CFRelease (cfStr);
	return (charCount == len);
#else
	return false;
#endif
}

//-----------------------------------------------------------------------------
//	String
//-----------------------------------------------------------------------------
String::String ()
{
	isWide = kWideStringDefault ? 1 : 0;
}

//-----------------------------------------------------------------------------
String::String (const char8* str, MBCodePage codePage, int32 n, bool isTerminated)
{
	isWide = false;
	if (str)
	{
		if (isTerminated && n >= 0 && str[n] != 0)
		{
			// isTerminated is not always set correctly
			isTerminated = false;
		}

		if (!isTerminated)
		{
			assign (str, n, isTerminated);
			toWideString (codePage);
		}
		else
		{
			if (n < 0)
				n = static_cast<int32> (strlen (str));
			if (n > 0)
				_toWideString (str, n, codePage);
		}
	}
}

//-----------------------------------------------------------------------------
String::String (const char8* str, int32 n, bool isTerminated)
{
	if (str)
		assign (str, n, isTerminated);
}

//-----------------------------------------------------------------------------
String::String (const char16* str, int32 n, bool isTerminated)
{
	isWide = 1;
	if (str)
		assign (str, n, isTerminated);
}

//-----------------------------------------------------------------------------
String::String (const String& str, int32 n)
{
	isWide = str.isWideString ();
	if (!str.isEmpty ())
		assign (str, n);
}

//-----------------------------------------------------------------------------
String::String (const ConstString& str, int32 n)
{
	isWide = str.isWideString ();
	if (!str.isEmpty ())
		assign (str, n);
}

//-----------------------------------------------------------------------------
String::String (const FVariant& var)
{
	isWide = kWideStringDefault ? 1 : 0;
	fromVariant (var);
}

//-----------------------------------------------------------------------------
String::String (IString* str)
{
	isWide = str->isWideString ();
	if (isWide)
		assign (str->getText16 ());
	else
		assign (str->getText8 ());
}

//-----------------------------------------------------------------------------
String::~String ()
{
	if (buffer)
		resize (0, false);
}

#if SMTG_CPP11_STDLIBSUPPORT
//-----------------------------------------------------------------------------
String::String (String&& str)
{
	*this = std::move (str);
}

//-----------------------------------------------------------------------------
String& String::operator= (String&& str)
{
	SMTG_ASSERT (buffer == nullptr || buffer != str.buffer);
	tryFreeBuffer ();
	
	isWide = str.isWide;
	buffer = str.buffer;
	len = str.len;
	str.buffer = nullptr;
	str.len = 0;
	return *this;
}
#endif

//-----------------------------------------------------------------------------
void String::updateLength ()
{
	if (isWide)
		len = strlen16 (text16 ());
	else
		len = strlen8 (text8 ());
}

//-----------------------------------------------------------------------------
bool String::toWideString (uint32 sourceCodePage)
{
	if (!isWide && buffer8 && len > 0)
		return _toWideString (buffer8, len, sourceCodePage);
	isWide = true;
	return true;
}

//-----------------------------------------------------------------------------
bool String::_toWideString (const char8* src, int32 length, uint32 sourceCodePage)
{
	if (!isWide)
	{
		if (src && length > 0)
		{
			int32 bytesNeeded = multiByteToWideString (nullptr, src, 0, sourceCodePage) * sizeof (char16);
			if (bytesNeeded)
			{
				bytesNeeded += sizeof (char16);
				char16* newStr = (char16*)malloc (bytesNeeded);
				if (multiByteToWideString (newStr, src, length + 1, sourceCodePage) < 0)
				{
					free (newStr);
					return false;
				}
				if (buffer8)
					free (buffer8);

				buffer16 = newStr;
				isWide = true;
				updateLength ();
			}
			else
			{
				return false;
			}
		}
		isWide = true;
	}
	return true;
}

#define SMTG_STRING_CHECK_CONVERSION 1
#define SMTG_STRING_CHECK_CONVERSION_NO_BREAK 1

#if SMTG_STRING_CHECK_CONVERSION_NO_BREAK
	#define SMTG_STRING_CHECK_MSG FDebugPrint
#else
	#define SMTG_STRING_CHECK_MSG FDebugBreak
#endif
//-----------------------------------------------------------------------------
bool String::checkToMultiByte (uint32 destCodePage) const
{
	if (!isWide || isEmpty ())
		return true;

#if DEVELOPMENT && SMTG_STRING_CHECK_CONVERSION
	int debugLen = length ();
	int debugNonASCII = 0;
	for (int32 i = 0; i < length (); i++)
	{
		if (buffer16[i] > 127)
			++debugNonASCII;
	}
	
	String* backUp = nullptr;
	if (debugNonASCII > 0)
		backUp = NEW String (*this);
#endif

	// this should be avoided, since it can lead to information loss
	bool result = const_cast <String&> (*this).toMultiByte (destCodePage);

#if DEVELOPMENT && SMTG_STRING_CHECK_CONVERSION
	if (backUp)
	{
		String temp (*this);
		temp.toWideString (destCodePage);
		
		if (temp != *backUp)
		{
			backUp->toMultiByte (kCP_Utf8);
			SMTG_STRING_CHECK_MSG ("Indirect string conversion information loss !   %d/%d non ASCII chars:   \"%s\"   ->    \"%s\"\n", debugNonASCII, debugLen, backUp->buffer8, buffer8);
		}
		else
			SMTG_STRING_CHECK_MSG ("Indirect string potential conversion information loss !   %d/%d non ASCII chars   result: \"%s\"\n", debugNonASCII, debugLen, buffer8);

		delete backUp;
	}
#endif

	return result;
}

//-----------------------------------------------------------------------------
bool String::toMultiByte (uint32 destCodePage)
{
	if (isWide)
	{
		if (buffer16 && len > 0)
		{
			int32 numChars = wideStringToMultiByte (nullptr, buffer16, 0, destCodePage) + sizeof (char8);
			char8* newStr = (char8*) malloc (numChars * sizeof (char8));
			if (wideStringToMultiByte (newStr, buffer16, numChars, destCodePage) <= 0)
			{
				free (newStr);
				return false;
			}
			free (buffer16);
			buffer8 = newStr;
			isWide = false;
			updateLength ();
		}
		isWide = false;
	}
	else if (destCodePage != kCP_Default)
	{
		if (toWideString () == false)
			return false;
		return toMultiByte (destCodePage);
	}
	return true;
}

//-----------------------------------------------------------------------------
void String::fromUTF8 (const char8* utf8String)
{
	if (buffer8 != utf8String)
		resize (0, false);
	_toWideString (utf8String, static_cast<int32> (strlen (utf8String)), kCP_Utf8);
}

//-----------------------------------------------------------------------------
bool String::normalize (UnicodeNormalization n)
{
	if (isWide == false)
		return false;

	if (buffer16 == nullptr)
		return true;

#if SMTG_OS_WINDOWS
#ifdef UNICODE
	if (n != kUnicodeNormC)
		return false;

	uint32 normCharCount = static_cast<uint32> (FoldString (MAP_PRECOMPOSED, wscast (buffer16), len, nullptr, 0));
	if (normCharCount == len)
		return true;

	char16* newString = (char16*)malloc ((normCharCount + 1) * sizeof (char16));
	uint32 converterCount = static_cast<uint32> (FoldString (MAP_PRECOMPOSED, wscast (buffer16), len, wscast (newString), normCharCount + 1));
	if (converterCount != normCharCount)
	{
		free (newString);
		return false;
	}
	newString [converterCount] = 0;
	free (buffer16);
	buffer16 = newString;
	updateLength ();
	return true;
#else
	return false;
#endif

#elif SMTG_OS_MACOS
	CFMutableStringRef origStr = (CFMutableStringRef)toCFStringRef (0xFFFF, true);
	if (origStr)
	{
		CFStringNormalizationForm normForm = kCFStringNormalizationFormD;
		switch (n)
		{
			case kUnicodeNormC: normForm = kCFStringNormalizationFormC; break;
			case kUnicodeNormD: normForm = kCFStringNormalizationFormD; break;
			case kUnicodeNormKC: normForm = kCFStringNormalizationFormKC; break;
			case kUnicodeNormKD: normForm = kCFStringNormalizationFormKD; break;
		}
		CFStringNormalize (origStr, normForm);
		bool result = fromCFStringRef (origStr);
		CFRelease (origStr);
		return result;
	}
	return false;
#else
	return false;
#endif
}

//-----------------------------------------------------------------------------
void String::tryFreeBuffer ()
{
	if (buffer)
	{
		free (buffer);
		buffer = nullptr;
	}
}

//-----------------------------------------------------------------------------
bool String::resize (uint32 newLength, bool wide, bool fill)
{
	if (newLength == 0)
	{
		tryFreeBuffer ();
		len = 0;
		isWide = wide ? 1 : 0;
	}
	else
	{
		size_t newCharSize = wide ? sizeof (char16) : sizeof (char8);
		size_t oldCharSize = (isWide != 0) ? sizeof (char16) : sizeof (char8);

		size_t newBufferSize = (newLength + 1) * newCharSize;
		size_t oldBufferSize = (len + 1) * oldCharSize;

		isWide = wide ? 1 : 0;

		if (buffer)
		{
			if (newBufferSize != oldBufferSize)
			{
				void* newstr = realloc (buffer, newBufferSize);
				if (newstr == nullptr)
					return false;
				buffer = newstr;
				if (isWide)
					buffer16[newLength] = 0;
				else
					buffer8[newLength] = 0;
			}
			else if (wide && newCharSize != oldCharSize)
				buffer16[newLength] = 0;
		}
		else
		{
			void* newstr = malloc (newBufferSize);
			if (newstr == nullptr)
				return false;
			buffer = newstr;
			if (isWide)
			{
				buffer16[0] = 0;
				buffer16[newLength] = 0;
			}
			else
			{
				buffer8[0] = 0;
				buffer8[newLength] = 0;
			}
		}

		if (fill && len < newLength && buffer)
		{
			if (isWide)
			{
				char16 c = ' ';	
				for (uint32 i = len; i < newLength; i++)
					buffer16 [i] = c;
			}
			else
			{
				memset (buffer8 + len, ' ', newLength - len);
			}
		}
	}
	return true;
}

//-----------------------------------------------------------------------------
bool String::setChar8 (uint32 index, char8 c)
{
	if (index == len && c == 0)
		return true;

	if (index >= len)
	{
		if (c == 0)
		{
			if (resize (index, isWide, true) == false)
				return false;
			len = index;
			return true;
		}
		
		if (resize (index + 1, isWide, true) == false)
			return false;
		len = index + 1;
	}
	
	if (index < len && buffer)
	{
		if (isWide)
		{
			if (c == 0)
				buffer16[index] = 0;
			else
			{
				char8 src[] = {c, 0};
				char16 dest[8] = {0};
				if (multiByteToWideString (dest, src, 2) > 0)
					buffer16[index] = dest[0];
			}
			SMTG_ASSERT (buffer16[len] == 0)
		}
		else
		{
			buffer8[index] = c;
			SMTG_ASSERT (buffer8[len] == 0)
		}

		if (c == 0)
			updateLength ();

		return true;
	}
	return false;
}

//-----------------------------------------------------------------------------
bool String::setChar16 (uint32 index, char16 c)
{
	if (index == len && c == 0)
		return true;

	if (index >= len)
	{
		if (c == 0)
		{
			if (resize (index, isWide, true) == false)
				return false;
			len = index;
			return true;
		}
		if (resize (index + 1, isWide, true) == false)
			return false;
		len = index + 1;
	}

	if (index < len && buffer)
	{
		if (isWide)
		{
			buffer16[index] = c;
			SMTG_ASSERT (buffer16[len] == 0)
		}
		else
		{
			SMTG_ASSERT (buffer8[len] == 0)
			char16 src[] = {c, 0};
			char8 dest[8] = {0};
			if (wideStringToMultiByte (dest, src, 2) > 0 && dest[1] == 0)
				buffer8[index] = dest[0];
			else
				return false;
		}

		if (c == 0)
			updateLength ();

		return true;
	}
	return false;
}

//-----------------------------------------------------------------------------
String& String::assign (const ConstString& str, int32 n)
{
	if (str.isWideString ())
		return assign (str.text16 (), n < 0 ? str.length () : n);
	return assign (str.text8 (), n < 0 ? str.length () : n);
}

//-----------------------------------------------------------------------------
String& String::assign (const char8* str, int32 n, bool isTerminated)
{
	if (str == buffer8)
		return *this;

	if (isTerminated)
	{
		uint32 stringLength = (uint32)((str) ? strlen (str) : 0);
		n = n < 0 ? stringLength : Min<uint32> (n, stringLength);
	}
	else if (n < 0)
		return *this;

	if (resize (n, false))
	{
		if (buffer8 && n > 0 && str)
		{
			memcpy (buffer8, str, n * sizeof (char8));
			SMTG_ASSERT (buffer8[n] == 0)
		}
		isWide = 0;
		len = n;
	}
	return *this;
}

//-----------------------------------------------------------------------------
String& String::assign (const char16* str, int32 n, bool isTerminated)
{
	if (str == buffer16)
		return *this;

	if (isTerminated)
	{
		uint32 stringLength = (uint32)((str) ? strlen16 (str) : 0);
		n = n < 0 ? stringLength : Min<uint32> (n, stringLength);
	}
	else if (n < 0)
		return *this;

	if (resize (n, true))
	{
		if (buffer16 && n > 0 && str)
		{
			memcpy (buffer16, str, n * sizeof (char16));
			SMTG_ASSERT (buffer16[n] == 0)
		}
		isWide = 1;
		len = n;
	}
	return *this;
}

//-----------------------------------------------------------------------------
String& String::assign (char8 c, int32 n)
{
	if (resize (n, false))
	{
		if (buffer8 && n > 0)
		{
			memset (buffer8, c, n * sizeof (char8));
			SMTG_ASSERT (buffer8[n] == 0)
		}
		isWide = 0;
		len = n;
	}
	return *this;

}

//-----------------------------------------------------------------------------
String& String::assign (char16 c, int32 n)
{
	if (resize (n, true))
	{
		if (buffer && n > 0)
		{
			for (int32 i = 0; i < n; i++)
				buffer16[i] = c;
			SMTG_ASSERT (buffer16[n] == 0)
		}
		isWide = 1;
		len = n;
	}
	return *this;
}

//-----------------------------------------------------------------------------
String& String::append (const ConstString& str, int32 n)
{
	if (str.isWideString ())
		return append (str.text16 (), n);
	return append (str.text8 (), n);
}

//-----------------------------------------------------------------------------
String& String::append (const char8* str, int32 n)
{
	if (str == buffer8)
		return *this;

	if (len == 0)
		return assign (str, n);

	if (isWide)
	{
		String tmp (str);
		if (tmp.toWideString () == false)
			return *this;

		return append (tmp.buffer16, n);
	}

	uint32 stringLength = (uint32)((str) ? strlen (str) : 0);
	n = n < 0 ? stringLength : Min<uint32> (n, stringLength);

	if (n > 0)
	{
		int32 newlen = n + len;
		if (!resize (newlen, false))
			return *this;

		if (buffer && str)
		{
			memcpy (buffer8 + len, str, n * sizeof (char8));
			SMTG_ASSERT (buffer8[newlen] == 0)
		}

		len += n;
	}
	return *this;
}

//-----------------------------------------------------------------------------
String& String::append (const char16* str, int32 n)
{
	if (str == buffer16)
		return *this;

	if (len == 0)
		return assign (str, n);

	if (!isWide)
	{
		if (toWideString () == false)
			return *this;
	}

	uint32 stringLength = (uint32)((str) ? strlen16 (str) : 0);
	n = n < 0 ? stringLength : Min<uint32> (n, stringLength);

	if (n > 0)
	{
		int32 newlen = n + len;
		if (!resize (newlen, true))
			return *this;

		if (buffer16 && str)
		{
			memcpy (buffer16 + len, str, n * sizeof (char16));
			SMTG_ASSERT (buffer16[newlen] == 0)
		}

		len += n;
	}
	return *this;
}

//-----------------------------------------------------------------------------
String& String::append (const char8 c, int32 n)
{
	char8 str[] = {c, 0};
	if (n == 1)
	{
		return append (str, 1);
	}
	if (n > 1)
	{
		if (isWide)
		{
			String tmp (str);
			if (tmp.toWideString () == false)
				return *this;

			return append (tmp.buffer16[0], n);
		}

		int32 newlen = n + len;
		if (!resize (newlen, false))
			return *this;

		if (buffer)
		{
			memset (buffer8 + len, c, n * sizeof (char8));
			SMTG_ASSERT (buffer8[newlen] == 0)
		}

		len += n;
	}
	return *this;
}

//-----------------------------------------------------------------------------
String& String::append (const char16 c, int32 n)
{
	if (n == 1)
	{
		char16 str[] = {c, 0};
		return append (str, 1);
	}
	if (n > 1)
	{
		if (!isWide)
		{
			if (toWideString () == false)
				return *this;
		}

		int32 newlen = n + len;
		if (!resize (newlen, true))
			return *this;

		if (buffer16)
		{
			for (int32 i = len; i < newlen; i++)
				buffer16[i] = c;
			SMTG_ASSERT (buffer16[newlen] == 0)
		}

		len += n;
	}
	return *this;
}

//-----------------------------------------------------------------------------
String& String::insertAt (uint32 idx, const ConstString& str, int32 n)
{
	if (str.isWideString ())
		return insertAt (idx, str.text16 (), n);
	return insertAt (idx, str.text8 (), n);
}

//-----------------------------------------------------------------------------
String& String::insertAt (uint32 idx, const char8* str, int32 n)
{
	if (idx > len)
		return *this;

	if (isWide)
	{
		String tmp (str);
		if (tmp.toWideString () == false)
			return *this;
		return insertAt (idx, tmp.buffer16, n);
	}

	uint32 stringLength = (uint32)((str) ? strlen (str) : 0);
	n = n < 0 ? stringLength : Min<uint32> (n, stringLength);

	if (n > 0)
	{
		int32 newlen = len + n;
		if (!resize (newlen, false))
			return *this;

		if (buffer && str)
		{
			if (idx < len)
				memmove (buffer8 + idx + n, buffer8 + idx, (len - idx) * sizeof (char8));
			memcpy (buffer8 + idx, str, n * sizeof (char8));
			SMTG_ASSERT (buffer8[newlen] == 0)
		}

		len += n;
	}
	return *this;
}

//-----------------------------------------------------------------------------
String& String::insertAt (uint32 idx, const char16* str, int32 n)
{
	if (idx > len)
		return *this;

	if (!isWide)
	{
		if (toWideString () == false)
			return *this;
	}

	uint32 stringLength = (uint32)((str) ? strlen16 (str) : 0);
	n = n < 0 ? stringLength : Min<uint32> (n, stringLength);

	if (n > 0)
	{
		int32 newlen = len + n;
		if (!resize (newlen, true))
			return *this;

		if (buffer && str)
		{
			if (idx < len)
				memmove (buffer16 + idx + n, buffer16 + idx, (len - idx) * sizeof (char16));
			memcpy (buffer16 + idx, str, n * sizeof (char16));
			SMTG_ASSERT (buffer16[newlen] == 0)
		}

		len += n;
	}
	return *this;
}

//-----------------------------------------------------------------------------
String& String::replace (uint32 idx, int32 n1, const ConstString& str, int32 n2)
{
	if (str.isWideString ())
		return replace (idx, n1, str.text16 (), n2);
	return replace (idx, n1, str.text8 (), n2);
}

// "replace" replaces n1 number of characters at the specified index with
// n2 characters from the specified string.
//-----------------------------------------------------------------------------
String& String::replace (uint32 idx, int32 n1, const char8* str, int32 n2)
{
	if (idx > len || str == nullptr)
		return *this;

	if (isWide)
	{
		String tmp (str);
		if (tmp.toWideString () == false)
			return *this;
		if (tmp.length () == 0 || n2 == 0)
			return remove (idx, n1);
		return replace (idx, n1, tmp.buffer16, n2);
	}

	if (n1 < 0 || idx + n1 > len)
		n1 = len - idx;
	if (n1 == 0)
		return *this;

	uint32 stringLength = (uint32)((str) ? strlen (str) : 0);
	n2 = n2 < 0 ? stringLength : Min<uint32> (n2, stringLength);

	uint32 newlen = len - n1 + n2;
	if (newlen > len)
		if (!resize (newlen, false))
			return *this;

	if (buffer)
	{
		memmove (buffer8 + idx + n2, buffer8 + idx + n1, (len - (idx + n1)) * sizeof (char8));
		memcpy (buffer8 + idx, str, n2 * sizeof (char8));
		buffer8[newlen] = 0;	// cannot be removed because resize is not called called in all cases (newlen > len)
	}

	len = newlen;

	return *this;
}

//-----------------------------------------------------------------------------
String& String::replace (uint32 idx, int32 n1, const char16* str, int32 n2)
{
	if (idx > len || str == nullptr)
		return *this;

	if (!isWide)
	{
		if (toWideString () == false)
			return *this;
	}

	if (n1 < 0 || idx + n1 > len)
		n1 = len - idx;
	if (n1 == 0)
		return *this;

	uint32 stringLength = (uint32)((str) ? strlen16 (str) : 0);
	n2 = n2 < 0 ? stringLength : Min<uint32> (n2, stringLength);

	uint32 newlen = len - n1 + n2;
	if (newlen > len)
		if (!resize (newlen, true))
			return *this;

	if (buffer)
	{
		memmove (buffer16 + idx + n2, buffer16 + idx + n1, (len - (idx + n1)) * sizeof (char16));
		memcpy (buffer16 + idx, str, n2 * sizeof (char16));
		buffer16[newlen] = 0;	// cannot be removed because resize is not called called in all cases (newlen > len)
	}

	len = newlen;

	return *this;
}

//-----------------------------------------------------------------------------
int32 String::replace (const char8* toReplace, const char8* toReplaceWith, bool all, CompareMode m)
{
	if (toReplace == nullptr || toReplaceWith == nullptr)
		return 0;

	int32 result = 0;

	int32 idx = findFirst (toReplace, -1, m);
	if (idx > -1)
	{
		int32 toReplaceLen = static_cast<int32> (strlen (toReplace));
		int32 toReplaceWithLen = static_cast<int32> (strlen (toReplaceWith));
		while (idx > -1)
		{
			replace (idx, toReplaceLen, toReplaceWith, toReplaceWithLen);
			result++;

			if (all)
				idx = findNext (idx + toReplaceWithLen , toReplace, -1, m);
			else
				break;
		}
	}

	return result;
}

//-----------------------------------------------------------------------------
int32 String::replace (const char16* toReplace, const char16* toReplaceWith, bool all, CompareMode m)
{
	if (toReplace == nullptr || toReplaceWith == nullptr)
		return 0;

	int32 result = 0;

	int32 idx = findFirst (toReplace, -1, m);
	if (idx > -1)
	{
		int32 toReplaceLen = strlen16 (toReplace);
		int32 toReplaceWithLen = strlen16 (toReplaceWith);
		while (idx > -1)
		{
			replace (idx, toReplaceLen, toReplaceWith, toReplaceWithLen);
			result++;

			if (all)
				idx = findNext (idx + toReplaceWithLen, toReplace, -1, m);
			else
				break;
		}
	}
	return result;
}

//-----------------------------------------------------------------------------
template <class T>
static bool performReplace (T* str, const T* toReplace, T toReplaceBy)
{
	bool anyReplace = false;
	T* p = str;
	while (*p)
	{
		const T* rep = toReplace;
		while (*rep)
		{
			if (*p == *rep)
			{
				*p = toReplaceBy;
				anyReplace = true;
				break;
			}
			rep++;
		}
		p++;
	}
	return anyReplace;
}

//-----------------------------------------------------------------------------
bool String::replaceChars8 (const char8* toReplace, char8 toReplaceBy)
{
	if (isEmpty ())
		return false;

	if (isWide)
	{
		String toReplaceW (toReplace);
		if (toReplaceW.toWideString () == false)
			return false;

		char8 src[] = {toReplaceBy, 0};
		char16 dest[2] = {0};
		if (multiByteToWideString (dest, src, 2) > 0)
		{
			return replaceChars16 (toReplaceW.text16 (), dest[0]);
		}
		return false;
	}

	if (toReplaceBy == 0)
		toReplaceBy = ' ';

	return performReplace<char8> (buffer8, toReplace, toReplaceBy);
}

//-----------------------------------------------------------------------------
bool String::replaceChars16 (const char16* toReplace, char16 toReplaceBy)
{
	if (isEmpty ())
		return false;

	if (!isWide)
	{
		String toReplaceA (toReplace);
		if (toReplaceA.toMultiByte () == false)
			return false;

		if (toReplaceA.length () > 1)
		{
			SMTG_WARNING("cannot replace non ASCII chars on non Wide String")
			return false;
		}

		char16 src[] = {toReplaceBy, 0};
		char8 dest[8] = {0};
		if (wideStringToMultiByte (dest, src, 2) > 0 && dest[1] == 0)
			return replaceChars8 (toReplaceA.text8 (), dest[0]);

		return false;
	}

	if (toReplaceBy == 0)
		toReplaceBy = STR16 (' ');

	return performReplace<char16> (buffer16, toReplace, toReplaceBy);
}

// "remove" removes the specified number of characters from the string
// starting at the specified index.
//-----------------------------------------------------------------------------
String& String::remove (uint32 idx, int32 n)
{
	if (isEmpty () || idx >= len || n == 0)
		return *this;

	if ((idx + n > len) || n < 0)
		n = len - idx;
	else
	{
		int32 toMove = len - idx - n;
		if (buffer)
		{
			if (isWide)
				memmove (buffer16 + idx, buffer16 + idx + n, toMove * sizeof (char16));
			else
				memmove (buffer8 + idx, buffer8 + idx + n, toMove * sizeof (char8));
		}
	}

	resize (len - n, isWide);
	updateLength ();

	return *this;
}

//-----------------------------------------------------------------------------
bool String::removeSubString (const ConstString& subString, bool allOccurences)
{
	bool removed = false;
	while (!removed || allOccurences)
	{
		int32 idx = findFirst (subString);
		if (idx < 0)
			break;
		remove (idx, subString.length ());
		removed = true;
	}
	return removed;
}

//-----------------------------------------------------------------------------
template <class T, class F>
static uint32 performTrim (T* str, uint32 length, F func, bool funcResult)
{
	uint32 toRemoveAtHead = 0;
	uint32 toRemoveAtTail = 0;

	T* p = str;

	while ((*p) && ((func (*p) != 0) == funcResult))
		p++;

	toRemoveAtHead = static_cast<uint32> (p - str);

	if (toRemoveAtHead < length)
	{
		p = str + length - 1;

		while (((func (*p) != 0) == funcResult) && (p > str))
		{
			p--;
			toRemoveAtTail++;
		}
	}

	uint32 newLength = length - (toRemoveAtHead + toRemoveAtTail);
	if (newLength != length)
	{
		if (toRemoveAtHead)
			memmove (str, str + toRemoveAtHead, newLength * sizeof (T));
	}
	return newLength;
}

// "trim" trims the leading and trailing unwanted characters from the string.
//-----------------------------------------------------------------------------
bool String::trim (String::CharGroup group)
{
	if (isEmpty ())
		return false;

	uint32 newLength;

	switch (group)
	{
		case kSpace:
			if (isWide)
				newLength = performTrim<char16> (buffer16, len, iswspace, true);
			else
				newLength = performTrim<char8> (buffer8, len, isspace, true);
			break;

		case kNotAlphaNum:
			if (isWide)
				newLength = performTrim<char16> (buffer16, len, iswalnum, false);
			else
				newLength = performTrim<char8> (buffer8, len, isalnum, false);
			break;

		case kNotAlpha:
			if (isWide)
				newLength = performTrim<char16> (buffer16, len, iswalpha, false);
			else
				newLength = performTrim<char8> (buffer8, len, isalpha, false);
			break;
            
        default: // Undefined enum value
            return false;
	}

	if (newLength != len)
	{
		resize (newLength, isWide);
		len = newLength;
		return true;
	}
	return false;
}

//-----------------------------------------------------------------------------
template <class T, class F>
static uint32 performRemove (T* str, uint32 length, F func, bool funcResult)
{
	T* p = str;

	while (*p)
	{
		if ((func (*p) != 0) == funcResult)
		{
			size_t toMove = length - (p - str);
			memmove (p, p + 1, toMove * sizeof (T));
			length--;
		}
		else
			p++;
	}
	return length;
}
//-----------------------------------------------------------------------------
void String::removeChars (CharGroup group)
{
	if (isEmpty ())
		return;

	uint32 newLength;

	switch (group)
	{
		case kSpace:
			if (isWide)
				newLength = performRemove<char16> (buffer16, len, iswspace, true);
			else
				newLength = performRemove<char8> (buffer8, len, isspace, true);
			break;

		case kNotAlphaNum:
			if (isWide)
				newLength = performRemove<char16> (buffer16, len, iswalnum, false);
			else
				newLength = performRemove<char8> (buffer8, len, isalnum, false);
			break;

		case kNotAlpha:
			if (isWide)
				newLength = performRemove<char16> (buffer16, len, iswalpha, false);
			else
				newLength = performRemove<char8> (buffer8, len, isalpha, false);
			break;
            
        default: // Undefined enum value
            return;
	}

	if (newLength != len)
	{
		resize (newLength, isWide);
		len = newLength;
	}
}

//-----------------------------------------------------------------------------
template <class T>
static uint32 performRemoveChars (T* str, uint32 length, const T* toRemove)
{
	T* p = str;

	while (*p)
	{
		bool found = false;
		const T* rem = toRemove;
		while (*rem)
		{
			if (*p == *rem)
			{
				found = true;
				break;
			}
			rem++;
		}

		if (found)
		{
			size_t toMove = length - (p - str);
			memmove (p, p + 1, toMove * sizeof (T));
			length--;
		}
		else
			p++;
	}
	return length;
}

//-----------------------------------------------------------------------------
bool String::removeChars8 (const char8* toRemove)
{
	if (isEmpty () || toRemove == nullptr)
		return true;

	if (isWide)
	{
		String wStr (toRemove);
		if (wStr.toWideString () == false)
			return false;
		return removeChars16 (wStr.text16 ());
	}

	uint32 newLength = performRemoveChars<char8> (buffer8, len, toRemove);

	if (newLength != len)
	{
		resize (newLength, false);
		len = newLength;
	}
	return true;
}

//-----------------------------------------------------------------------------
bool String::removeChars16 (const char16* toRemove)
{
	if (isEmpty () || toRemove == nullptr)
		return true;

	if (!isWide)
	{
		String str8 (toRemove);
		if (str8.toMultiByte () == false)
			return false;
		return removeChars8 (str8.text8 ());
	}

	uint32 newLength = performRemoveChars<char16> (buffer16, len, toRemove);

	if (newLength != len)
	{
		resize (newLength, true);
		len = newLength;
	}
	return true;
}

//-----------------------------------------------------------------------------
String& String::printf (const char8* format, ...)
{
	char8 string[kPrintfBufferSize];

	va_list marker;
	va_start (marker, format);
	
	vsnprintf (string, kPrintfBufferSize-1, format, marker);
	return assign (string);
}


//-----------------------------------------------------------------------------
String& String::printf (const char16* format, ...)
{
	char16 string[kPrintfBufferSize];

	va_list marker;
	va_start (marker, format);
	
	vsnwprintf (string, kPrintfBufferSize-1, format, marker);
	return assign (string);
}

//-----------------------------------------------------------------------------
String& String::vprintf (const char8* format, va_list args)
{
	char8 string[kPrintfBufferSize];

	vsnprintf (string, kPrintfBufferSize-1, format, args);
	return assign (string);
}

//-----------------------------------------------------------------------------
String& String::vprintf (const char16* format, va_list args)
{
	char16 string[kPrintfBufferSize];

	vsnwprintf (string, kPrintfBufferSize-1, format, args);
	return assign (string);
}

//-----------------------------------------------------------------------------
String& String::printInt64 (int64 value)
{
	if (isWide)
	{
	#if SMTG_CPP11
		return String::printf (STR("%") STR(FORMAT_INT64A), value);
	#else
		return String::printf (STR("%" FORMAT_INT64A), value);
	#endif
	}
	else
		return String::printf ("%" FORMAT_INT64A, value);
}

//-----------------------------------------------------------------------------
String& String::printFloat (double value, uint32 maxPrecision)
{
	static constexpr auto kMaxAfterCommaResolution = 16;
	// escape point for integer values, avoid unnecessary complexity later on
	const bool withinInt64Boundaries = value <= std::numeric_limits<int64>::max () && value >= std::numeric_limits<int64>::lowest ();
	if (withinInt64Boundaries && (maxPrecision == 0 || std::round (value) == value))
		return printInt64 (value);

	const auto absValue = std::abs (value);
	const uint32 valueExponent = absValue >= 1 ? std::log10 (absValue) : -std::log10 (absValue) + 1;

	maxPrecision = std::min<uint32> (kMaxAfterCommaResolution - valueExponent, maxPrecision);

	if (isWide)
		printf (STR ("%s%dlf"), STR ("%."), maxPrecision);
	else
		printf ("%s%dlf", "%.", maxPrecision);

	if (isWide)
		printf (text16 (), value);
	else
		printf (text8 (), value);

	// trim trail zeros
	for (int32 i = length () - 1; i >= 0; i--)
	{
		if (isWide && testChar16 (i, '0') || testChar8 (i, '0'))
			remove (i);
		else if (isWide && testChar16(i,'.') || testChar8(i, '.'))
		{
			remove(i);
			break;
		}
		else
			break;
	}

	return *this;
}

//-----------------------------------------------------------------------------
bool String::incrementTrailingNumber (uint32 width, tchar separator, uint32 minNumber, bool applyOnlyFormat)
{
	if (width > 32)
		return false;

	int64 number = 1;
	int32 index = getTrailingNumberIndex ();
	if (index >= 0)
	{
		if (scanInt64 (number, index))
			if (!applyOnlyFormat)
				number++;

		if (separator != 0 && index > 0 && testChar (index - 1, separator) == true)
			index--;

		remove (index);
	}

	if (number < minNumber)
		number = minNumber;

	if (isWide)
	{
		char16 format[64];
		char16 trail[128];
		if (separator && isEmpty () == false)
		{
			sprintf16 (format, STR16 ("%%c%%0%uu"), width);
			sprintf16 (trail, format, separator, (uint32) number);
		}
		else
		{
			sprintf16 (format, STR16 ("%%0%uu"), width);
			sprintf16 (trail, format, (uint32) number);
		}
		append (trail);
	}
	else
	{
		static constexpr auto kFormatSize = 64u;
		static constexpr auto kTrailSize = 64u;
		char format[kFormatSize];
		char trail[kTrailSize];
		if (separator && isEmpty () == false)
		{
			snprintf (format, kFormatSize, "%%c%%0%uu", width);
			snprintf (trail, kTrailSize, format, separator, (uint32) number);
		}
		else
		{
			snprintf (format, kFormatSize, "%%0%uu", width);
			snprintf (trail, kTrailSize, format, (uint32) number);
		}
		append (trail);
	}

	return true;
}

//-----------------------------------------------------------------------------
void String::toLower (uint32 index)
{
	if (buffer && index < len)
	{
		if (isWide)
			buffer16[index] = ConstString::toLower (buffer16[index]);
		else
			buffer8[index] = ConstString::toLower (buffer8[index]);
	}
}

//-----------------------------------------------------------------------------
void String::toLower ()
{
	int32 i = len;
	if (buffer && i > 0)
	{
		if (isWide)
		{
#if SMTG_OS_MACOS
			CFMutableStringRef cfStr = CFStringCreateMutableWithExternalCharactersNoCopy (kCFAllocator, (UniChar*)buffer16, len, len+1, kCFAllocatorNull);
			CFStringLowercase (cfStr, NULL);
			CFRelease (cfStr);
#else
			char16* c = buffer16;
			while (i--)
			{
				*c = ConstString::toLower (*c);
				c++;
			}
#endif
		}
		else
		{
			char8* c = buffer8;
			while (i--)
			{
				*c = ConstString::toLower (*c);
				c++;
			}
		}
	}
}

//-----------------------------------------------------------------------------
void String::toUpper (uint32 index)
{
	if (buffer && index < len)
	{
		if (isWide)
			buffer16[index] = ConstString::toUpper (buffer16[index]);
		else
			buffer8[index] = ConstString::toUpper (buffer8[index]);
	}
}

//-----------------------------------------------------------------------------
void String::toUpper ()
{
	int32 i = len;
	if (buffer && i > 0)
	{
		if (isWide)
		{
#if SMTG_OS_MACOS
			CFMutableStringRef cfStr = CFStringCreateMutableWithExternalCharactersNoCopy (kCFAllocator, (UniChar*)buffer16, len, len+1, kCFAllocatorNull);
			CFStringUppercase (cfStr, NULL);
			CFRelease (cfStr);
#else
			char16* c = buffer16;
			while (i--)
			{
				*c = ConstString::toUpper (*c);
				c++;
			}
#endif
		}
		else
		{
			char8* c = buffer8;
			while (i--)
			{
				*c = ConstString::toUpper (*c);
				c++;
			}
		}
	}
}

//-----------------------------------------------------------------------------
bool String::fromVariant (const FVariant& var)
{
	switch (var.getType ())
	{
		case FVariant::kString8:
			assign (var.getString8 ());
			return true;

		case FVariant::kString16:
			assign (var.getString16 ());
			return true;

		case FVariant::kFloat:
			printFloat (var.getFloat ());
			return true;

		case FVariant::kInteger:
			printInt64 (var.getInt ());
			return true;

		default:
			remove ();
	}
	return false;
}

//-----------------------------------------------------------------------------
void String::toVariant (FVariant& var) const
{
	if (isWide)
	{
		var.setString16 (text16 ());
	}
	else
	{
		var.setString8 (text8 ());
	}
}

//-----------------------------------------------------------------------------
bool String::fromAttributes (IAttributes* a, IAttrID attrID)
{
	FVariant variant;
	if (a->get (attrID, variant) == kResultTrue)
		return fromVariant (variant);
	return false;
}

//-----------------------------------------------------------------------------
bool String::toAttributes (IAttributes* a, IAttrID attrID)
{
	FVariant variant;
	toVariant (variant);
	if (a->set (attrID, variant) == kResultTrue)
		return true;
	return false;
}

// "swapContent" swaps ownership of the strings pointed to
//-----------------------------------------------------------------------------
void String::swapContent (String& s)
{
	void* tmp = s.buffer;
	uint32 tmpLen = s.len;
	bool tmpWide = s.isWide;
	s.buffer = buffer;
	s.len = len;
	s.isWide = isWide;
	buffer = tmp;
	len = tmpLen;
	isWide = tmpWide;
}

//-----------------------------------------------------------------------------
void String::take (String& other)
{
	resize (0, other.isWide);
	buffer = other.buffer;
	len = other.len;

	other.buffer = nullptr;
	other.len = 0;
}

//-----------------------------------------------------------------------------
void String::take (void* b, bool wide)
{
	resize (0, wide);
	buffer = b;
	isWide = wide;
	updateLength ();
}

//-----------------------------------------------------------------------------
void* String::pass ()
{
	void* res = buffer;
	len = 0;
	buffer = nullptr;
	return res;
}

//-----------------------------------------------------------------------------
void String::passToVariant (FVariant& var)
{
	void* passed = pass ();

	if (isWide)
	{
		if (passed)
		{
			var.setString16 ((const char16*)passed);
			var.setOwner (true);
		}
		else
			var.setString16 (kEmptyString16);
	}
	else
	{
		if (passed)
		{
			var.setString8 ((const char8*)passed);
			var.setOwner (true);
		}
		else
			var.setString8 (kEmptyString8);
	}
}


//-----------------------------------------------------------------------------
unsigned char* String::toPascalString (unsigned char* buf)
{
	if (buffer)
	{
		if (isWide)
		{
			String tmp (*this);
			tmp.toMultiByte ();
			return tmp.toPascalString (buf);
		}

		int32 length = len;
		if (length > 255)
			length = 255;
		buf[0] = (uint8)length;
		while (length >= 0)
		{
			buf[length + 1] = buffer8[length];
			length--;
		}
		return buf;
	}
	
	*buf = 0;
	return buf;
}

//-----------------------------------------------------------------------------
const String& String::fromPascalString (const unsigned char* buf)
{
	resize (0, false);
	isWide = 0;
	int32 length = buf[0];
	resize (length + 1, false);
	buffer8[length] = 0;	// cannot be removed, because we only do the 0-termination for multibyte buffer8
	while (--length >= 0)
		buffer8[length] = buf[length + 1];
	len = buf[0];
	return *this;
}

#if SMTG_OS_MACOS

//-----------------------------------------------------------------------------
bool String::fromCFStringRef (const void* cfStr, uint32 encoding)
{
	if (cfStr == 0)
		return false;

	CFStringRef strRef = (CFStringRef)cfStr;
	if (isWide)
	{
		CFRange range = { 0, CFStringGetLength (strRef)};
		CFIndex usedBytes;
		if (resize (static_cast<int32> (range.length + 1), true))
		{
			if (encoding == 0xFFFF)
				encoding = kCFStringEncodingUnicode;
			if (CFStringGetBytes (strRef, range, encoding, ' ', false, (UInt8*)buffer16, range.length * 2, &usedBytes) > 0)
			{
				buffer16[usedBytes/2] = 0;
				this->len = strlen16 (buffer16);
				return true;
			}
		}
	}
	else
	{
		if (cfStr == 0)
			return false;
		if (encoding == 0xFFFF)
			encoding = kCFStringEncodingASCII;
		int32 len = static_cast<int32> (CFStringGetLength (strRef) * 2);
		if (resize (++len, false))
		{
			if (CFStringGetCString (strRef, buffer8, len, encoding))
			{
				this->len = static_cast<int32> (strlen (buffer8));
				return true;
			}
		}
	}

	return false;
}

//-----------------------------------------------------------------------------
void* ConstString::toCFStringRef (uint32 encoding, bool mutableCFString) const
{
	if (mutableCFString)
	{
		CFMutableStringRef str = CFStringCreateMutable (kCFAllocator, 0);
		if (isWide)
		{
			CFStringAppendCharacters (str, (const UniChar *)buffer16, len);
			return str;
		}
		else
		{
			if (encoding == 0xFFFF)
				encoding = kCFStringEncodingASCII;
			CFStringAppendCString (str, buffer8, encoding);
			return str;
		}
	}
	else
	{
		if (isWide)
		{
			if (encoding == 0xFFFF)
				encoding = kCFStringEncodingUnicode;
			return (void*)CFStringCreateWithBytes (kCFAllocator, (const unsigned char*)buffer16, len * 2, encoding, false);
		}
		else
		{
			if (encoding == 0xFFFF)
				encoding = kCFStringEncodingASCII;
			if (buffer8)
				return (void*)CFStringCreateWithCString (kCFAllocator, buffer8, encoding);
			else
				return (void*)CFStringCreateWithCString (kCFAllocator, "", encoding);
		}
	}
	return nullptr;
}

#endif

//-----------------------------------------------------------------------------
uint32 hashString8 (const char8* s, uint32 m)
{
	uint32 h = 0;
	if (s)
	{
		for (h = 0; *s != '\0'; s++)
			h = (64 * h + *s) % m;
	}
	return h;
}

//-----------------------------------------------------------------------------
uint32 hashString16 (const char16* s, uint32 m)
{
	uint32 h = 0;
	if (s)
	{
		for (h = 0; *s != 0; s++)
			h = (64 * h + *s) % m;
	}
	return h;
}

//------------------------------------------------------------------------
template <class T> int32 tstrnatcmp (const T* s1, const T* s2, bool caseSensitive = true)
{
	if (s1 == nullptr && s2 == nullptr)
		return 0;
	if (s1 == nullptr)
		return -1;
	if (s2 == nullptr)
		return 1;

	while (*s1 && *s2)
	{
		if (ConstString::isCharDigit (*s1) && ConstString::isCharDigit (*s2))
		{
			int32 s1LeadingZeros = 0;
			while (*s1 == '0')
			{
				s1++; // skip leading zeros
				s1LeadingZeros++;
			}
			int32 s2LeadingZeros = 0;
			while (*s2 == '0')
			{
				s2++; // skip leading zeros
				s2LeadingZeros++;
			}

			int32 countS1Digits = 0;
			while (*(s1 + countS1Digits) && ConstString::isCharDigit (*(s1 + countS1Digits)))
				countS1Digits++;
			int32 countS2Digits = 0;
			while (*(s2 + countS2Digits) && ConstString::isCharDigit (*(s2 + countS2Digits)))
				countS2Digits++;

			if (countS1Digits != countS2Digits)
				return countS1Digits - countS2Digits; // one number is longer than the other

			for (int32 i = 0; i < countS1Digits; i++)
			{
				// countS1Digits == countS2Digits
				if (*s1 != *s2)
					return (int32)(*s1 - *s2); // the digits differ
				s1++;
				s2++;
			}

			if (s1LeadingZeros != s2LeadingZeros)
				return s1LeadingZeros - s2LeadingZeros; // differentiate by the number of leading zeros
		}
		else
		{
			if (caseSensitive == false)
			{
				T srcToUpper = static_cast<T> (toupper (*s1));
				T dstToUpper = static_cast<T> (toupper (*s2));
				if (srcToUpper != dstToUpper)
					return (int32)(srcToUpper - dstToUpper);
			}
			else if (*s1 != *s2)
				return (int32)(*s1 - *s2);

			s1++;
			s2++;
		}
	}

	if (*s1 == 0 && *s2 == 0)
		return 0;
	if (*s1 == 0)
		return -1;
	if (*s2 == 0)
		return 1;
	return (int32)(*s1 - *s2);
}

//------------------------------------------------------------------------
int32 strnatcmp8 (const char8* s1, const char8* s2, bool caseSensitive /*= true*/)
{
	return tstrnatcmp (s1, s2, caseSensitive);
}

//------------------------------------------------------------------------
int32 strnatcmp16 (const char16* s1, const char16* s2, bool caseSensitive /*= true*/)
{
	return tstrnatcmp (s1, s2, caseSensitive);
}

//-----------------------------------------------------------------------------
// StringObject Implementation
//-----------------------------------------------------------------------------
void PLUGIN_API StringObject::setText (const char8* text)
{
	assign (text);
}

//-----------------------------------------------------------------------------
void PLUGIN_API StringObject::setText8 (const char8* text)
{	
	assign (text);
}

//-----------------------------------------------------------------------------
void PLUGIN_API StringObject::setText16 (const char16* text)
{
	assign (text);
}

//-----------------------------------------------------------------------------
const char8* PLUGIN_API StringObject::getText8 ()
{
	return text8 ();
}

//-----------------------------------------------------------------------------
const char16* PLUGIN_API StringObject::getText16 ()
{
	return text16 ();
}

//-----------------------------------------------------------------------------
void PLUGIN_API StringObject::take (void* s, bool _isWide)
{
	String::take (s, _isWide);
}

//-----------------------------------------------------------------------------
bool PLUGIN_API StringObject::isWideString () const
{
	return String::isWideString ();
}

//------------------------------------------------------------------------
} // namespace Steinberg
//------------------------------------------------------------------------
// Project     : SDK Base
// Version     : 1.0
// 
// Category    : Helpers
// Filename    : base/source/timer.cpp
// Created by  : Steinberg, 05/2006
// Description : Timer class for receiving triggers at regular intervals
// 
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
// 
//   * Redistributions of source code must retain the above copyright notice, 
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation 
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this 
//     software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#include "base/source/timer.h"

namespace Steinberg {
static bool timersEnabled = true;

//------------------------------------------------------------------------
DisableDispatchingTimers::DisableDispatchingTimers ()
{
	oldState = timersEnabled;
	timersEnabled = false;
}

//------------------------------------------------------------------------
DisableDispatchingTimers::~DisableDispatchingTimers ()
{
	timersEnabled = oldState;
}

//------------------------------------------------------------------------
namespace SystemTime {

//------------------------------------------------------------------------
struct ZeroStartTicks
{
	static const uint64 startTicks;

	static int32 getTicks32 ()
	{
		return static_cast<int32> (SystemTime::getTicks64 () - startTicks);
	}
};
const uint64 ZeroStartTicks::startTicks = SystemTime::getTicks64 ();

//------------------------------------------------------------------------
int32 getTicks ()
{
	return ZeroStartTicks::getTicks32 ();
}

//------------------------------------------------------------------------
} // namespace SystemTime
} // namespace Steinberg


#if SMTG_OS_MACOS
#include <CoreFoundation/CoreFoundation.h>
#include <mach/mach_time.h>

#ifdef verify
#undef verify
#endif

namespace Steinberg {
namespace SystemTime {

//------------------------------------------------------------------------
struct MachTimeBase
{
private:
	struct mach_timebase_info timebaseInfo;

	MachTimeBase () { mach_timebase_info (&timebaseInfo); }

	static const MachTimeBase& instance ()
	{
		static MachTimeBase gInstance;
		return gInstance;
	}

public:
	static double getTimeNanos ()
	{
		const MachTimeBase& timeBase = instance ();
		double absTime = static_cast<double> (mach_absolute_time ());
		// nano seconds
		double d = (absTime / timeBase.timebaseInfo.denom) * timeBase.timebaseInfo.numer;
		return d;
	}
};

/*
	@return the current system time in milliseconds
*/
uint64 getTicks64 ()
{
	return static_cast<uint64> (MachTimeBase::getTimeNanos () / 1000000.);
}
//------------------------------------------------------------------------
} // namespace SystemTime

//------------------------------------------------------------------------
class MacPlatformTimer : public Timer
{
public:
	MacPlatformTimer (ITimerCallback* callback, uint32 milliseconds);
	~MacPlatformTimer ();

	void stop () override;
	bool verify () const { return platformTimer != nullptr; }

	static void timerCallback (CFRunLoopTimerRef timer, void* info);

protected:
	CFRunLoopTimerRef platformTimer;
	ITimerCallback* callback;
};

//------------------------------------------------------------------------
MacPlatformTimer::MacPlatformTimer (ITimerCallback* callback, uint32 milliseconds)
: platformTimer (nullptr), callback (callback)
{
	if (callback)
	{
		CFRunLoopTimerContext timerContext = {};
		timerContext.info = this;
		platformTimer = CFRunLoopTimerCreate (
		    kCFAllocatorDefault, CFAbsoluteTimeGetCurrent () + milliseconds * 0.001,
		    milliseconds * 0.001f, 0, 0, timerCallback, &timerContext);
		if (platformTimer)
			CFRunLoopAddTimer (CFRunLoopGetMain (), platformTimer, kCFRunLoopCommonModes);
	}
}

//------------------------------------------------------------------------
MacPlatformTimer::~MacPlatformTimer ()
{
	stop ();
}

//------------------------------------------------------------------------
void MacPlatformTimer::stop ()
{
	if (platformTimer)
	{
		CFRunLoopRemoveTimer (CFRunLoopGetMain (), platformTimer, kCFRunLoopCommonModes);
		CFRelease (platformTimer);
		platformTimer = nullptr;
	}
}

//------------------------------------------------------------------------
void MacPlatformTimer::timerCallback (CFRunLoopTimerRef, void* info)
{
	if (timersEnabled)
	{
		if (auto timer = (MacPlatformTimer*)info)
			timer->callback->onTimer (timer);
	}
}

//------------------------------------------------------------------------
Timer* Timer::create (ITimerCallback* callback, uint32 milliseconds)
{
	auto timer = NEW MacPlatformTimer (callback, milliseconds);
	if (timer->verify ())
		return timer;
	timer->release ();
	return nullptr;
}
//------------------------------------------------------------------------
} // namespace Steinberg

#elif SMTG_OS_WINDOWS

#include <windows.h>
#include <algorithm>
#include <list>

namespace Steinberg {
namespace SystemTime {

//------------------------------------------------------------------------
/*
    @return the current system time in milliseconds
*/
uint64 getTicks64 ()
{
#if defined(__MINGW32__)
	return GetTickCount ();
#else
	return GetTickCount64 ();
#endif
} // namespace SystemTime
} // namespace Steinberg

class WinPlatformTimer;
using WinPlatformTimerList = std::list<WinPlatformTimer*>;

//------------------------------------------------------------------------
// WinPlatformTimer
//------------------------------------------------------------------------
class WinPlatformTimer : public Timer
{
public:
//------------------------------------------------------------------------
	WinPlatformTimer (ITimerCallback* callback, uint32 milliseconds);
	~WinPlatformTimer ();

	void stop () override;
	bool verify () const { return id != 0; }

//------------------------------------------------------------------------
private:
	UINT_PTR id;
	ITimerCallback* callback;

	static void addTimer (WinPlatformTimer* t);
	static void removeTimer (WinPlatformTimer* t);

	static void CALLBACK TimerProc (HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
	static WinPlatformTimerList* timers;
};

//------------------------------------------------------------------------
WinPlatformTimerList* WinPlatformTimer::timers = nullptr;

//------------------------------------------------------------------------
WinPlatformTimer::WinPlatformTimer (ITimerCallback* callback, uint32 milliseconds)
: callback (callback)
{
	id = SetTimer (nullptr, 0, milliseconds, TimerProc);
	if (id)
		addTimer (this);
}

//------------------------------------------------------------------------
WinPlatformTimer::~WinPlatformTimer ()
{
	stop ();
}

//------------------------------------------------------------------------
void WinPlatformTimer::addTimer (WinPlatformTimer* t)
{
	if (timers == nullptr)
		timers = NEW WinPlatformTimerList;
	timers->push_back (t);
}

//------------------------------------------------------------------------
void WinPlatformTimer::removeTimer (WinPlatformTimer* t)
{
	if (!timers)
		return;

	WinPlatformTimerList::iterator it = std::find (timers->begin (), timers->end (), t);
	if (it != timers->end ())
		timers->erase (it);
	if (timers->empty ())
	{
		delete timers;
		timers = nullptr;
	}
}

//------------------------------------------------------------------------
void WinPlatformTimer::stop ()
{
	if (!id)
		return;

	KillTimer (nullptr, id);
	removeTimer (this);
	id = 0;
}

//------------------------------------------------------------------------
void CALLBACK WinPlatformTimer::TimerProc (HWND /*hwnd*/, UINT /*uMsg*/, UINT_PTR idEvent,
                                           DWORD /*dwTime*/)
{
	if (timersEnabled && timers)
	{
		WinPlatformTimerList::const_iterator it = timers->cbegin ();
		while (it != timers->cend ())
		{
			WinPlatformTimer* timer = *it;
			if (timer->id == idEvent)
			{
				if (timer->callback)
					timer->callback->onTimer (timer);
				return;
			}
			++it;
		}
	}
}

//------------------------------------------------------------------------
Timer* Timer::create (ITimerCallback* callback, uint32 milliseconds)
{
	auto* platformTimer = NEW WinPlatformTimer (callback, milliseconds);
	if (platformTimer->verify ())
		return platformTimer;
	platformTimer->release ();
	return nullptr;
}

//------------------------------------------------------------------------
} // namespace Steinberg

#elif SMTG_OS_LINUX

#include <cassert>
#include <time.h>

namespace Steinberg {
namespace SystemTime {

//------------------------------------------------------------------------
/*
    @return the current system time in milliseconds
*/
uint64 getTicks64 ()
{
	struct timespec ts;
	clock_gettime (CLOCK_MONOTONIC, &ts);
	return static_cast<uint64> (ts.tv_sec) * 1000 + static_cast<uint64> (ts.tv_nsec) / 1000000;
}
//------------------------------------------------------------------------
} // namespace SystemTime

static CreateTimerFunc createTimerFunc = nullptr;

//------------------------------------------------------------------------
void InjectCreateTimerFunction (CreateTimerFunc f)
{
	createTimerFunc = f;
}

//------------------------------------------------------------------------
Timer* Timer::create (ITimerCallback* callback, uint32 milliseconds)
{
	if (createTimerFunc)
		return createTimerFunc (callback, milliseconds);
	return nullptr;
}

//------------------------------------------------------------------------
} // namespace Steinberg

#endif
//------------------------------------------------------------------------
// Project     : SDK Base
// Version     : 1.0
//
// Category    : Helpers
// Filename    : base/source/updatehandler.cpp
// Created by  : Steinberg, 2008
// Description :
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
// 
//   * Redistributions of source code must retain the above copyright notice, 
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation 
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this 
//     software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#include "base/source/updatehandler.h"
#include "base/source/classfactoryhelpers.h"
#include "base/source/fstring.h"

#if SMTG_CPP11_STDLIBSUPPORT
#include <unordered_map>
#else
#include <map>
#endif
#include <deque>
#include <vector>
#include <algorithm>

#define NON_EXISTING_DEPENDENCY_CHECK 0 // not yet
#define CLASS_NAME_TRACKED DEVELOPMENT

using Steinberg::Base::Thread::FGuard;

namespace Steinberg {

DEF_CLASS_IID (IUpdateManager)

namespace Update {
const uint32 kHashSize = (1 << 8); // must be power of 2 (16 bytes * 256 == 4096)
const uint32 kMapSize = 1024 * 10;

//------------------------------------------------------------------------
inline uint32 hashPointer (void* p)
{
	return (uint32)((uint64 (p) >> 12) & (kHashSize - 1));
}

//------------------------------------------------------------------------
inline IPtr<FUnknown> getUnknownBase (FUnknown* unknown)
{
	FUnknown* result = nullptr;
	if (unknown)
		unknown->queryInterface (FUnknown::iid, (void**)&result);

	return owned (result);
}

#if CLASS_NAME_TRACKED
//------------------------------------------------------------------------
struct Dependency
{
	Dependency (FUnknown* o, IDependent* d)
	: obj (o), dep (d), objClass (nullptr), depClass (nullptr)
	{
	}

	inline bool operator== (const Dependency& d) const { return obj == d.obj; }
	inline bool operator!= (const Dependency& d) const { return obj != d.obj; }
	inline bool operator< (const Dependency& d) const { return obj < d.obj; }
	inline bool operator> (const Dependency& d) const { return obj > d.obj; }
	FUnknown* obj;
	IDependent* dep;

	FClassID objClass;
	FClassID depClass;
};
#endif

//------------------------------------------------------------------------
struct DeferedChange
{
	DeferedChange (FUnknown* o, int32 m = 0) : obj (o), msg (m) {}
	~DeferedChange () {}
	DeferedChange (const DeferedChange& r) : obj (r.obj), msg (r.msg) {}
	inline bool operator== (const DeferedChange& d) const { return obj == d.obj; }
	inline bool operator!= (const DeferedChange& d) const { return obj != d.obj; }
	FUnknown* obj;
	int32 msg;
};

//------------------------------------------------------------------------
struct UpdateData
{
	UpdateData (FUnknown* o, IDependent** d, uint32 c)
	: obj (o), dependents (d), count (c)
	{
	}
	FUnknown* obj;
	IDependent** dependents;
	uint32 count;
	bool operator== (const UpdateData& d) const
	{
		return d.obj == obj && d.dependents == dependents;
	}
};

//------------------------------------------------------------------------
using DeferedChangeList = std::deque<DeferedChange>;
using DeferedChangeListIterConst = DeferedChangeList::const_iterator;
using DeferedChangeListIter = DeferedChangeList::iterator;

using UpdateDataList = std::deque<UpdateData>;
using UpdateDataListIterConst = UpdateDataList::const_iterator;

#if CLASS_NAME_TRACKED
using DependentList = std::vector<Dependency>;
#else
typedef std::vector<IDependent*> DependentList;
#endif
using DependentListIter = DependentList::iterator;
using DependentListIterConst = DependentList::const_iterator;

#if SMTG_CPP11_STDLIBSUPPORT
using DependentMap = std::unordered_map<const FUnknown*, DependentList>;
#else
typedef std::map<const FUnknown*, DependentList> DependentMap;
#endif
using DependentMapIter = DependentMap::iterator;
using DependentMapIterConst = DependentMap::const_iterator;

struct Table
{
	DependentMap depMap[kHashSize];
	DeferedChangeList defered;
	UpdateDataList updateData;
};

//------------------------------------------------------------------------
void updateDone (FUnknown* unknown, int32 message)
{
	if (message != IDependent::kDestroyed)
	{
		FObject* obj = FObject::unknownToObject (unknown);
		if (obj)
			obj->updateDone (message);
	}
}
} // namespace Update

//------------------------------------------------------------------------
static int32 countEntries (Update::DependentMap& map)
{
	int32 total = 0;
	Update::DependentMapIterConst iterMap = map.begin ();
	while (iterMap != map.end ())
	{
		const Update::DependentList& list = iterMap->second;
		Update::DependentListIterConst iterList = list.begin ();
		while (iterList != list.end ())
		{
			total++;
			++iterList;
		}
		++iterMap;
	}
	return total;
}

//------------------------------------------------------------------------
UpdateHandler::UpdateHandler ()
{
	table = NEW Update::Table;
	if (FObject::getUpdateHandler () == nullptr)
		FObject::setUpdateHandler (this);
}

//------------------------------------------------------------------------
UpdateHandler::~UpdateHandler ()
{
	if (FObject::getUpdateHandler () == this)
		FObject::setUpdateHandler (nullptr);
	delete table;
	table = nullptr;
}

//------------------------------------------------------------------------
tresult PLUGIN_API UpdateHandler::addDependent (FUnknown* u, IDependent* _dependent)
{
	IPtr<FUnknown> unknown = Update::getUnknownBase (u);
	if (!unknown || !_dependent)
		return kResultFalse;

	FGuard guard (lock);

#if CLASS_NAME_TRACKED
	Update::Dependency dependent (unknown, _dependent);

	FObject* obj = FObject::unknownToObject (unknown);
	if (obj)
		dependent.objClass = obj->isA ();
	obj = FObject::unknownToObject (_dependent);
	if (obj)
		dependent.depClass = obj->isA ();
#else
	IDependent* dependent = _dependent;
#endif

	Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)];
	Update::DependentMapIter it = map.find (unknown);
	if (it == map.end ())
	{
		Update::DependentList list;
		list.push_back (dependent);
		map[unknown] = list;
	}
	else
	{
		(*it).second.push_back (dependent);
	}

	return kResultTrue;
}
//------------------------------------------------------------------------
tresult PLUGIN_API UpdateHandler::removeDependent (FUnknown* u, IDependent* dependent)
{
	size_t eraseCount;
	return removeDependent (u, dependent, eraseCount);
}

//------------------------------------------------------------------------
tresult PLUGIN_API UpdateHandler::removeDependent (FUnknown* u, IDependent* dependent, size_t& eraseCount)
{
	eraseCount = 0;
	IPtr<FUnknown> unknown = Update::getUnknownBase (u);
	if (unknown == nullptr && dependent == nullptr)
		return kResultFalse;

	FGuard guard (lock);

	Update::UpdateDataListIterConst iter = table->updateData.begin ();
	while (iter != table->updateData.end ())
	{
		if ((*iter).obj == unknown || unknown == nullptr)
		{
			for (uint32 count = 0; count < (*iter).count; count++)
			{
				if ((*iter).dependents[count] == dependent)
					(*iter).dependents[count] = nullptr;
			}
		}
		++iter;
	}
	// Remove all objects for the given dependent
	if (unknown == nullptr)
	{
		for (uint32 j = 0; j < Update::kHashSize; j++)
		{
			Update::DependentMap& map = table->depMap[j];
			Update::DependentMapIter iterMap = map.begin ();
			while (iterMap != map.end ())
			{
				Update::DependentList& list = (*iterMap).second;
				Update::DependentListIter iterList = list.begin ();
				bool listIsEmpty = false;
				
				while (iterList != list.end ())
				{
#if CLASS_NAME_TRACKED
					if ((*iterList).dep == dependent)
#else
					if ((*iterList) == dependent)
#endif
					{
						eraseCount = list.size ();
						if (list.size () == 1u)
						{
							listIsEmpty = true;
							break;
						}
						else
						{
							iterList = list.erase (iterList);
						}
					}
					else
					{
						++iterList;
					}
				}
				
				if (listIsEmpty)
					iterMap = map.erase (iterMap);
				else
					++iterMap;
			}
		}
	}
	else
	{
		bool mustFlush = true;

		Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)];
		Update::DependentMapIter iterList = map.find (unknown);

#if NON_EXISTING_DEPENDENCY_CHECK
		SMTG_ASSERT (iterList != map.end () && "ERROR: Trying to remove a non existing dependency!")
#endif
		if (iterList != map.end ())
		{
			if (dependent == nullptr) // Remove all dependents of object
			{
				eraseCount  = iterList->second.size ();
				map.erase (iterList);
			}
			else // Remove one dependent
			{
				Update::DependentList& dependentlist = (*iterList).second;
				Update::DependentListIter iterDependentlist = dependentlist.begin ();
				while (iterDependentlist != dependentlist.end ())
				{
#if CLASS_NAME_TRACKED
					if ((*iterDependentlist).dep == dependent)
#else
					if ((*iterDependentlist) == dependent)
#endif
					{
						iterDependentlist = dependentlist.erase (iterDependentlist);
						eraseCount++;
						if (dependentlist.empty ())
						{
							map.erase (iterList);
							break;
						}
					}
					else
					{
						++iterDependentlist;
						mustFlush = false;
					}
				}
			}
		}
		if (mustFlush)
			cancelUpdates (unknown);
	}

	return kResultTrue;
}

//------------------------------------------------------------------------
tresult UpdateHandler::doTriggerUpdates (FUnknown* u, int32 message, bool suppressUpdateDone)
{
	IPtr<FUnknown> unknown = Update::getUnknownBase (u);
	if (!unknown)
		return kResultFalse;

	// to avoid crashes due to stack overflow, we reduce the amount of memory reserved for the
	// dependents
	IDependent* smallDependents[Update::kMapSize / 10]; // 8kB for x64
	IDependent** dependents = smallDependents;
	int32 maxDependents = Update::kMapSize / 10;
	int32 count = 0;

	{
		FGuard guard (lock); // scope for lock

		Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)];
		Update::DependentMapIterConst iterList = map.find (unknown);
		if (iterList != map.end ())
		{
			const Update::DependentList& dependentlist = (*iterList).second;
			Update::DependentListIterConst iterDependentlist = dependentlist.begin ();
			while (iterDependentlist != dependentlist.end ())
			{
#if CLASS_NAME_TRACKED
				dependents[count] = (*iterDependentlist).dep;
#else
				dependents[count] = *iterDependentlist;
#endif
				count++;

				if (count >= maxDependents)
				{
					if (dependents == smallDependents)
					{
						dependents = NEW IDependent*[Update::kMapSize];
						memcpy (dependents, smallDependents, count * sizeof (dependents[0]));
						maxDependents = Update::kMapSize;
					}
					else
					{
						SMTG_WARNING ("Dependency overflow")
						break;
					}
				}
				++iterDependentlist;
			}
		}

		// push update data on the stack
		if (count > 0)
		{
			Update::UpdateData data (unknown, dependents, count);
			table->updateData.push_back (data);
		}
	} // end scope

	int32 i = 0;
	while (i < count)
	{
		if (dependents[i])
			dependents[i]->update (unknown, message);
		i++;
	}

	if (dependents != smallDependents)
		delete[] dependents;

	// remove update data from the stack
	if (count > 0)
	{
		FGuard guard (lock);

		table->updateData.pop_back ();
	}

	// send update done message
	if (suppressUpdateDone == false)
		Update::updateDone (unknown, message);

	return count > 0 ? kResultTrue : kResultFalse; // Object was found and has been updated
}

//------------------------------------------------------------------------
tresult PLUGIN_API UpdateHandler::triggerUpdates (FUnknown* u, int32 message)
{
	return doTriggerUpdates (u, message, false);
}

//------------------------------------------------------------------------
tresult PLUGIN_API UpdateHandler::deferUpdates (FUnknown* u, int32 message)
{
	IPtr<FUnknown> unknown = Update::getUnknownBase (u);
	if (!unknown)
		return kResultFalse;

	FGuard guard (lock);

	Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)];
	Update::DependentMapIterConst iterList = map.find (unknown);

	bool hasDependency = (iterList != map.end ());
	if (hasDependency == false)
	{
		Update::updateDone (unknown, message);
		return kResultTrue;
	}

	bool found = false;
	Update::DeferedChangeListIterConst iter = table->defered.begin ();
	while (iter != table->defered.end ())
	{
		if ((*iter).obj == unknown && (*iter).msg == message)
		{
			found = true;
			break;
		}
		++iter;
	}

	if (!found)
	{
		Update::DeferedChange change (unknown, message);
		table->defered.push_back (change);
	}

	return kResultTrue;
}

//------------------------------------------------------------------------
tresult PLUGIN_API UpdateHandler::triggerDeferedUpdates (FUnknown* unknown)
{
	Update::DeferedChangeList deferedAgain;
	if (!unknown)
	{
		while (table->defered.empty () == false)
		{
			// Remove first from queue
			lock.lock ();

			FUnknown* obj = table->defered.front ().obj;
			int32 msg = table->defered.front ().msg;
			table->defered.pop_front ();

			// check if this object is currently being updated. in this case, defer update again...
			bool canSignal = true;
			Update::UpdateDataListIterConst iter = table->updateData.begin ();
			while (iter != table->updateData.end ())
			{
				if ((*iter).obj == obj)
				{
					canSignal = false;
					break;
				}
				++iter;
			}
			lock.unlock ();

			if (canSignal)
			{
				triggerUpdates (obj, msg);
			}
			else
			{
				Update::DeferedChange change (obj, msg);
				deferedAgain.push_back (change);
			}
		}
	}
	else
	{
		IPtr<FUnknown> object = Update::getUnknownBase (unknown);
		Update::DeferedChange tmp (object);

		while (true)
		{
			lock.lock ();
			Update::DeferedChangeListIter it =
			    std::find (table->defered.begin (), table->defered.end (), tmp);
			if (it == table->defered.end ())
			{
				lock.unlock ();
				return kResultTrue;
			}

			if ((*it).obj != nullptr)
			{
				int32 msg = (*it).msg;
				table->defered.erase (it);

				// check if this object is currently being updated. in this case, defer update
				// again...
				bool canSignal = true;
				Update::UpdateDataListIterConst iter = table->updateData.begin ();
				while (iter != table->updateData.end ())
				{
					if ((*iter).obj == object)
					{
						canSignal = false;
						break;
					}
					++iter;
				}
				lock.unlock ();

				if (canSignal)
				{
					triggerUpdates (object, msg);
				}
				else
				{
					Update::DeferedChange change (object, msg);
					deferedAgain.push_back (change);
				}
			}
		}
	}

	if (deferedAgain.empty () == false)
	{
		FGuard guard (lock);

		Update::DeferedChangeListIterConst iter = deferedAgain.begin ();
		while (iter != deferedAgain.end ())
		{
			table->defered.push_back (*iter);
			++iter;
		}
	}

	return kResultTrue;
}

//------------------------------------------------------------------------
tresult PLUGIN_API UpdateHandler::cancelUpdates (FUnknown* u)
{
	IPtr<FUnknown> unknown = Update::getUnknownBase (u);
	if (!unknown)
		return kResultFalse;

	FGuard guard (lock);

	Update::DeferedChange change (unknown, 0);
	while (true)
	{
		auto iter = std::find (table->defered.begin (), table->defered.end (), change);
		if (iter != table->defered.end ())
			table->defered.erase (iter);
		else
			break;
	}

	return kResultTrue;
}

//------------------------------------------------------------------------
size_t UpdateHandler::countDependencies (FUnknown* object)
{
	FGuard guard (lock);
	uint32 res = 0;

	IPtr<FUnknown> unknown = Update::getUnknownBase (object);
	if (unknown)
	{
		Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)];
		Update::DependentMapIter iterList = map.find (unknown);
		if (iterList != map.end ())
			return iterList->second.size ();
	}
	else
	{
		for (uint32 j = 0; j < Update::kHashSize; j++)
		{
			Update::DependentMap& map = table->depMap[j];
			res += countEntries (map);
		}
	}
	return res;
}

#if DEVELOPMENT
//------------------------------------------------------------------------
bool UpdateHandler::checkDeferred (FUnknown* object)
{
	IPtr<FUnknown> unknown = Update::getUnknownBase (object);

	FGuard guard (lock);

	Update::DeferedChange tmp (unknown);
	Update::DeferedChangeListIterConst it =
	    std::find (table->defered.begin (), table->defered.end (), tmp);
	if (it != table->defered.end ())
		return true;

	return false;
}

//------------------------------------------------------------------------
bool UpdateHandler::hasDependencies (FUnknown* u)
{
	IPtr<FUnknown> unknown = Update::getUnknownBase (u);
	if (!unknown)
		return false;

	FGuard guard (lock);

	Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)];
	Update::DependentMapIterConst iterList = map.find (unknown);
	bool hasDependency = (iterList != map.end ());

	return hasDependency;
}

//------------------------------------------------------------------------
void UpdateHandler::printForObject (FObject* obj) const
{
	IPtr<FUnknown> unknown = Update::getUnknownBase (obj);
	if (!unknown)
		return;

	FUnknownPtr<IDependent> dep (obj);

	bool header = false;

	Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)];
	Update::DependentMapIterConst iterList = map.begin ();
	while (iterList != map.end ())
	{
		const Update::DependentList& dependentlist = (*iterList).second;
		Update::DependentListIterConst iterDependentlist = dependentlist.begin ();
		while (iterDependentlist != dependentlist.end ())
		{
#if CLASS_NAME_TRACKED
			if ((*iterList).first == unknown || (*iterDependentlist).dep == dep.getInterface ())
			{
				if (!header)
				{
					FDebugPrint ("Dependencies for object %8" FORMAT_INT64A " %s\n", (uint64)obj,
					             obj->isA ());
					header = true;
				}
				FDebugPrint ("%s %8" FORMAT_INT64A "\n <- %s %8" FORMAT_INT64A "\n",
				             (*iterDependentlist).depClass, (uint64) (*iterDependentlist).dep,
				             (*iterDependentlist).objClass, (uint64) ((*iterList).first));
			}
#else
			if ((*iterList).first == unknown || (*iterDependentlist) == dep.getInterface ())
			{
				if (!header)
				{
					FDebugPrint ("Dependencies for object %8" FORMAT_INT64A " %s\n", (uint64)obj,
					             obj->isA ());
					header = true;
				}
				FDebugPrint ("%8" FORMAT_INT64A "\n <- %8" FORMAT_INT64A "\n",
				             (uint64) (*iterDependentlist), (uint64) ((*iterList).first));
			}
#endif
			++iterDependentlist;
		}

		++iterList;
	}
}
#endif

//------------------------------------------------------------------------
} // namespace Steinberg
//------------------------------------------------------------------------
// Project     : SDK Base
// Version     : 1.0
//
// Category    : Helpers
// Filename    : base/source/classfactoryhelpers.h
// Created by  : Steinberg, 03/2017
// Description : Class factory
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
// 
//   * Redistributions of source code must retain the above copyright notice, 
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation 
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this 
//     software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#pragma once

//------------------------------------------------------------------------------
// Helper Macros. Not intended for direct use.
// Use:
//	META_CLASS(className),
//	META_CLASS_IFACE(className,Interface),
//	META_CLASS_SINGLE(className,Interface)
// instead.
//------------------------------------------------------------------------------
#define META_CREATE_FUNC(funcName) static FUnknown* funcName ()

#define CLASS_CREATE_FUNC(className)                                               \
	namespace Meta {                                                               \
	META_CREATE_FUNC (make##className) { return (NEW className)->unknownCast (); } \
	}

#define SINGLE_CREATE_FUNC(className)                                                     \
	namespace Meta {                                                                      \
	META_CREATE_FUNC (make##className) { return className::instance ()->unknownCast (); } \
	}

#define _META_CLASS(className)                                                         \
	namespace Meta {                                                                   \
	static Steinberg::MetaClass meta##className ((#className), Meta::make##className); \
	}

#define _META_CLASS_IFACE(className, Interface)                                                  \
	namespace Meta {                                                                             \
	static Steinberg::MetaClass meta##Interface##className ((#className), Meta::make##className, \
	                                                        Interface##_iid);                    \
	}

/** TODO
 */
#define META_CLASS(className)     \
	CLASS_CREATE_FUNC (className) \
	_META_CLASS (className)

/** TODO
 */
#define META_CLASS_IFACE(className, Interface) \
	CLASS_CREATE_FUNC (className)              \
	_META_CLASS_IFACE (className, Interface)

/** TODO
 */
#define META_CLASS_SINGLE(className, Interface) \
	SINGLE_CREATE_FUNC (className)              \
	_META_CLASS_IFACE (className, Interface)
//------------------------------------------------------------------------
// Project     : SDK Base
// Version     : 1.0
//
// Category    : Helpers
// Filename    : base/source/fbuffer.h
// Created by  : Steinberg, 2008
// Description : 
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
// 
//   * Redistributions of source code must retain the above copyright notice, 
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation 
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this 
//     software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#pragma once

#include "pluginterfaces/base/ftypes.h"
#include <cstring> 

namespace Steinberg {
class String;

//------------------------------------------------------------------------
/** Buffer.
@ingroup adt

A Buffer is an object-oriented wrapper for a piece of memory.
It adds several utility functions, e.g. for managing the size of the Buffer,
appending or prepending values or strings to it.
Internally it uses the standard memory functions malloc(), free(), etc. */
//------------------------------------------------------------------------
class Buffer
{
public:
//---------------------------------------------------------------------
	
	/**	Default constructor, allocates no memory at all.
	*/
	Buffer ();

	/**	Constructor - creates a new Buffer with a given size and copies contents from optional memory pointer.
	\param[in] b : optional memory pointer with the size of at least the given size
	\param[in] size : the size of the new Buffer to be allocated, in bytes.
	*/
	Buffer (const void* b, uint32 size);

	/**	Constructor - creates a new Buffer with a given size and fills it all with a given value.
	\param[in] size : the size of the new Buffer to be allocated, in bytes.
	\param[in] initVal : the initial value the Buffer will be completely filled with
	*/
	Buffer (uint32 size, uint8 initVal);

	/**	Constructor - creates a new Buffer with a given size.
	\param[in] size : the size of the new Buffer to be allocated, in bytes.
	*/
	Buffer (uint32 size);

	/**	Copy constructor - creates a new Buffer from a given Buffer.
	\param[in] buff : the Buffer from which all memory will be copied to the new one
	*/
	Buffer (const Buffer& buff);

	/**	Destructor - deallocates the internal memory.
	*/
	virtual ~Buffer ();
	
	/**	Assignment operator - copies contents from a given Buffer and increases the size if necessary.
	\param[in] buff : the Buffer from which all memory will be copied
	*/
	void operator = (const Buffer& buff);
	
	/**	Comparison operator - copies contents from a given Buffer and increases the size if necessary.
	\param[in] buff : the Buffer to be compared to
	\return true, if the given Buffer's content is equal to this one, else false
	*/
	bool operator == (const Buffer& buff)const;

	uint32 getSize () const {return memSize;}		///< \return the actual size of the Buffer's memory, in bytes.

	/**	Sets a new size for this Buffer, keeping as much content as possible.
	\param[in] newSize : the new size for the Buffer, in bytes, newSize maybe zero
	\return true, if the new size could be adapted, else false
	*/
	bool setSize (uint32 newSize);

	/**	Increases the Buffer to the next block, block size given by delta.
	\param[in] memSize : the new minimum size of the Buffer, newSize maybe zero
	\return true, if the Buffer could be grown successfully, else false
	*/
	bool grow (uint32 memSize);
	bool setMaxSize (uint32 size) {return grow (size);}	///< see \ref grow()

	void fillup (uint8 initVal = 0);				///< set from fillSize to end
	uint32 getFillSize ()const {return fillSize;}	///< \return the actual fill size
	bool setFillSize (uint32 c);					///< sets a new fill size, does not change any memory
	inline void flush () {setFillSize (0);}			///< sets fill size to zero
	bool truncateToFillSize ();						///< \return always true, truncates the size of the Buffer to the actual fill size

	bool isFull () const { return (fillSize == memSize); }	///< \return true, if all memory is filled up, else false
	uint32 getFree () const { return (memSize - fillSize); }///< \return remaining memory

	inline void shiftStart (int32 amount) {return shiftAt (0, amount);} ///< moves all memory by given amount, grows the Buffer if necessary
	void shiftAt (uint32 position, int32 amount);						///< moves memory starting at the given position
	void move (int32 amount, uint8 initVal = 0);						///< shifts memory at start without growing the buffer, so data is lost and initialized with init val

	bool copy (uint32 from, uint32 to, uint32 bytes);	///< copies a number of bytes from one position to another, the size may be adapted
	uint32 get (void* b, uint32 size);					///< copy to buffer from fillSize, and shift fillSize

	void setDelta (uint32 d) {delta = d;}				///< define the block size by which the Buffer grows, see \ref grow()

	bool put (uint8);							///< append value at end, grows Buffer if necessary 
	bool put (char16 c);                        ///< append value at end, grows Buffer if necessary
	bool put (char c);							///< append value at end, grows Buffer if necessary
	bool put (const void* , uint32 size);		///< append bytes from a given buffer, grows Buffer if necessary
	bool put (void* , uint32 size);				///< append bytes from a given buffer, grows Buffer if necessary
	bool put (uint8* , uint32 size);			///< append bytes from a given buffer, grows Buffer if necessary
	bool put (char8* , uint32 size);			///< append bytes from a given buffer, grows Buffer if necessary
	bool put (const uint8* , uint32 size);		///< append bytes from a given buffer, grows Buffer if necessary
	bool put (const char8* , uint32 size);		///< append bytes from a given buffer, grows Buffer if necessary
	bool put (const String&);					///< append String at end, grows Buffer if necessary

	void set (uint8 value);		///< fills complete Buffer with given value
	
	// strings ----------------
	bool appendString (const tchar* s);
	bool appendString (tchar* s);
	bool appendString (tchar c)                   { return put (c); }

	bool appendString8 (const char8* s);	
	bool appendString16 (const char16* s);

	bool appendString8 (char8* s)                 { return appendString8 ((const char8*)s); }
	bool appendString8 (unsigned char* s)		  { return appendString8 ((const char8*)s); }         
	bool appendString8 (const unsigned char* s)   { return appendString8 ((const char8*)s); }

	bool appendString8 (char8 c)                  { return put ((uint8)c); }
	bool appendString8 (unsigned char c)          { return put (c); }
	bool appendString16 (char16 c)                { return put (c); }
	bool appendString16 (char16* s)               { return appendString16 ((const char16*)s); }

	bool prependString (const tchar* s);   
	bool prependString (tchar* s);   
	bool prependString (tchar c);                

	bool prependString8 (const char8* s);           
	bool prependString16 (const char16* s);

	bool prependString8 (char8 c);
	bool prependString8 (unsigned char c)         { return prependString8 ((char8)c); }
	bool prependString8 (char8* s)                { return prependString8 ((const char8*)s); }
	bool prependString8 (unsigned char* s)        { return prependString8((const char8*)s); }           
	bool prependString8 (const unsigned char* s)  { return prependString8 ((const char8*)s); } 
	bool prependString16 (char16 c);
	bool prependString16 (char16* s)              { return prependString16 ((const char16*)s); }

	bool operator+= (const char* s)               { return appendString8 (s); }
	bool operator+= (char c)                      { return appendString8 (c); }
	bool operator+= (const char16* s)             { return appendString16 (s); }
	bool operator+= (char16 c)                    { return appendString16 (c); }

	bool operator= (const char* s)                { flush (); return appendString8 (s); }
	bool operator= (const char16* s)              { flush (); return appendString16 (s); }
	bool operator= (char8 c)                      { flush (); return appendString8 (c); }
	bool operator= (char16 c)                     { flush (); return appendString16 (c); }

	void endString () {put (tchar (0));}
	void endString8 () {put (char8 (0));}
	void endString16 () {put (char16 (0));}

	bool makeHexString (String& result);
	bool fromHexString (const char8* string);

	// conversion
	operator void* () const { return (void*)buffer; }				///< conversion
	inline tchar*   str ()   const {return (tchar*)buffer;}			///< conversion
	inline char8*   str8 ()   const {return (char8*)buffer;}		///< conversion
	inline char16*  str16 ()   const {return (char16*)buffer;}		///< conversion
	inline int8*   int8Ptr ()   const {return (int8*)buffer;}		///< conversion
	inline uint8*  uint8Ptr ()  const {return (uint8*)buffer; }		///< conversion
	inline int16*  int16Ptr ()  const {return (int16*)buffer; }		///< conversion
    inline uint16* uint16Ptr () const {return (uint16*)buffer; }	///< conversion
	inline int32*  int32Ptr ()  const {return (int32*)buffer; }		///< conversion
	inline uint32* uint32Ptr () const {return (uint32*)buffer; }	///< conversion
	inline float*  floatPtr ()  const {return (float*)buffer; }		///< conversion
	inline double* doublePtr () const {return (double*)buffer; }	///< conversion
	inline char16*  wcharPtr ()  const {return (char16*)buffer;}	///< conversion

	int8* operator + (uint32 i);	///< \return the internal Buffer's address plus the given offset i, zero if offset is out of range
	
	int32 operator ! ()  { return buffer == nullptr; }
	
	enum swapSize 
	{
		kSwap16 = 2,
		kSwap32 = 4,
		kSwap64 = 8
	};
	bool swap (int16 swapSize);											///< swap all bytes of this Buffer by the given swapSize
	static bool swap (void* buffer, uint32 bufferSize, int16 swapSize);	///< utility, swap given number of bytes in given buffer by the given swapSize

	void take (Buffer& from);	///< takes another Buffer's memory, frees the current Buffer's memory
	int8* pass ();				///< pass the current Buffer's memory

	/**	Converts a Buffer's content to UTF-16 from a given multi-byte code page, Buffer must contain char8 of given encoding.
		\param[in] sourceCodePage : the actual code page of the Buffer's content
		\return true, if the conversion was successful, else false
	*/
	virtual bool toWideString (int32 sourceCodePage); // Buffer contains char8 of given encoding -> utf16

	/**	Converts a Buffer's content from UTF-16 to a given multi-byte code page, Buffer must contain UTF-16 encoded characters.
		\param[in] destCodePage : the desired code page to convert the Buffer's content to
		\return true, if the conversion was successful, else false
	*/
	virtual bool toMultibyteString (int32 destCodePage); // Buffer contains utf16 -> char8 of given encoding

//------------------------------------------------------------------------
protected:
	static const uint32 defaultDelta = 0x1000; // 0x1000
	
	int8* buffer;
	uint32 memSize;
	uint32 fillSize;
	uint32 delta;
};

inline bool Buffer::put (void* p, uint32 count)     { return put ((const void*)p , count ); }
inline bool Buffer::put (uint8 * p, uint32 count)   { return put ((const void*)p , count ); }
inline bool Buffer::put (char8* p, uint32 count)    { return put ((const void*)p , count ); }
inline bool Buffer::put (const uint8* p, uint32 count) { return put ((const void*)p , count ); }
inline bool Buffer::put (const char8* p, uint32 count) { return put ((const void*)p , count ); }

//------------------------------------------------------------------------
inline bool Buffer::appendString (const tchar* s)
{
#ifdef UNICODE
	return appendString16 (s);
#else
	return appendString8 (s);
#endif
}

//------------------------------------------------------------------------
inline bool Buffer::appendString (tchar* s)
{
#ifdef UNICODE
	return appendString16 (s);
#else
	return appendString8 (s);
#endif
}

//------------------------------------------------------------------------
inline bool Buffer::prependString (const tchar* s)
{
#ifdef UNICODE
	return prependString16 (s);
#else
	return prependString8 (s);
#endif
}

//------------------------------------------------------------------------
inline bool Buffer::prependString (tchar* s)
{
#ifdef UNICODE
	return prependString16 (s);
#else
	return prependString8 (s);
#endif
}

//------------------------------------------------------------------------
inline bool Buffer::prependString (tchar c)
{
#ifdef UNICODE
	return prependString16 (c);
#else
	return prependString8 (c);
#endif
}

//------------------------------------------------------------------------
} // namespace Steinberg
//------------------------------------------------------------------------
// Project     : SDK Base
// Version     : 1.0
//
// Category    : Helpers
// Filename    : base/source/fcleanup.h
// Created by  : Steinberg, 2008
// Description : Template classes for automatic resource cleanup
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
// 
//   * Redistributions of source code must retain the above copyright notice, 
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation 
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this 
//     software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#pragma once

#include <cstdlib>

namespace Steinberg {

/** Template definition for classes that help guarding against memory leaks.
A stack allocated object of this type automatically deletes an at construction time passed
dynamically allocated single object when it reaches the end of its scope. \n\n
Intended usage:
\code
    {
        int* pointerToInt = new int;
        Steinberg::FDeleter<int> deleter (pointerToInt);

        // Do something with the variable behind pointerToInt.

    } // No memory leak here, destructor of deleter cleans up the integer.
\endcode
*/
//------------------------------------------------------------------------
template <class T>
struct FDeleter
{
	/// Constructor. _toDelete is a pointer to the dynamically allocated object that is to be
	/// deleted when this FDeleter object's destructor is executed.
	FDeleter (T* _toDelete) : toDelete (_toDelete) {}
	/// Destructor. Calls delete on the at construction time passed pointer.
	~FDeleter ()
	{
		if (toDelete)
			delete toDelete;
	}
	T* toDelete; ///< Remembers the object that is to be deleted during destruction.
};

/** Template definition for classes that help guarding against memory leaks.
A stack allocated object of this type automatically deletes an at construction time passed
dynamically allocated array of objects when it reaches the end of its scope. \n\n
Intended usage:
\code
    {
        int* pointerToIntArray = new int[10];
        Steinberg::FArrayDeleter<int> deleter (pointerToIntArray);

        // Do something with the array behind pointerToIntArray.

    } // No memory leak here, destructor of deleter cleans up the integer array.
\endcode
*/
//------------------------------------------------------------------------
template <class T>
struct FArrayDeleter
{
	/// Constructor. _arrayToDelete is a pointer to the dynamically allocated array of objects that
	/// is to be deleted when this FArrayDeleter object's destructor is executed.
	FArrayDeleter (T* _arrayToDelete) : arrayToDelete (_arrayToDelete) {}

	/// Destructor. Calls delete[] on the at construction time passed pointer.
	~FArrayDeleter ()
	{
		if (arrayToDelete)
			delete[] arrayToDelete;
	}

	T* arrayToDelete; ///< Remembers the array of objects that is to be deleted during destruction.
};

/** Template definition for classes that help guarding against dangling pointers.
A stack allocated object of this type automatically resets an at construction time passed
pointer to null when it reaches the end of its scope. \n\n
Intended usage:
\code
    int* pointerToInt = 0;
    {
        int i = 1;
        pointerToInt = &i;
        Steinberg::FPtrNuller<int> ptrNuller (pointerToInt);

        // Do something with pointerToInt.

    } // No dangling pointer here, pointerToInt is reset to 0 by destructor of ptrNuller.
\endcode
*/
//------------------------------------------------------------------------
template <class T>
struct FPtrNuller
{
	/// Constructor. _toNull is a reference to the pointer that is to be reset to NULL when this
	/// FPtrNuller object's destructor is executed.
	FPtrNuller (T*& _toNull) : toNull (_toNull) {}
	/// Destructor. Calls delete[] on the at construction time passed pointer.
	~FPtrNuller () { toNull = 0; }

	T*& toNull; ///< Remembers the pointer that is to be set to NULL during destruction.
};

/** Template definition for classes that help resetting an object's value.
A stack allocated object of this type automatically resets the value of an at construction time
passed object to null when it reaches the end of its scope. \n\n Intended usage: \code int theObject
= 0;
    {
        Steinberg::FNuller<int> theNuller (theObject);

        theObject = 1;

    } // Here the destructor of theNuller resets the value of theObject to 0.
\endcode
*/
//------------------------------------------------------------------------
template <class T>
struct FNuller
{
	/// Constructor. _toNull is a reference to the object that is to be assigned 0 when this FNuller
	/// object's destructor is executed.
	FNuller (T& _toNull) : toNull (_toNull) {}
	/// Destructor. Assigns 0 to the at construction time passed object reference.
	~FNuller () { toNull = 0; }

	T& toNull; ///< Remembers the object that is to be assigned 0 during destruction.
};

/** Class definition for objects that help resetting boolean variables.
A stack allocated object of this type automatically sets an at construction time passed
boolean variable immediately to TRUE and resets the same variable to FALSE when it
reaches the end of its own scope. \n\n
Intended usage:
\code
    bool theBoolean = false;
    {
        Steinberg::FBoolSetter theBoolSetter (theBoolean);
        // Here the constructor of theBoolSetter sets theBoolean to TRUE.

        // Do something.

    } // Here the destructor of theBoolSetter resets theBoolean to FALSE.
\endcode
*/
//------------------------------------------------------------------------
template <class T>
struct FBooleanSetter
{
	/// Constructor. _toSet is a reference to the boolean that is set to TRUE immediately in this
	/// constructor call and gets reset to FALSE when this FBoolSetter object's destructor is
	/// executed.
	FBooleanSetter (T& _toSet) : toSet (_toSet) { toSet = true; }
	/// Destructor. Resets the at construction time passed boolean to FALSE.
	~FBooleanSetter () { toSet = false; }

	T& toSet; ///< Remembers the boolean that is to be reset during destruction.
};

using FBoolSetter = FBooleanSetter<bool>;

/** Class definition for objects that help setting boolean variables.
A stack allocated object of this type automatically sets an at construction time passed
boolean variable to TRUE if the given condition is met. At the end of its own scope the
stack object will reset the same boolean variable to FALSE, if it wasn't set so already. \n\n
Intended usage:
\code
    bool theBoolean = false;
    {
        bool creativityFirst = true;
        Steinberg::FConditionalBoolSetter theCBSetter (theBoolean, creativityFirst);
        // Here the constructor of theCBSetter sets theBoolean to the value of creativityFirst.

        // Do something.

    } // Here the destructor of theCBSetter resets theBoolean to FALSE.
\endcode
*/
//------------------------------------------------------------------------
struct FConditionalBoolSetter
{
	/// Constructor. _toSet is a reference to the boolean that is to be set. If the in the second
	/// parameter given condition is TRUE then also _toSet is set to TRUE immediately.
	FConditionalBoolSetter (bool& _toSet, bool condition) : toSet (_toSet)
	{
		if (condition)
			toSet = true;
	}
	/// Destructor. Resets the at construction time passed boolean to FALSE.
	~FConditionalBoolSetter () { toSet = false; }

	bool& toSet; ///< Remembers the boolean that is to be reset during destruction.
};

/** Template definition for classes that help closing resources.
A stack allocated object of this type automatically calls the close method of an at
construction time passed object when it reaches the end of its scope.
It goes without saying that the given type needs to have a close method. \n\n
Intended usage:
\code
    struct CloseableObject
    {
        void close() {};
    };

    {
        CloseableObject theObject;
        Steinberg::FCloser<CloseableObject> theCloser (&theObject);

        // Do something.

    } // Here the destructor of theCloser calls the close method of theObject.
\endcode
*/
template <class T>
struct FCloser
{
	/// Constructor. _obj is the pointer on which close is to be called when this FCloser object's
	/// destructor is executed.
	FCloser (T* _obj) : obj (_obj) {}
	/// Destructor. Calls the close function on the at construction time passed pointer.
	~FCloser ()
	{
		if (obj)
			obj->close ();
	}

	T* obj; ///< Remembers the pointer on which close is to be called during destruction.
};

/** Class definition for objects that help guarding against memory leaks.
A stack allocated object of this type automatically frees the "malloced" memory behind an at
construction time passed pointer when it reaches the end of its scope.
*/
//------------------------------------------------------------------------
/*! \class FMallocReleaser
 */
//------------------------------------------------------------------------
class FMallocReleaser
{
public:
	/// Constructor. _data is the pointer to the memory on which free is to be called when this
	/// FMallocReleaser object's destructor is executed.
	FMallocReleaser (void* _data) : data (_data) {}
	/// Destructor. Calls the free function on the at construction time passed pointer.
	~FMallocReleaser ()
	{
		if (data)
			free (data);
	}
//------------------------------------------------------------------------
protected:
	void* data; ///< Remembers the pointer on which free is to be called during destruction.
};

//------------------------------------------------------------------------
} // namespace Steinberg


#if SMTG_OS_MACOS
typedef const void* CFTypeRef;
extern "C" {
	extern void CFRelease (CFTypeRef cf);
}

namespace Steinberg {

/** Class definition for objects that helps releasing CoreFoundation objects.
A stack allocated object of this type automatically releases an at construction time
passed CoreFoundation object when it reaches the end of its scope.

Only available on Macintosh platform.
*/
//------------------------------------------------------------------------
/*! \class CFReleaser
*/
//------------------------------------------------------------------------
class CFReleaser
{
public:
	/// Constructor. _obj is the reference to an CoreFoundation object which is to be released when this CFReleaser object's destructor is executed. 
	CFReleaser (CFTypeRef _obj) : obj (_obj) {}
	/// Destructor. Releases the at construction time passed object.
	~CFReleaser () { if (obj) CFRelease (obj); }
protected:
	CFTypeRef obj; ///< Remembers the object which is to be released during destruction.
};

//------------------------------------------------------------------------
} // namespace Steinberg

#endif // SMTG_OS_MACOS
//------------------------------------------------------------------------
// Project     : SDK Base
// Version     : 1.0
//
// Category    : Helpers
// Filename    : base/source/fcommandline.h
// Created by  : Steinberg, 2007
// Description : Very simple command-line parser.
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
// 
//   * Redistributions of source code must retain the above copyright notice, 
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation 
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this 
//     software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

//------------------------------------------------------------------------
/** @file base/source/fcommandline.h
	Very simple command-line parser.
	@see Steinberg::CommandLine */
//------------------------------------------------------------------------
#pragma once

#include <algorithm>
#include <deque>
#include <initializer_list>
#include <iterator>
#include <map>
#include <sstream>
#include <vector>

namespace Steinberg {
//------------------------------------------------------------------------
/** Very simple command-line parser.

Parses the command-line into a CommandLine::VariablesMap.\n
The command-line parser uses CommandLine::Descriptions to define the available options.

@b Example: 
\code
#include "base/source/fcommandline.h"
#include <iostream>

int main (int argc, char* argv[])
{
	using namespace std;

	CommandLine::Descriptions desc;
	CommandLine::VariablesMap valueMap;

	desc.addOptions ("myTool")
		("help", "produce help message")
		("opt1", string(), "option 1")
		("opt2", string(), "option 2")
	;
	CommandLine::parse (argc, argv, desc, valueMap);

	if (valueMap.hasError () || valueMap.count ("help"))
	{
		cout << desc << "\n";
		return 1;
	}			
	if (valueMap.count ("opt1"))
	{
		cout << "Value of option 1 " << valueMap["opt1"] << "\n";
	}
	if (valueMap.count ("opt2"))
	{
		cout << "Value of option 2 " << valueMap["opt2"] << "\n";
	}
	return 0;
}
\endcode
@note
This is a "header only" implementation.\n
If you need the declarations in more than one cpp file, you have to define 
@c SMTG_NO_IMPLEMENTATION in all but one file.

*/
//------------------------------------------------------------------------
namespace CommandLine {
	
	//------------------------------------------------------------------------
	/**	Command-line parsing result. 
		
		This is the result of the parser.\n
		- Use hasError() to check for errors.\n
		- To test if a option was specified on the command-line use: count()\n
		- To retrieve the value of an options, use operator [](const VariablesMapContainer::key_type k)\n
	*/
	//------------------------------------------------------------------------
	class VariablesMap
	{
		bool mParaError;
		using VariablesMapContainer = std::map<std::string, std::string>;
		VariablesMapContainer mVariablesMapContainer;
	public:
		VariablesMap () : mParaError (false) {}								///< Constructor. Creates a empty VariablesMap.
		bool hasError () const { return mParaError; }						///< Returns @c true when an error has occurred. 
		void setError () { mParaError = true; }								///< Sets the error state to @c true.
		std::string& operator [](const VariablesMapContainer::key_type k);	///< Retrieve the value of option @c k.
		const std::string& operator [](const VariablesMapContainer::key_type k) const;	///< Retrieve the value of option @c k.
		VariablesMapContainer::size_type count (const VariablesMapContainer::key_type k) const; ///< Returns @c != @c 0 if command-line contains option @c k.
	};

	//! type of the list of elements on the command line that are not handled by options parsing
	using FilesVector = std::vector<std::string>;

	//------------------------------------------------------------------------
	/** The description of one single command-line option. 
		
		Normally you rarely use a Description directly.\n
		In most cases you will use the Descriptions::addOptions (const std::string&) method to create and add descriptions.
	*/
	//------------------------------------------------------------------------
	class Description : public std::string
	{
	public:
		Description (const std::string& name, const std::string& help, const std::string& valueType ); ///< Construct a Description 
		std::string mHelp; ///< The help string for this option.
		std::string mType; ///< The type of this option (kBool, kString).
		static const std::string kBool;
		static const std::string kString;
	};
	//------------------------------------------------------------------------
	/** List of command-line option descriptions. 
	
		Use addOptions(const std::string&) to add Descriptions.
	*/
	//------------------------------------------------------------------------
	class Descriptions 
	{
		using DescriptionsList = std::deque<Description>;
		DescriptionsList mDescriptions;
		std::string mCaption;
	public:
		/** Sets the command-line tool caption and starts adding Descriptions. */
	    Descriptions& addOptions (const std::string& caption = "",
	                              std::initializer_list<Description>&& options = {});
	    /** Parse the command-line. */
		bool parse (int ac, char* av[], VariablesMap& result, FilesVector* files = nullptr) const;
		/** Print a brief description for the command-line tool into the stream @c os. */
		void print (std::ostream& os) const;
		/** Add a new switch. Only */
		Descriptions& operator() (const std::string& name, const std::string& help);
		/** Add a new option of type @c inType. Currently only std::string is supported. */
	    template <typename Type>
	    Descriptions& operator () (const std::string& name, const Type& inType, std::string help);
    };
	
//------------------------------------------------------------------------
// If you need the declarations in more than one cpp file you have to define
// SMTG_NO_IMPLEMENTATION in all but one file.
//------------------------------------------------------------------------
#ifndef SMTG_NO_IMPLEMENTATION

	//------------------------------------------------------------------------
	/*! If command-line contains option @c k more than once, only the last value will survive. */
	//------------------------------------------------------------------------
	std::string& VariablesMap::operator [](const VariablesMapContainer::key_type k)
	{
		return mVariablesMapContainer[k];
	}

	//------------------------------------------------------------------------
	/*! If command-line contains option @c k more than once, only the last value will survive. */
	//------------------------------------------------------------------------
	const std::string& VariablesMap::operator [](const VariablesMapContainer::key_type k) const
	{
		return (*const_cast<VariablesMap*>(this))[k];
	}
	
	//------------------------------------------------------------------------
	VariablesMap::VariablesMapContainer::size_type VariablesMap::count (const VariablesMapContainer::key_type k) const
	{
		return mVariablesMapContainer.count (k);
	}

	//------------------------------------------------------------------------
	/** Add a new option with a string as parameter. */
	//------------------------------------------------------------------------
	template <> Descriptions& Descriptions::operator() (const std::string& name, const std::string& inType, std::string help)
	{
		mDescriptions.emplace_back (name, help, inType);
		return *this;
	}
	bool parse (int ac, char* av[], const Descriptions& desc, VariablesMap& result, FilesVector* files = nullptr); ///< Parse the command-line.
    std::ostream& operator<< (std::ostream& os, const Descriptions& desc); ///< Make Descriptions stream able.

	const std::string Description::kBool = "bool";
	const std::string Description::kString = "string";

	//------------------------------------------------------------------------
	/*! In most cases you will use the Descriptions::addOptions (const std::string&) method to create and add descriptions.
	 
		@param[in] name of the option.
		@param[in] help a help description for this option.
		@param[out] valueType Description::kBool or Description::kString.
	*/
	Description::Description (const std::string& name, const std::string& help, const std::string& valueType)
	: std::string (name)
	, mHelp (help)
	, mType (valueType)
	{
	}

//------------------------------------------------------------------------
    /*! Returning a reference to *this, enables chaining of calls to operator()(const std::string&,
     const std::string&).
        @param[in] name of the added option.
        @param[in] help a help description for this option.
        @return a reference to *this.
    */
    Descriptions& Descriptions::operator () (const std::string& name, const std::string& help)
    {
	    mDescriptions.emplace_back (name, help, Description::kBool);
	    return *this;
    }

//------------------------------------------------------------------------
	/*!	<b>Usage example:</b>
																@code
	CommandLine::Descriptions desc;
	desc.addOptions ("myTool")           // Set caption to "myTool"
	    ("help", "produce help message") // add switch -help
	    ("opt1", string(), "option 1")   // add string option -opt1
	    ("opt2", string(), "option 2")   // add string option -opt2
	;
																@endcode
	@note
		The operator() is used for every additional option.

	Or with initializer list :
																@code
	CommandLine::Descriptions desc;
	desc.addOptions ("myTool",                                 // Set caption to "myTool"
	    {{"help", "produce help message", Description::kBool}, // add switch -help
	     {"opt1", "option 1", Description::kString},           // add string option -opt1
	     {"opt2", "option 2", Description::kString}}           // add string option -opt2
	);
																@endcode
	@param[in] caption the caption of the command-line tool.
	@param[in] options initializer list with options
	@return a reverense to *this.
	*/
    Descriptions& Descriptions::addOptions (const std::string& caption,
                                            std::initializer_list<Description>&& options)
    {
		mCaption = caption;
	    std::move (options.begin (), options.end (), std::back_inserter (mDescriptions));
	    return *this;
	}

	//------------------------------------------------------------------------
	/*!	@param[in] ac count of command-line parameters
		@param[in] av command-line as array of strings
		@param[out] result the parsing result
		@param[out] files optional list of elements on the command line that are not handled by options parsing
	*/
    bool Descriptions::parse (int ac, char* av[], VariablesMap& result, FilesVector* files) const
    {
	    using namespace std;

	    for (int i = 1; i < ac; i++)
	    {
		    string current = av[i];
		    if (current[0] == '-')
		    {
			    int pos = current[1] == '-' ? 2 : 1;
			    current = current.substr (pos, string::npos);

			    DescriptionsList::const_iterator found =
			        find (mDescriptions.begin (), mDescriptions.end (), current);
			    if (found != mDescriptions.end ())
			    {
				    result[*found] = "true";
				    if (found->mType != Description::kBool)
				    {
					    if (((i + 1) < ac) && *av[i + 1] != '-')
					    {
						    result[*found] = av[++i];
					    }
					    else
					    {
						    result[*found] = "error!";
						    result.setError ();
						    return false;
					    }
				    }
			    }
			    else
			    {
				    result.setError ();
				    return false;
			    }
		    }
		    else if (files)
			    files->push_back (av[i]);
	    }
	    return true;
    }

//------------------------------------------------------------------------
	/*!	The description includes the help strings for all options. */
	//------------------------------------------------------------------------
    void Descriptions::print (std::ostream& os) const
    {
	    if (!mCaption.empty ())
		    os << mCaption << ":\n";

	    size_t maxLength = 0u;
	    std::for_each (mDescriptions.begin (), mDescriptions.end (),
	                   [&] (const Description& d) { maxLength = std::max (maxLength, d.size ()); });

	    for (const Description& opt : mDescriptions)
	    {
		    os << "-" << opt;
		    for (auto s = opt.size (); s < maxLength; ++s)
			    os << " ";
		    os << " | " << opt.mHelp << "\n";
	    }
    }

//------------------------------------------------------------------------
    std::ostream& operator<< (std::ostream& os, const Descriptions& desc)
	{
        desc.print (os);
        return os;
	}

	//------------------------------------------------------------------------
	/*! @param[in] ac count of command-line parameters
		@param[in] av command-line as array of strings
		@param[in] desc Descriptions including all allowed options
		@param[out] result the parsing result
		@param[out] files optional list of elements on the command line that are not handled by options parsing
	*/
	bool parse (int ac, char* av[], const Descriptions& desc, VariablesMap& result, FilesVector* files)
	{
		return desc.parse (ac, av, result, files);
	}
#endif

} //namespace CommandLine
} //namespace Steinberg
//------------------------------------------------------------------------
// Project     : SDK Base
// Version     : 1.0
//
// Category    : Helpers
// Filename    : base/source/fdebug.h
// Created by  : Steinberg, 1995
// Description : There are 2 levels of debugging messages:
//	             DEVELOPMENT               During development
//	             RELEASE                   Program is shipping.
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
// 
//   * Redistributions of source code must retain the above copyright notice, 
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation 
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this 
//     software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
/** @file base/source/fdebug.h
	Debugging tools.

	There are 2 levels of debugging messages:
	- DEVELOPMENT
	  - During development
	- RELEASE
	  - Program is shipping.
*/
//-----------------------------------------------------------------------------
#pragma once

#include "pluginterfaces/base/ftypes.h"
#include <cstring>

#if SMTG_OS_MACOS
#include <new>
#endif

/** Returns true if a debugger is attached. */
bool AmIBeingDebugged ();

//-----------------------------------------------------------------------------
// development / release
//-----------------------------------------------------------------------------
#if !defined (DEVELOPMENT) && !defined (RELEASE) 
	#ifdef _DEBUG
		#define DEVELOPMENT 1
	#elif defined (NDEBUG)
		#define RELEASE 1
	#else
		#error DEVELOPMENT, RELEASE, _DEBUG, or NDEBUG  must be defined!
	#endif
#endif

//-----------------------------------------------------------------------------
#if SMTG_OS_WINDOWS

/** Disable compiler warning:
 * C4291: "No matching operator delete found; memory will not be freed if initialization throws an
 * exception. A placement new is used for which there is no placement delete." */
#if DEVELOPMENT && defined(_MSC_VER)
#pragma warning(disable : 4291)
#pragma warning(disable : 4985)
#endif

#endif // SMTG_OS_WINDOWS

#if DEVELOPMENT
//-----------------------------------------------------------------------------
/** If "f" is not true and a debugger is present, send an error string to the debugger for display
   and cause a breakpoint exception to occur in the current process. SMTG_ASSERT is removed
   completely in RELEASE configuration. So do not pass methods calls to this macro that are expected
   to exist in the RELEASE build (for method calls that need to be present in a RELEASE build, use
   the VERIFY macros instead)*/
#define SMTG_ASSERT(f) \
	if (!(f))          \
		FDebugBreak ("%s(%d) : Assert failed: %s\n", __FILE__, __LINE__, #f);

#define SMTG_ASSERT_MSG(f, msg) \
	if (!(f))          \
		FDebugBreak ("%s(%d) : Assert failed: [%s] [%s]\n", __FILE__, __LINE__, #f, msg);

/** Send "comment" string to the debugger for display. */
#define SMTG_WARNING(comment) FDebugPrint ("%s(%d) : %s\n", __FILE__, __LINE__, comment);

/** Send the last error string to the debugger for display. */
#define SMTG_PRINTSYSERROR FPrintLastError (__FILE__, __LINE__);

/** If a debugger is present, send string "s" to the debugger for display and
    cause a breakpoint exception to occur in the current process. */
#define SMTG_DEBUGSTR(s) FDebugBreak (s);

/** Use VERIFY for calling methods "f" having a bool result (expecting them to return 'true')
     The call of "f" is not removed in RELEASE builds, only the result verification. eg: SMTG_VERIFY
   (isValid ()) */
#define SMTG_VERIFY(f) SMTG_ASSERT (f)

/** Use VERIFY_IS for calling methods "f" and expect a certain result "r".
    The call of "f" is not removed in RELEASE builds, only the result verification. eg:
   SMTG_VERIFY_IS (callMethod (), kResultOK) */
#define SMTG_VERIFY_IS(f, r) \
	if ((f) != (r))          \
		FDebugBreak ("%s(%d) : Assert failed: %s\n", __FILE__, __LINE__, #f);

/** Use VERIFY_NOT for calling methods "f" and expect the result to be anything else but "r".
     The call of "f" is not removed in RELEASE builds, only the result verification. eg:
   SMTG_VERIFY_NOT (callMethod (), kResultError) */
#define SMTG_VERIFY_NOT(f, r) \
	if ((f) == (r))           \
		FDebugBreak ("%s(%d) : Assert failed: %s\n", __FILE__, __LINE__, #f);

/** @name Shortcut macros for sending strings to the debugger for display.
	First parameter is always the format string (printf like).
*/

///@{
#define SMTG_DBPRT0(a) FDebugPrint (a);
#define SMTG_DBPRT1(a, b) FDebugPrint (a, b);
#define SMTG_DBPRT2(a, b, c) FDebugPrint (a, b, c);
#define SMTG_DBPRT3(a, b, c, d) FDebugPrint (a, b, c, d);
#define SMTG_DBPRT4(a, b, c, d, e) FDebugPrint (a, b, c, d, e);
#define SMTG_DBPRT5(a, b, c, d, e, f) FDebugPrint (a, b, c, d, e, f);
///@}

/** @name Helper functions for the above defined macros.

    You shouldn't use them directly (if you do so, don't forget "#if DEVELOPMENT")!
    It is recommended to use the macros instead.
*/
///@{
void FDebugPrint (const char* format, ...);
void FDebugBreak (const char* format, ...);
void FPrintLastError (const char* file, int line);
///@}

/** @name Provide a custom assertion handler and debug print handler, eg
        so that we can provide an assert with a custom dialog, or redirect
        the debug output to a file or stream.
*/
///@{
using AssertionHandler = bool (*) (const char* message);
extern AssertionHandler gAssertionHandler;
extern AssertionHandler gPreAssertionHook;
using DebugPrintLogger = void (*) (const char* message);
extern DebugPrintLogger gDebugPrintLogger;
///@}

/** Definition of memory allocation macros:
    Use "NEW" to allocate storage for individual objects.
    Use "NEWVEC" to allocate storage for an array of objects. */
#if SMTG_OS_MACOS
void* operator new (size_t, int, const char*, int);
void* operator new[] (size_t, int, const char*, int);
void operator delete (void* p, int, const char* file, int line);
void operator delete[] (void* p, int, const char* file, int line);
#ifndef NEW
#define NEW new (1, __FILE__, __LINE__)
#define NEWVEC new (1, __FILE__, __LINE__)
#endif

#define DEBUG_NEW DEBUG_NEW_LEAKS

#elif SMTG_OS_WINDOWS && defined(_MSC_VER)
#ifndef NEW
void* operator new (size_t, int, const char*, int);
#define NEW new (1, __FILE__, __LINE__)
#define NEWVEC new (1, __FILE__, __LINE__)
#endif

#else
#ifndef NEW
#define NEW new
#define NEWVEC new
#endif
#endif

#else
/** if DEVELOPMENT is not set, these macros will do nothing. */
#define SMTG_ASSERT(f)
#define SMTG_ASSERT_MSG(f, msg)
#define SMTG_WARNING(s)
#define SMTG_PRINTSYSERROR
#define SMTG_DEBUGSTR(s)
#define SMTG_VERIFY(f) f;
#define SMTG_VERIFY_IS(f, r) f;
#define SMTG_VERIFY_NOT(f, r) f;

#define SMTG_DBPRT0(a)
#define SMTG_DBPRT1(a, b)
#define SMTG_DBPRT2(a, b, c)
#define SMTG_DBPRT3(a, b, c, d)
#define SMTG_DBPRT4(a, b, c, d, e)
#define SMTG_DBPRT5(a, b, c, d, e, f)

#ifndef NEW
#define NEW new
#define NEWVEC new

#endif
#endif

// replace #if SMTG_CPPUNIT_TESTING
bool isSmtgUnitTesting ();
void setSmtgUnitTesting ();

#if !SMTG_RENAME_ASSERT
#if SMTG_OS_WINDOWS
#undef ASSERT
#endif

#define ASSERT				SMTG_ASSERT
#define WARNING				SMTG_WARNING
#define DEBUGSTR			SMTG_DEBUGSTR
#define VERIFY				SMTG_VERIFY
#define VERIFY_IS			SMTG_VERIFY_IS
#define VERIFY_NOT			SMTG_VERIFY_NOT
#define PRINTSYSERROR		SMTG_PRINTSYSERROR

#define DBPRT0				SMTG_DBPRT0
#define DBPRT1				SMTG_DBPRT1
#define DBPRT2				SMTG_DBPRT2
#define DBPRT3				SMTG_DBPRT3
#define DBPRT4				SMTG_DBPRT4
#define DBPRT5				SMTG_DBPRT5
#endif
//------------------------------------------------------------------------
// Project     : SDK Base
// Version     : 1.0
//
// Category    : Helpers
// Filename    : base/source/fdynlib.h
// Created by  : Steinberg, 1998
// Description : Dynamic library loading
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
// 
//   * Redistributions of source code must retain the above copyright notice, 
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation 
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this 
//     software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

//------------------------------------------------------------------------
/** @file base/source/fdynlib.h
	Platform independent dynamic library loading. */
//------------------------------------------------------------------------
#pragma once

#include "pluginterfaces/base/ftypes.h"
#include "base/source/fobject.h"

namespace Steinberg {

//------------------------------------------------------------------------
/** Platform independent dynamic library loader. */
//------------------------------------------------------------------------
class FDynLibrary : public FObject
{
public:
//------------------------------------------------------------------------
	/** Constructor.
	
		Loads the specified dynamic library.

		@param[in] name the path of the library to load.
		@param[in] addExtension if @c true append the platform dependent default extension to @c name.

		@remarks
		- If @c name specifies a full path, the FDynLibrary searches only that path for the library.
		- If @c name specifies a relative path or a name without path, 
		  FDynLibrary uses a standard search strategy of the current platform to find the library; 
		- If @c name is @c NULL the library is not loaded. 
		  - Use init() to load the library. */
	FDynLibrary (const tchar* name = nullptr, bool addExtension = true);
	
	/** Destructor.
		The destructor unloads the library.*/
	~FDynLibrary () override;

	/** Loads the library if not already loaded.
		This function is normally called by FDynLibrary(). 
		@remarks If the library is already loaded, this call has no effect. */
	bool init (const tchar* name, bool addExtension = true);

	/** Returns the address of the procedure @c name */ 
	void* getProcAddress (const char* name);

	/** Returns true when the library was successfully loaded. */
	bool isLoaded () {return isloaded;} 

	/** Unloads the library if it is loaded.
		This function is called by ~FDynLibrary (). */
	bool unload ();
	
	/** Returns the platform dependent representation of the library instance. */
	void* getPlatformInstance () const { return instance; } 

#if SMTG_OS_MACOS
	/** Returns @c true if the library is a bundle (Mac only). */
	bool isBundleLib () const { return isBundle; }
#endif

//------------------------------------------------------------------------
	OBJ_METHODS(FDynLibrary, FObject)
protected:	
	bool isloaded;

	void* instance;

#if SMTG_OS_MACOS
	void* firstSymbol;
	bool isBundle;
#endif
};

//------------------------------------------------------------------------
} // namespace Steinberg
//------------------------------------------------------------------------
// Project     : SDK Base
// Version     : 1.0
//
// Category    : Helpers
// Filename    : base/source/fobject.h
// Created by  : Steinberg, 2008
// Description : Basic Object implementing FUnknown
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
// 
//   * Redistributions of source code must retain the above copyright notice, 
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation 
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this 
//     software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

//------------------------------------------------------------------------
/** @file base/source/fobject.h
	Basic Object implementing FUnknown. */
//------------------------------------------------------------------------
#pragma once

#include "pluginterfaces/base/funknown.h"
#include "pluginterfaces/base/iupdatehandler.h"
#include "base/source/fdebug.h" // use of NEW

#define SMTG_DEPENDENCY_COUNT	DEVELOPMENT

namespace Steinberg {

//----------------------------------

using FClassID = FIDString;

//------------------------------------------------------------------------
// Basic FObject - implements FUnknown + IDependent
//------------------------------------------------------------------------
/** Implements FUnknown and IDependent.

FObject is a polymorphic class that implements IDependent (of SKI module) and therefore derived from
FUnknown, which is the most abstract base class of all.

All COM-like virtual methods of FUnknown such as queryInterface(), addRef(), release() are
implemented here. On top of that, dependency-related methods are implemented too.

Pointer casting is done via the template methods FCast, either FObject to FObject or FUnknown to
FObject.

FObject supports a new singleton concept, therefore these objects are deleted automatically upon
program termination.

- Runtime type information: An object can be queried at runtime, of what class it is. To do this
correctly, every class must override some methods. This is simplified by using the OBJ_METHODS
macros

@see
    - FUnknown
    - IDependent
    - IUpdateHandler
*/
//------------------------------------------------------------------------
class FObject : public IDependent
{
public:
	//------------------------------------------------------------------------
	FObject () = default;													///< default constructor...
	FObject (const FObject&)												///< overloaded constructor...
		: refCount (1)
#if SMTG_DEPENDENCY_COUNT
		, dependencyCount (0) 
#endif		
	{}			
	FObject& operator= (const FObject&) { return *this; }					///< overloads operator "=" as the reference assignment
	virtual ~FObject ();													///< destructor...

	// OBJECT_METHODS
	static inline FClassID getFClassID () {return "FObject";}				///< return Class ID as an ASCII string (statically)
	virtual FClassID isA () const {return FObject::getFClassID ();}			///< a local alternative to getFClassID ()
	virtual bool isA (FClassID s) const {return isTypeOf (s, false);}		///< evaluates if the passed ID is of the FObject type
	virtual bool isTypeOf (FClassID s, bool /*askBaseClass*/ = true) const {return classIDsEqual (s, FObject::getFClassID ());}
																			///< evaluates if the passed ID is of the FObject type
	int32 getRefCount () {return refCount;}									///< returns the current interface reference count
	FUnknown* unknownCast () {return this;}									///< get FUnknown interface from object

	// FUnknown
	tresult PLUGIN_API queryInterface (const TUID _iid, void** obj) SMTG_OVERRIDE; ///< please refer to FUnknown::queryInterface ()
	uint32 PLUGIN_API addRef () SMTG_OVERRIDE;						///< please refer to FUnknown::addref ()
	uint32 PLUGIN_API release () SMTG_OVERRIDE;						///< please refer to FUnknown::release ()

	// IDependent
	void PLUGIN_API update (FUnknown* /*changedUnknown*/, int32 /*message*/) SMTG_OVERRIDE {}
																			///< empty virtual method that should be overridden by derived classes for data updates upon changes	
	// IDependency
	virtual void addDependent (IDependent* dep);							///< adds dependency to the object
	virtual void removeDependent (IDependent* dep);							///< removes dependency from the object
	virtual void changed (int32 msg = kChanged);							///< Inform all dependents, that the object has changed.
	virtual void deferUpdate (int32 msg = kChanged);						///< Similar to triggerUpdates, except only delivered in idle (usefull in collecting updates).
	virtual void updateDone (int32 /* msg */) {}							///< empty virtual method that should be overridden by derived classes
	virtual bool isEqualInstance (FUnknown* d) {return this == d;}
	
	static void setUpdateHandler (IUpdateHandler* handler) {gUpdateHandler = handler;}	///< set method for the local attribute
	static IUpdateHandler* getUpdateHandler () {return gUpdateHandler;}					///< get method for the local attribute 

	// static helper functions
	static inline bool classIDsEqual (FClassID ci1, FClassID ci2);			///< compares (evaluates) 2 class IDs
	static inline FObject* unknownToObject (FUnknown* unknown);				///< pointer conversion from FUnknown to FObject

	/** Special UID that is used to cast an FUnknown pointer to a FObject */
	static const FUID iid;

//------------------------------------------------------------------------
protected:
	int32 refCount = 1;															///< COM-model local reference count
#if SMTG_DEPENDENCY_COUNT
	int16 dependencyCount = 0;
#endif
	static IUpdateHandler* gUpdateHandler;
};


//------------------------------------------------------------------------
// conversion from FUnknown to FObject
//------------------------------------------------------------------------
inline FObject* FObject::unknownToObject (FUnknown* unknown)
{
	FObject* object = nullptr;
	if (unknown) 
	{
		unknown->queryInterface (FObject::iid, (void**)&object);
		if (object)
			object->release (); // queryInterface has added ref		
	}
	return object;
}

//------------------------------------------------------------------------
inline bool FObject::classIDsEqual (FClassID ci1, FClassID ci2)
{
	return (ci1 && ci2) ? (strcmp (ci1, ci2) == 0) : false;
}

//-----------------------------------------------------------------------
/** FCast overload 1 - FObject to FObject */
//-----------------------------------------------------------------------
template <class C>
inline C* FCast (const FObject* object)
{
	if (object && object->isTypeOf (C::getFClassID (), true))
		return (C*) object;
	return 0;
}

//-----------------------------------------------------------------------
/** FCast overload 2 - FUnknown to FObject */
//-----------------------------------------------------------------------
template <class C>
inline C* FCast (FUnknown* unknown)
{
	FObject* object = FObject::unknownToObject (unknown);
	return FCast<C> (object);
}

//-----------------------------------------------------------------------
/** FUCast - casting from FUnknown to Interface */
//-----------------------------------------------------------------------
template <class C>
inline C* FUCast (FObject* object)
{
	return FUnknownPtr<C> (object ? object->unknownCast () : nullptr);
}

template <class C>
inline C* FUCast (FUnknown* object)
{
	return FUnknownPtr<C> (object);
}

//------------------------------------------------------------------------
/** @name Convenience methods that call release or delete respectively
	on a pointer if it is non-zero, and then set the pointer to zero.
	Note: you should prefer using IPtr or OPtr instead of these methods
	whenever possible. 
	<b>Examples:</b>
	@code
	~Foo ()
	{
		// instead of ...
		if (somePointer)
		{
			somePointer->release ();
			somePointer = 0;
		}
		// ... just being lazy I write
		SafeRelease (somePointer)
	}
	@endcode
*/
///@{
//-----------------------------------------------------------------------
template <class I>
inline void SafeRelease (I *& ptr) 
{ 
	if (ptr) 
	{
		ptr->release (); 
		ptr = 0;
	}
}

//-----------------------------------------------------------------------
template <class I>
inline void SafeRelease (IPtr<I> & ptr) 
{ 
	ptr = 0;
}


//-----------------------------------------------------------------------
template <class T>
inline void SafeDelete (T *& ptr)
{
	if (ptr) 
	{
		delete ptr;
		ptr = 0;
	}
}
///@}

//-----------------------------------------------------------------------
template <class T>
inline void AssignShared (T*& dest, T* newPtr)
{
	if (dest == newPtr)
		return;
	
	if (dest) 
		dest->release (); 
	dest = newPtr; 
	if (dest) 
		dest->addRef ();
}

//-----------------------------------------------------------------------
template <class T>
inline void AssignSharedDependent (IDependent* _this, T*& dest, T* newPtr)
{
	if (dest == newPtr)
		return;

	if (dest)
		dest->removeDependent (_this);
	AssignShared (dest, newPtr);
	if (dest)
		dest->addDependent (_this);
}

//-----------------------------------------------------------------------
template <class T>
inline void AssignSharedDependent (IDependent* _this, IPtr<T>& dest, T* newPtr)
{
	if (dest == newPtr)
		return;

	if (dest)
		dest->removeDependent (_this);
	dest = newPtr;
	if (dest)
		dest->addDependent (_this);
}
	
//-----------------------------------------------------------------------
template <class T>
inline void SafeReleaseDependent (IDependent* _this, T*& dest)
{
	if (dest)
		dest->removeDependent (_this);
	SafeRelease (dest);
}
	
//-----------------------------------------------------------------------
template <class T>
inline void SafeReleaseDependent (IDependent* _this, IPtr<T>& dest)
{
	if (dest)
		dest->removeDependent (_this);
	SafeRelease (dest);
}


//------------------------------------------------------------------------
/** Automatic creation and destruction of singleton instances. */
namespace Singleton {
	/** registers an instance (type FObject) */
	void registerInstance (FObject** o);

	/** Returns true when singleton instances were already released. */
	bool isTerminated ();

	/** lock and unlock the singleton registration for multi-threading safety */
	void lockRegister ();
	void unlockRegister ();
}

//------------------------------------------------------------------------
} // namespace Steinberg

//-----------------------------------------------------------------------
#define SINGLETON(ClassName)	\
	static ClassName* instance (bool create = true) \
	{ \
		static Steinberg::FObject* inst = nullptr; \
		if (inst == nullptr && create && Steinberg::Singleton::isTerminated () == false) \
		{	\
			Steinberg::Singleton::lockRegister (); \
			if (inst == nullptr) \
			{ \
				inst = NEW ClassName; \
				Steinberg::Singleton::registerInstance (&inst); \
			} \
			Steinberg::Singleton::unlockRegister (); \
		}	\
		return (ClassName*)inst; \
	}

//-----------------------------------------------------------------------
#define OBJ_METHODS(className, baseClass)								\
	static inline Steinberg::FClassID getFClassID () {return (#className);}		\
	virtual Steinberg::FClassID isA () const SMTG_OVERRIDE {return className::getFClassID ();}	\
	virtual bool isA (Steinberg::FClassID s) const SMTG_OVERRIDE {return isTypeOf (s, false);}	\
	virtual bool isTypeOf (Steinberg::FClassID s, bool askBaseClass = true) const SMTG_OVERRIDE	\
    {  return (FObject::classIDsEqual (s, #className) ? true : (askBaseClass ? baseClass::isTypeOf (s, true) : false)); } 

//------------------------------------------------------------------------
/** Delegate refcount functions to BaseClass.
	BaseClase must implement ref counting. 
*/
//------------------------------------------------------------------------
#define REFCOUNT_METHODS(BaseClass) \
virtual Steinberg::uint32 PLUGIN_API addRef ()SMTG_OVERRIDE{ return BaseClass::addRef (); } \
virtual Steinberg::uint32 PLUGIN_API release ()SMTG_OVERRIDE{ return BaseClass::release (); }

//------------------------------------------------------------------------
/** @name Macros to implement FUnknown::queryInterface ().

	<b>Examples:</b>
	@code
	class Foo : public FObject, public IFoo2, public IFoo3 
	{
	    ...
		DEFINE_INTERFACES
	        DEF_INTERFACE (IFoo2)
	        DEF_INTERFACE (IFoo3)
	    END_DEFINE_INTERFACES (FObject)
	    REFCOUNT_METHODS(FObject)
	    // Implement IFoo2 interface ...
	    // Implement IFoo3 interface ...
	    ...
	};
	@endcode	
*/
///@{
//------------------------------------------------------------------------
/** Start defining interfaces. */
//------------------------------------------------------------------------
#define DEFINE_INTERFACES \
Steinberg::tresult PLUGIN_API queryInterface (const Steinberg::TUID iid, void** obj) SMTG_OVERRIDE \
{

//------------------------------------------------------------------------
/** Add a interfaces. */
//------------------------------------------------------------------------
#define DEF_INTERFACE(InterfaceName) \
	QUERY_INTERFACE (iid, obj, InterfaceName::iid, InterfaceName)

//------------------------------------------------------------------------
/** End defining interfaces. */
//------------------------------------------------------------------------
#define END_DEFINE_INTERFACES(BaseClass) \
	return BaseClass::queryInterface (iid, obj); \
}
///@}

//------------------------------------------------------------------------
/** @name Convenient macros to implement Steinberg::FUnknown::queryInterface ().
	<b>Examples:</b>
	@code
	class Foo : public FObject, public IFoo2, public IFoo3 
	{
	    ...
	    DEF_INTERFACES_2(IFoo2,IFoo3,FObject)
	    REFCOUNT_METHODS(FObject)
	    ...
	};
	@endcode
*/
///@{
//------------------------------------------------------------------------
#define DEF_INTERFACES_1(InterfaceName,BaseClass) \
DEFINE_INTERFACES \
DEF_INTERFACE (InterfaceName) \
END_DEFINE_INTERFACES (BaseClass)

//------------------------------------------------------------------------
#define DEF_INTERFACES_2(InterfaceName1,InterfaceName2,BaseClass) \
DEFINE_INTERFACES \
DEF_INTERFACE (InterfaceName1) \
DEF_INTERFACE (InterfaceName2) \
END_DEFINE_INTERFACES (BaseClass)

//------------------------------------------------------------------------
#define DEF_INTERFACES_3(InterfaceName1,InterfaceName2,InterfaceName3,BaseClass) \
DEFINE_INTERFACES \
DEF_INTERFACE (InterfaceName1) \
DEF_INTERFACE (InterfaceName2) \
DEF_INTERFACE (InterfaceName3) \
END_DEFINE_INTERFACES (BaseClass)

//------------------------------------------------------------------------
#define DEF_INTERFACES_4(InterfaceName1,InterfaceName2,InterfaceName3,InterfaceName4,BaseClass) \
	DEFINE_INTERFACES \
	DEF_INTERFACE (InterfaceName1) \
	DEF_INTERFACE (InterfaceName2) \
	DEF_INTERFACE (InterfaceName3) \
	DEF_INTERFACE (InterfaceName4) \
	END_DEFINE_INTERFACES (BaseClass)
///@}

//------------------------------------------------------------------------
/** @name Convenient macros to implement Steinberg::FUnknown methods.
	<b>Examples:</b>
	@code
	class Foo : public FObject, public IFoo2, public IFoo3 
	{
	    ...
	    FUNKNOWN_METHODS2(IFoo2,IFoo3,FObject)
	    ...
	};
	@endcode
*/
///@{
#define FUNKNOWN_METHODS(InterfaceName,BaseClass) \
DEF_INTERFACES_1(InterfaceName,BaseClass) \
REFCOUNT_METHODS(BaseClass)

#define FUNKNOWN_METHODS2(InterfaceName1,InterfaceName2,BaseClass) \
DEF_INTERFACES_2(InterfaceName1,InterfaceName2,BaseClass) \
REFCOUNT_METHODS(BaseClass)

#define FUNKNOWN_METHODS3(InterfaceName1,InterfaceName2,InterfaceName3,BaseClass) \
DEF_INTERFACES_3(InterfaceName1,InterfaceName2,InterfaceName3,BaseClass) \
REFCOUNT_METHODS(BaseClass)

#define FUNKNOWN_METHODS4(InterfaceName1,InterfaceName2,InterfaceName3,InterfaceName4,BaseClass) \
DEF_INTERFACES_4(InterfaceName1,InterfaceName2,InterfaceName3,InterfaceName4,BaseClass) \
REFCOUNT_METHODS(BaseClass)
///@}


//------------------------------------------------------------------------
//------------------------------------------------------------------------
#if COM_COMPATIBLE
//------------------------------------------------------------------------
/** @name Macros to implement IUnknown interfaces with FObject.
	<b>Examples:</b>
	@code
	class MyEnumFormat : public FObject, IEnumFORMATETC
	{
	    ...
		COM_UNKNOWN_METHODS (IEnumFORMATETC, IUnknown)
	    ...
	};
	@endcode
*/
///@{
//------------------------------------------------------------------------
#define IUNKNOWN_REFCOUNT_METHODS(BaseClass) \
STDMETHOD_ (ULONG, AddRef) (void) {return BaseClass::addRef ();} \
STDMETHOD_ (ULONG, Release) (void) {return BaseClass::release ();}

//------------------------------------------------------------------------
#define COM_QUERY_INTERFACE(iid, obj, InterfaceName)     \
if (riid == __uuidof(InterfaceName))                     \
{                                                        \
	addRef ();                                           \
	*obj = (InterfaceName*)this;                         \
	return kResultOk;                                    \
}

//------------------------------------------------------------------------
#define COM_OBJECT_QUERY_INTERFACE(InterfaceName,BaseClass)        \
STDMETHOD (QueryInterface) (REFIID riid, void** object)            \
{                                                                  \
	COM_QUERY_INTERFACE (riid, object, InterfaceName)              \
	return BaseClass::queryInterface ((FIDString)&riid, object);   \
}

//------------------------------------------------------------------------
#define COM_UNKNOWN_METHODS(InterfaceName,BaseClass) \
COM_OBJECT_QUERY_INTERFACE(InterfaceName,BaseClass) \
IUNKNOWN_REFCOUNT_METHODS(BaseClass)
///@}

#endif // COM_COMPATIBLE
//------------------------------------------------------------------------
// Project     : SDK Base
// Version     : 1.0
//
// Category    : Helpers
// Filename    : base/source/fstdmethods.h
// Created by  : Steinberg, 2007
// Description : Convenient macros to create setter and getter methods.
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
// 
//   * Redistributions of source code must retain the above copyright notice, 
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation 
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this 
//     software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

//------------------------------------------------------------------------
/** @file base/source/fstdmethods.h
	Convenient macros to create setter and getter methods. */
//------------------------------------------------------------------------
#pragma once

//----------------------------------------------------------------------------------
/**	@name Methods for flags.
	Macros to create setter and getter methods for flags.

	Usage example with DEFINE_STATE: 
										\code
	class MyClass
	{
	public:
		MyClass () : flags (0) {}
		DEFINE_FLAG (flags, isFlagged, 1<<0)
		DEFINE_FLAG (flags, isMine, 1<<1)
	private:
		uint32 flags;
	};
	void someFunction ()
	{
		MyClass c;
		if (c.isFlagged ())        // check the flag
			c.isFlagged (false);   // set the flag
	}
										\endcode
*/
//----------------------------------------------------------------------------------
///@{

/** Create Methods with @c get and @c set prefix. */
#define DEFINE_STATE(flagVar,methodName,value)\
void set##methodName (bool state) { if (state) flagVar |= (value); else flagVar &= ~(value); }\
bool get##methodName ()const { return (flagVar & (value)) != 0; }

/** Create Methods with @c get prefix.
	There is only a 'get' method. */
#define DEFINE_GETSTATE(flagVar,methodName,value)\
bool get##methodName ()const { return (flagVar & (value)) != 0; }

/** Create Methods. 
	Same name for the getter and setter. */
#define DEFINE_FLAG(flagVar,methodName,value)\
void methodName (bool state) { if (state) flagVar |= (value); else flagVar &= ~(value); }\
bool methodName ()const { return (flagVar & (value)) != 0; }

/** Create Methods.
	There is only a 'get' method. */
#define DEFINE_GETFLAG(flagVar,methodName,value)\
bool methodName ()const { return (flagVar & (value)) != 0; }


/** Create @c static Methods. 
	Same name for the getter and setter. */
#define DEFINE_FLAG_STATIC(flagVar,methodName,value)\
static void methodName (bool __state) { if (__state) flagVar |= (value); else flagVar &= ~(value); }\
static bool methodName () { return (flagVar & (value)) != 0; }
///@}

//----------------------------------------------------------------------------------
/**	@name Methods for data members.
	Macros to create setter and getter methods for class members.
	
	<b>Examples:</b>
										\code	
	class MyClass
	{
	public:
		DATA_MEMBER (double, distance, Distance)
		STRING_MEMBER (Steinberg::String, name, Name)
		SHARED_MEMBER (FUnknown, userData, UserData)
		CLASS_MEMBER (Steinberg::Buffer, bufferData, BufferData)
		POINTER_MEMBER (Steinberg::FObject, refOnly, RefOnly)  
	};
										\endcode
*/
//--------------------------------------------------------------------------------------
///@{

/** Build-in member (pass by value). */
#define DATA_MEMBER(type,varName,methodName)\
public:void set##methodName (type v) { varName = v;}\
type get##methodName ()const { return varName; }\
protected: type varName; public:

//** Object member (pass by reference). */
#define CLASS_MEMBER(type,varName,methodName)\
public:void set##methodName (const type& v){ varName = v;}\
const type& get##methodName () const { return varName; }\
protected: type varName; public:

//** Simple pointer. */
#define POINTER_MEMBER(type,varName,methodName)\
public:void set##methodName (type* ptr){ varName = ptr;}\
type* get##methodName ()const { return varName; }\
private: type* varName; public:

//** Shared member - FUnknown / FObject /  etc */
#define SHARED_MEMBER(type,varName,methodName)\
public:void set##methodName (type* v){ varName = v;}\
type* get##methodName ()const { return varName; }\
private: IPtr<type> varName; public:

//** Owned member - FUnknown / FObject / CmObject etc */
#define OWNED_MEMBER(type,varName,methodName)\
public:void set##methodName (type* v){ varName = v;}\
type* get##methodName ()const { return varName; }\
private: OPtr<type> varName; public:

//** tchar* / String class member - set by const tchar*, return by reference */
#define STRING_MEMBER(type,varName,methodName)\
public:void set##methodName (const tchar* v){ varName = v;}\
const type& get##methodName () const { return varName; }\
protected: type varName; public:

//** char8* / String class member - set by const char8*, return by reference */
#define STRING8_MEMBER(type,varName,methodName)\
public:void set##methodName (const char8* v){ varName = v;}\
const type& get##methodName () const { return varName; }\
protected: type varName; public:

//** Standard String Member Steinberg::String */
#define STRING_MEMBER_STD(varName,methodName) STRING_MEMBER(Steinberg::String,varName,methodName)
#define STRING8_MEMBER_STD(varName,methodName) STRING8_MEMBER(Steinberg::String,varName,methodName)

///@}


// obsolete names
#define DEFINE_VARIABLE(type,varName,methodName) DATA_MEMBER(type,varName,methodName)
#define DEFINE_POINTER(type,varName,methodName) POINTER_MEMBER(type,varName,methodName)
#define DEFINE_MEMBER(type,varName,methodName) CLASS_MEMBER(type,varName,methodName)





//------------------------------------------------------------------------
// for implementing comparison operators using a class member or a compare method:
//------------------------------------------------------------------------
#define COMPARE_BY_MEMBER_METHODS(className,memberName) \
	bool operator == (const className& other) const {return (memberName == other.memberName);} \
	bool operator != (const className& other) const {return (memberName != other.memberName);} \
	bool operator < (const className& other) const {return (memberName < other.memberName);} \
	bool operator > (const className& other) const {return (memberName > other.memberName);} \
	bool operator <= (const className& other) const {return (memberName <= other.memberName);} \
	bool operator >= (const className& other) const {return (memberName >= other.memberName);}

#define COMPARE_BY_MEMORY_METHODS(className) \
	bool operator == (const className& other) const {return memcmp (this, &other, sizeof (className)) == 0;} \
	bool operator != (const className& other) const {return memcmp (this, &other, sizeof (className)) != 0;} \
	bool operator < (const className& other) const {return memcmp (this, &other, sizeof (className)) < 0;} \
	bool operator > (const className& other) const {return memcmp (this, &other, sizeof (className)) > 0;} \
	bool operator <= (const className& other) const {return memcmp (this, &other, sizeof (className)) <= 0;} \
	bool operator >= (const className& other) const {return memcmp (this, &other, sizeof (className)) >= 0;}

#define COMPARE_BY_COMPARE_METHOD(className,methodName) \
	bool operator == (const className& other) const {return methodName (other) == 0;} \
	bool operator != (const className& other) const {return methodName (other) != 0;} \
	bool operator < (const className& other) const {return methodName (other) < 0;} \
	bool operator > (const className& other) const {return methodName (other) > 0;} \
	bool operator <= (const className& other) const {return methodName (other) <= 0; } \
	bool operator >= (const className& other) const {return methodName (other) >= 0; }
//------------------------------------------------------------------------
// Project     : SDK Base
// Version     : 1.0
//
// Category    : Helpers
// Filename    : base/source/fstreamer.h
// Created by  : Steinberg, 12/2005
// Description : Extract of typed stream i/o methods from FStream
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
// 
//   * Redistributions of source code must retain the above copyright notice, 
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation 
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this 
//     software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#pragma once

#include "pluginterfaces/base/funknown.h"

namespace Steinberg {

//------------------------------------------------------------------------
enum FSeekMode
{
	kSeekSet,
	kSeekCurrent,
	kSeekEnd
};

//------------------------------------------------------------------------
// FStreamer
//------------------------------------------------------------------------
/** Byteorder-aware base class for typed stream i/o. */
//------------------------------------------------------------------------
class FStreamer
{
public:
//------------------------------------------------------------------------
	FStreamer (int16 byteOrder = BYTEORDER);
	virtual ~FStreamer () {}

	/** @name Implementing class must override. */
	///@{
	virtual TSize readRaw (void*, TSize) = 0;          ///< Read one buffer of size.
	virtual TSize writeRaw (const void*, TSize) = 0;   ///< Write one buffer of size.
	virtual int64 seek (int64, FSeekMode) = 0;         ///< Set file position for stream.
	virtual int64 tell () = 0;                         ///< Return current file position.
	///@}

	/** @name Streams are byteOrder aware. */
	///@{
	inline void setByteOrder (int32 e) { byteOrder = (int16)e; }
	inline int32 getByteOrder () const { return byteOrder; }
	///@}

	/** @name read and write int8 and char. */
	///@{
	bool writeChar8 (char8); 
	bool readChar8 (char8&);                         
	bool writeUChar8 (unsigned char);   
	bool readUChar8 (unsigned char&);
	bool writeChar16 (char16 c); 
	bool readChar16 (char16& c); 

	bool writeInt8 (int8 c);   
	bool readInt8 (int8& c);
	bool writeInt8u (uint8 c);   
	bool readInt8u (uint8& c);
	///@}

	/** @name read and write int16. */
	///@{
	bool writeInt16 (int16);
	bool readInt16 (int16&);
	bool writeInt16Array (const int16* array, int32 count);  
	bool readInt16Array (int16* array, int32 count);  
	bool writeInt16u (uint16);
	bool readInt16u (uint16&);
	bool writeInt16uArray (const uint16* array, int32 count);  
	bool readInt16uArray (uint16* array, int32 count);  
	///@}

	/** @name read and write int32. */
	///@{
	bool writeInt32 (int32);  
	bool readInt32 (int32&);  
	bool writeInt32Array (const int32* array, int32 count);  
	bool readInt32Array (int32* array, int32 count);  
	bool writeInt32u (uint32);
	bool readInt32u (uint32&); 
	bool writeInt32uArray (const uint32* array, int32 count);  
	bool readInt32uArray (uint32* array, int32 count);  
	///@}

	/** @name read and write int64. */
	///@{
	bool writeInt64 (int64);
	bool readInt64 (int64&);
	bool writeInt64Array (const int64* array, int32 count);  
	bool readInt64Array (int64* array, int32 count);  
	bool writeInt64u (uint64);
	bool readInt64u (uint64&);
	bool writeInt64uArray (const uint64* array, int32 count);  
	bool readInt64uArray (uint64* array, int32 count);  
	///@}

	/** @name read and write float and float array. */
	///@{
	bool writeFloat (float);
	bool readFloat (float&);
	bool writeFloatArray (const float* array, int32 count);  
	bool readFloatArray (float* array, int32 count);  
	///@}

	/** @name read and write double and double array. */
	///@{
	bool writeDouble (double);                         
	bool readDouble (double&);                         
	bool writeDoubleArray (const double* array, int32 count);  
	bool readDoubleArray (double* array, int32 count);  
	///@}

	/** @name read and write Boolean. */
	///@{
	bool writeBool (bool);                                   ///< Write one boolean
	bool readBool (bool&);                                   ///< Read one bool.
	///@}

	/** @name read and write Strings. */
	///@{
	TSize writeString8 (const char8* ptr, bool terminate = false); ///< a direct output function writing only one string (ascii 8bit)
	TSize readString8 (char8* ptr, TSize size);				///< a direct input function reading only one string (ascii) (ended by a \n or \0 or eof)
	
	bool writeStr8 (const char8* ptr);				       ///< write a string length (strlen) and string itself
	char8* readStr8 ();									   ///< read a string length and string text (The return string must be deleted when use is finished)

	static int32 getStr8Size (const char8* ptr);	       ///< returns the size of a saved string

	bool writeStringUtf8 (const tchar* ptr);               ///< always terminated, converts to utf8 if non ascii characters are in string
	int32 readStringUtf8 (tchar* ptr, int32 maxSize);      ///< read a UTF8 string
	///@}

	bool skip (uint32 bytes);
	bool pad (uint32 bytes);


//------------------------------------------------------------------------
protected:
	int16 byteOrder;
};


//------------------------------------------------------------------------
/** FStreamSizeHolder Declaration
	remembers size of stream chunk for backward compatibility.
	
	<b>Example:</b>
	@code
	externalize (a)
	{
		FStreamSizeHolder sizeHolder;
		sizeHolder.beginWrite ();	// sets start mark, writes dummy size
		a << ....
		sizeHolder.endWrite ();		// jumps to start mark, updates size, jumps back here
	}

	internalize (a)
	{
		FStreamSizeHolder sizeHolder;
		sizeHolder.beginRead ();	// reads size, mark
		a >> ....
		sizeHolder.endRead ();		// jumps forward if new version has larger size
	}
	@endcode
*/
//------------------------------------------------------------------------
class FStreamSizeHolder
{
public:
	FStreamSizeHolder (FStreamer &s);

	void beginWrite ();	///< remembers position and writes 0
	int32 endWrite ();	///< writes and returns size (since the start marker)
	int32 beginRead ();	///< returns size
	void endRead ();	///< jump to end of chunk

protected:
	FStreamer &stream;
	int64 sizePos;
};

class IBStream;

//------------------------------------------------------------------------
// IBStreamer
//------------------------------------------------------------------------
/** Wrapper class for typed reading/writing from or to IBStream. 
	Can be used framework-independent in plug-ins. */
//------------------------------------------------------------------------
class IBStreamer: public FStreamer
{
public:
//------------------------------------------------------------------------
	/** Constructor for a given IBSTream and a byteOrder. */
	IBStreamer (IBStream* stream, int16 byteOrder = BYTEORDER);

	IBStream* getStream () { return stream; }	///< Returns the associated IBStream.

	// FStreamer overrides:					
	TSize readRaw (void*, TSize) SMTG_OVERRIDE;				///< Read one buffer of size.
	TSize writeRaw (const void*, TSize) SMTG_OVERRIDE;		///< Write one buffer of size.
	int64 seek (int64, FSeekMode) SMTG_OVERRIDE;			///< Set file position for stream.
	int64 tell () SMTG_OVERRIDE;							///< Return current file position.
//------------------------------------------------------------------------
protected:
	IBStream* stream;
};

//------------------------------------------------------------------------
} // namespace Steinberg
//------------------------------------------------------------------------
// Project     : SDK Base
// Version     : 1.0
//
// Category    : Helpers
// Filename    : base/source/fstring.h
// Created by  : Steinberg, 2008
// Description : String class
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
// 
//   * Redistributions of source code must retain the above copyright notice, 
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation 
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this 
//     software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#pragma once

#include "pluginterfaces/base/ftypes.h"
#include "pluginterfaces/base/fstrdefs.h"
#include "pluginterfaces/base/istringresult.h"
#include "pluginterfaces/base/ipersistent.h"

#include "base/source/fobject.h"

#include <cstdarg>

namespace Steinberg {

class FVariant;
class String;

#ifdef UNICODE
static const bool kWideStringDefault = true;
#else
static const bool kWideStringDefault = false;
#endif

static const uint16 kBomUtf16 = 0xFEFF;					///< UTF16 Byte Order Mark
static const char8* const kBomUtf8 = "\xEF\xBB\xBF";	///< UTF8 Byte Order Mark
static const int32 kBomUtf8Length = 3;


enum MBCodePage
{
	kCP_ANSI = 0,			///< Default ANSI codepage.
	kCP_MAC_ROMAN = 2,		///< Default Mac codepage.

	kCP_ANSI_WEL = 1252,	///< West European Latin Encoding.
	kCP_MAC_CEE = 10029,	///< Mac Central European Encoding.
	kCP_Utf8 = 65001,		///< UTF8 Encoding.
	kCP_ShiftJIS = 932,		///< Shifted Japan Industrial Standard Encoding.
	kCP_US_ASCII = 20127,	///< US-ASCII (7-bit).

	kCP_Default = kCP_ANSI	///< Default ANSI codepage.
};

enum UnicodeNormalization
{
	kUnicodeNormC,	///< Unicode normalization Form C, canonical composition. 
	kUnicodeNormD,	///< Unicode normalization Form D, canonical decomposition. 
	kUnicodeNormKC,	///< Unicode normalization form KC, compatibility composition. 
	kUnicodeNormKD 	///< Unicode normalization form KD, compatibility decomposition. 
};

//------------------------------------------------------------------------
// Helper functions to create hash codes from string data.
//------------------------------------------------------------------------
extern uint32 hashString8 (const char8* s, uint32 m);
extern uint32 hashString16 (const char16* s, uint32 m);
inline uint32 hashString (const tchar* s, uint32 m)
{
#ifdef UNICODE
	return hashString16 (s, m);
#else
	return hashString8 (s, m);
#endif
}


//-----------------------------------------------------------------------------
/** Invariant String. 
@ingroup adt

A base class which provides methods to work with its
member string. Neither of the operations allows modifying the member string and 
that is why all operation are declared as const. 

There are operations for access, comparison, find, numbers and conversion.

Almost all operations exist in three versions for char8, char16 and the 
polymorphic type tchar. The type tchar can either be char8 or char16 depending
on whether UNICODE is activated or not.*/
//-----------------------------------------------------------------------------
class ConstString
{
public:
//-----------------------------------------------------------------------------
	ConstString (const char8* str, int32 length = -1);	///< Assign from string of type char8 (length=-1: all)
	ConstString (const char16* str, int32 length = -1);	///< Assign from string of type char16 (length=-1: all)
	ConstString (const ConstString& str, int32 offset = 0, int32 length = -1); ///< Copy constructor (length=-1: all).
	ConstString (const FVariant& var);	///< Assign a string from FVariant
	ConstString ();
	virtual ~ConstString () {}			///< Destructor.

	// access -----------------------------------------------------------------
	virtual int32 length () const {return static_cast<int32> (len);}	///< Return length of string
	inline bool isEmpty () const {return buffer == nullptr || len == 0;}		///< Return true if string is empty

	operator const char8* () const {return text8 ();} 							///< Returns pointer to string of type char8 (no modification allowed)
	operator const char16* () const {return text16 ();}							///< Returns pointer to string of type char16(no modification allowed)
	inline tchar operator[] (short idx) const {return getChar (static_cast<uint32> (idx));} ///< Returns character at 'idx'
	inline tchar operator[] (long idx) const {return getChar (static_cast<uint32> (idx));}
	inline tchar operator[] (int idx) const {return getChar (static_cast<uint32> (idx));}
	inline tchar operator[] (unsigned short idx) const {return getChar (idx);}
	inline tchar operator[] (unsigned long idx) const {return getChar (static_cast<uint32> (idx));}
	inline tchar operator[] (unsigned int idx) const {return getChar (idx);}

	inline virtual const char8* text8 () const;		///< Returns pointer to string of type char8
	inline virtual const char16* text16 () const;	///< Returns pointer to string of type char16
	inline virtual const tchar* text () const;		///< Returns pointer to string of type tchar
	inline virtual const void* ptr () const {return buffer;}	///< Returns pointer to string of type void

	inline virtual char8 getChar8 (uint32 index) const;		///< Returns character of type char16 at 'index'
	inline virtual char16 getChar16 (uint32 index) const;	///< Returns character of type char8 at 'index'
	inline tchar getChar (uint32 index) const;				///< Returns character of type tchar at 'index'
	inline tchar getCharAt (uint32 index) const;			///< Returns character of type tchar at 'index', no conversion!

	bool testChar8 (uint32 index, char8 c) const;			///< Returns true if character is equal at position 'index'
	bool testChar16 (uint32 index, char16 c) const;
	inline bool testChar (uint32 index, char8 c) const {return testChar8 (index, c);}
	inline bool testChar (uint32 index, char16 c) const {return testChar16 (index, c);}

	bool extract (String& result, uint32 idx, int32 n = -1) const;		///< Get n characters long substring starting at index (n=-1: until end)
	int32 copyTo8 (char8* str, uint32 idx = 0, int32 n = -1) const;
	int32 copyTo16 (char16* str, uint32 idx = 0, int32 n = -1) const;
	int32 copyTo (tchar* str, uint32 idx = 0, int32 n = -1) const;
	void copyTo (IStringResult* result) const;							///< Copies whole member string
	void copyTo (IString& string) const;							    ///< Copies whole member string

	inline uint32 hash (uint32 tsize) const 
	{
		return isWide ? hashString16 (buffer16, tsize) : hashString8 (buffer8, tsize) ;  
	}
	//-------------------------------------------------------------------------

	// compare ----------------------------------------------------------------
	enum CompareMode
	{
		kCaseSensitive,		///< Comparison is done with regard to character's case
		kCaseInsensitive	///< Comparison is done without regard to character's case
	};

	int32 compareAt (uint32 index, const ConstString& str, int32 n = -1, CompareMode m = kCaseSensitive) const; 				///< Compare n characters of str with n characters of this starting at index (return: see above)
	int32 compare (const ConstString& str, int32 n, CompareMode m = kCaseSensitive) const;										///< Compare n characters of str with n characters of this (return: see above)
	int32 compare (const ConstString& str, CompareMode m = kCaseSensitive) const;												///< Compare all characters of str with this (return: see above)

	int32 naturalCompare (const ConstString& str, CompareMode mode = kCaseSensitive) const;

	bool startsWith (const ConstString& str, CompareMode m = kCaseSensitive) const;												///< Check if this starts with str
	bool endsWith (const ConstString& str, CompareMode m = kCaseSensitive) const;												///< Check if this ends with str
	bool contains (const ConstString& str, CompareMode m = kCaseSensitive) const;												///< Check if this contains str											

	// static methods
	static bool isCharSpace (char8 character);	   ///< Returns true if character is a space
	static bool isCharSpace (char16 character);    ///< @copydoc isCharSpace(const char8)
	static bool isCharAlpha (char8 character);	   ///< Returns true if character is an alphabetic character
	static bool isCharAlpha (char16 character);    ///< @copydoc isCharAlpha(const char8)
	static bool isCharAlphaNum (char8 character);  ///< Returns true if character is an alphanumeric character
	static bool isCharAlphaNum (char16 character); ///< @copydoc isCharAlphaNum(const char8)
	static bool isCharDigit (char8 character);	   ///< Returns true if character is a number
	static bool isCharDigit (char16 character);    ///< @copydoc isCharDigit(const char8)
	static bool isCharAscii (char8 character);     ///< Returns true if character is in ASCII range
	static bool isCharAscii (char16 character);    ///< Returns true if character is in ASCII range
	static bool isCharUpper (char8 character);
	static bool isCharUpper (char16 character);
	static bool isCharLower (char8 character);
	static bool isCharLower (char16 character);
	//-------------------------------------------------------------------------

	/** @name Find first occurrence of n characters of str in this (n=-1: all) ending at endIndex (endIndex = -1: all)*/
	///@{
	inline int32 findFirst (const ConstString& str, int32 n = -1, CompareMode m = kCaseSensitive, int32 endIndex = -1) const {return findNext (0, str, n, m, endIndex);}
	inline int32 findFirst (char8 c, CompareMode m = kCaseSensitive, int32 endIndex = -1) const {return findNext (0, c, m, endIndex);}
	inline int32 findFirst (char16 c, CompareMode m = kCaseSensitive, int32 endIndex = -1) const {return findNext (0, c, m, endIndex);}
	///@}
	/** @name Find next occurrence of n characters of str starting at startIndex in this (n=-1: all) ending at endIndex (endIndex = -1: all)*/
	///@{
	int32 findNext (int32 startIndex, const ConstString& str, int32 n = -1, CompareMode = kCaseSensitive, int32 endIndex = -1) const;
	int32 findNext (int32 startIndex, char8 c, CompareMode = kCaseSensitive, int32 endIndex = -1) const;
	int32 findNext (int32 startIndex, char16 c, CompareMode = kCaseSensitive, int32 endIndex = -1) const;
	///@}
	/** @name Find previous occurrence of n characters of str starting at startIndex in this (n=-1: all) */
	///@{
	int32 findPrev (int32 startIndex, const ConstString& str, int32 n = -1, CompareMode = kCaseSensitive) const;
	int32 findPrev (int32 startIndex, char8 c, CompareMode = kCaseSensitive) const;
	int32 findPrev (int32 startIndex, char16 c, CompareMode = kCaseSensitive) const;
	///@}
	
	inline int32 findLast (const ConstString& str, int32 n = -1, CompareMode m = kCaseSensitive) const {return findPrev (-1, str, n, m);}	///< Find last occurrence of n characters of str in this (n=-1: all)
	inline int32 findLast (char8 c, CompareMode m = kCaseSensitive) const {return findPrev (-1, c, m);}
	inline int32 findLast (char16 c, CompareMode m = kCaseSensitive) const {return findPrev (-1, c, m);}

	int32 countOccurences (char8 c, uint32 startIndex, CompareMode = kCaseSensitive) const; ///< Counts occurences of c within this starting at index
	int32 countOccurences (char16 c, uint32 startIndex, CompareMode = kCaseSensitive) const;
	int32 getFirstDifferent (const ConstString& str, CompareMode = kCaseSensitive) const;	///< Returns position of first different character
	//-------------------------------------------------------------------------

	// numbers ----------------------------------------------------------------
	bool isDigit (uint32 index) const;	///< Returns true if character at position is a digit
	bool scanFloat (double& value, uint32 offset = 0, bool scanToEnd = true) const;		///< Converts string to double value starting at offset
	bool scanInt64 (int64& value, uint32 offset = 0, bool scanToEnd = true) const;		///< Converts string to int64 value starting at offset
	bool scanUInt64 (uint64& value, uint32 offset = 0, bool scanToEnd = true) const;	///< Converts string to uint64 value starting at offset
	bool scanInt32 (int32& value, uint32 offset = 0, bool scanToEnd = true) const;		///< Converts string to int32 value starting at offset
	bool scanUInt32 (uint32& value, uint32 offset = 0, bool scanToEnd = true) const;	///< Converts string to uint32 value starting at offset
	bool scanHex (uint8& value, uint32 offset = 0, bool scanToEnd = true) const;		///< Converts string to hex/uint8 value starting at offset

	int32 getTrailingNumberIndex (uint32 width = 0) const;	///< Returns start index of trailing number
	int64 getTrailingNumber (int64 fallback = 0) const;		///< Returns result of scanInt64 or the fallback
	int64 getNumber () const;								///< Returns result of scanInt64

	// static methods
	static bool scanInt64_8 (const char8* text, int64& value, bool scanToEnd = true);	///< Converts string of type char8 to int64 value
	static bool scanInt64_16 (const char16* text, int64& value, bool scanToEnd = true);	///< Converts string of type char16 to int64 value
	static bool scanInt64 (const tchar* text, int64& value, bool scanToEnd = true);		///< Converts string of type tchar to int64 value

	static bool scanUInt64_8 (const char8* text, uint64& value, bool scanToEnd = true);		///< Converts string of type char8 to uint64 value
	static bool scanUInt64_16 (const char16* text, uint64& value, bool scanToEnd = true);	///< Converts string of type char16 to uint64 value
	static bool scanUInt64 (const tchar* text, uint64& value, bool scanToEnd = true);		///< Converts string of type tchar to uint64 value

	static bool scanInt32_8 (const char8* text, int32& value, bool scanToEnd = true);		///< Converts string of type char8 to int32 value
	static bool scanInt32_16 (const char16* text, int32& value, bool scanToEnd = true);		///< Converts string of type char16 to int32 value
	static bool scanInt32 (const tchar* text, int32& value, bool scanToEnd = true);			///< Converts string of type tchar to int32 value

	static bool scanUInt32_8 (const char8* text, uint32& value, bool scanToEnd = true);		///< Converts string of type char8 to int32 value
	static bool scanUInt32_16 (const char16* text, uint32& value, bool scanToEnd = true);		///< Converts string of type char16 to int32 value
	static bool scanUInt32 (const tchar* text, uint32& value, bool scanToEnd = true);			///< Converts string of type tchar to int32 value

	static bool scanHex_8 (const char8* text, uint8& value, bool scanToEnd = true);		///< Converts string of type char8 to hex/unit8 value
	static bool scanHex_16 (const char16* text, uint8& value, bool scanToEnd = true);	///< Converts string of type char16 to hex/unit8 value
	static bool scanHex (const tchar* text, uint8& value, bool scanToEnd = true);		///< Converts string of type tchar to hex/unit8 value
	//-------------------------------------------------------------------------

	// conversion -------------------------------------------------------------
	void toVariant (FVariant& var) const;

	static char8 toLower (char8 c);		///< Converts to lower case
	static char8 toUpper (char8 c);		///< Converts to upper case
	static char16 toLower (char16 c);
	static char16 toUpper (char16 c);

	static int32 multiByteToWideString (char16* dest, const char8* source, int32 wcharCount, uint32 sourceCodePage = kCP_Default);	///< If dest is zero, this returns the maximum number of bytes needed to convert source
	static int32 wideStringToMultiByte (char8* dest, const char16* source, int32 char8Count, uint32 destCodePage = kCP_Default);	///< If dest is zero, this returns the maximum number of bytes needed to convert source

	bool isWideString () const {return isWide != 0;}	///< Returns true if string is wide
	bool isAsciiString () const; 						///< Checks if all characters in string are in ascii range

	bool isNormalized (UnicodeNormalization = kUnicodeNormC); ///< On PC only kUnicodeNormC is working

#if SMTG_OS_WINDOWS
	ConstString (const wchar_t* str, int32 length = -1) : ConstString (wscast (str), length) {}
	operator const wchar_t* () const { return wscast (text16 ());}
#endif

#if SMTG_OS_MACOS
	virtual void* toCFStringRef (uint32 encoding = 0xFFFF, bool mutableCFString = false) const;	///< CFString conversion
#endif
//-------------------------------------------------------------------------

//-----------------------------------------------------------------------------
protected:

	union 
	{
		void* buffer;
		char8* buffer8;
		char16* buffer16;
	};
	uint32 len : 30;
	uint32 isWide : 1;
};

//-----------------------------------------------------------------------------
/** String.
@ingroup adt

Extends class ConstString by operations which allow modifications. 

\see ConstString */
//-----------------------------------------------------------------------------
class String : public ConstString
{
public:
	
//-----------------------------------------------------------------------------
	String ();
	String (const char8* str, MBCodePage codepage, int32 n = -1, bool isTerminated = true);							///< assign n characters of str and convert to wide string by using the specified codepage
	String (const char8* str, int32 n = -1, bool isTerminated = true);	///< assign n characters of str (-1: all)
	String (const char16* str, int32 n = -1, bool isTerminated = true);	///< assign n characters of str (-1: all)
	String (const String& str, int32 n = -1);							///< assign n characters of str (-1: all)
	String (const ConstString& str, int32 n = -1);		///< assign n characters of str (-1: all)
	String (const FVariant& var);						///< assign from FVariant
	String (IString* str);						///< assign from IString
	~String () SMTG_OVERRIDE;

#if SMTG_CPP11_STDLIBSUPPORT
	String (String&& str);
	String& operator= (String&& str);
#endif

	// access------------------------------------------------------------------
	void updateLength (); ///< Call this when the string is truncated outside (not recommended though)
	const char8* text8 () const SMTG_OVERRIDE;
	const char16* text16 () const SMTG_OVERRIDE;
	char8 getChar8 (uint32 index) const SMTG_OVERRIDE;
	char16 getChar16 (uint32 index) const SMTG_OVERRIDE;

	bool setChar8 (uint32 index, char8 c);
	bool setChar16 (uint32 index, char16 c);
	inline bool setChar (uint32 index, char8 c) {return setChar8 (index, c);}
	inline bool setChar (uint32 index, char16 c) {return setChar16 (index, c);}
	//-------------------------------------------------------------------------

	// assignment--------------------------------------------------------------
	String& operator= (const char8* str) {return assign (str);}	///< Assign from a string of type char8
	String& operator= (const char16* str) {return assign (str);}
	String& operator= (const ConstString& str) {return assign (str);}
	String& operator= (const String& str) {return assign (str);}
	String& operator= (char8 c) {return assign (c);}
	String& operator= (char16 c) {return assign (c);}

	String& assign (const ConstString& str, int32 n = -1);			///< Assign n characters of str (-1: all)
	String& assign (const char8* str, int32 n = -1, bool isTerminated = true);			///< Assign n characters of str (-1: all)
	String& assign (const char16* str, int32 n = -1, bool isTerminated = true);			///< Assign n characters of str (-1: all)
	String& assign (char8 c, int32 n = 1);
	String& assign (char16 c, int32 n = 1);
	//-------------------------------------------------------------------------

	// concat------------------------------------------------------------------
	String& append (const ConstString& str, int32 n = -1);		///< Append n characters of str to this (n=-1: all)
	String& append (const char8* str, int32 n = -1);			///< Append n characters of str to this (n=-1: all)
	String& append (const char16* str, int32 n = -1);			///< Append n characters of str to this (n=-1: all)
	String& append (const char8 c, int32 n = 1);                ///< Append char c n times
	String& append (const char16 c, int32 n = 1);               ///< Append char c n times

	String& insertAt (uint32 idx, const ConstString& str, int32 n = -1);	///< Insert n characters of str at position idx (n=-1: all)
	String& insertAt (uint32 idx, const char8* str, int32 n = -1);	///< Insert n characters of str at position idx (n=-1: all)
	String& insertAt (uint32 idx, const char16* str, int32 n = -1);	///< Insert n characters of str at position idx (n=-1: all)
	String& insertAt (uint32 idx, char8 c) {char8 str[] = {c, 0}; return insertAt (idx, str, 1);} 
	String& insertAt (uint32 idx, char16 c) {char16 str[] = {c, 0}; return insertAt (idx, str, 1);}

	String& operator+= (const String& str) {return append (str);}
	String& operator+= (const ConstString& str) {return append (str);}
	String& operator+= (const char8* str) {return append (str);}
	String& operator+= (const char16* str) {return append (str);}
	String& operator+= (const char8 c) {return append (c);}
	String& operator+= (const char16 c) {return append (c);}
	//-------------------------------------------------------------------------

	// replace-----------------------------------------------------------------
	String& replace (uint32 idx, int32 n1, const ConstString& str, int32 n2 = -1);		///< Replace n1 characters of this (starting at idx) with n2 characters of str (n1,n2=-1: until end)
	String& replace (uint32 idx, int32 n1, const char8* str, int32 n2 = -1);			///< Replace n1 characters of this (starting at idx) with n2 characters of str (n1,n2=-1: until end)
	String& replace (uint32 idx, int32 n1, const char16* str, int32 n2 = -1);			///< Replace n1 characters of this (starting at idx) with n2 characters of str (n1,n2=-1: until end)

	int32 replace (const char8* toReplace, const char8* toReplaceWith, bool all = false, CompareMode m = kCaseSensitive);			///< Replace find string with replace string - returns number of replacements
	int32 replace (const char16* toReplace, const char16* toReplaceWith, bool all = false, CompareMode m = kCaseSensitive);		///< Replace find string with replace string - returns number of replacements

	bool replaceChars8 (const char8* toReplace, char8 toReplaceBy); ///< Returns true when any replacement was done
	bool replaceChars16 (const char16* toReplace, char16 toReplaceBy);
	inline bool replaceChars8 (char8 toReplace, char8 toReplaceBy)  {char8 str[] = {toReplace, 0}; return replaceChars8 (str, toReplaceBy);}
	inline bool replaceChars16 (char16 toReplace, char16 toReplaceBy)  {char16 str[] = {toReplace, 0}; return replaceChars16 (str, toReplaceBy);}
	inline bool replaceChars (char8 toReplace, char8 toReplaceBy) {return replaceChars8 (toReplace, toReplaceBy);}
	inline bool replaceChars (char16 toReplace, char16 toReplaceBy) {return replaceChars16 (toReplace, toReplaceBy);}
	inline bool replaceChars (const char8* toReplace, char8 toReplaceBy) {return replaceChars8 (toReplace, toReplaceBy);}
	inline bool replaceChars (const char16* toReplace, char16 toReplaceBy) {return replaceChars16 (toReplace, toReplaceBy);}
	//-------------------------------------------------------------------------

	// remove------------------------------------------------------------------
	String& remove (uint32 index = 0, int32 n = -1);		///< Remove n characters from string starting at index (n=-1: until end)
	enum CharGroup {kSpace, kNotAlphaNum, kNotAlpha};
	bool trim (CharGroup mode = kSpace);					///< Trim lead/trail.
	void removeChars (CharGroup mode = kSpace);				///< Removes all of group.
	bool removeChars8 (const char8* which);					///< Remove all occurrences of each char in 'which'
	bool removeChars16 (const char16* which);				///< Remove all occurrences of each char in 'which'
	inline bool removeChars8 (const char8 which) {char8 str[] = {which, 0}; return removeChars8 (str); }     
	inline bool removeChars16 (const char16 which) {char16 str[] = {which, 0}; return removeChars16 (str); }                
	inline bool removeChars (const char8* which) {return removeChars8 (which);}
	inline bool removeChars (const char16* which) {return removeChars16 (which);}
	inline bool removeChars (const char8 which) {return removeChars8 (which);}
	inline bool removeChars (const char16 which) {return removeChars16 (which);}
	bool removeSubString (const ConstString& subString, bool allOccurences = true);
	//-------------------------------------------------------------------------

	// print-------------------------------------------------------------------
	String& printf (const char8* format, ...);					///< Print formatted data into string
	String& printf (const char16* format, ...);					///< Print formatted data into string
	String& vprintf (const char8* format, va_list args);
	String& vprintf (const char16* format, va_list args);
	//-------------------------------------------------------------------------

	// numbers-----------------------------------------------------------------
	String& printInt64 (int64 value);

	/**
	* @brief				print a float into a string, trailing zeros will be trimmed
	* @param value			the floating value to be printed
	* @param maxPrecision	(optional) the max precision allowed for this, num of significant digits after the comma
	*						For instance printFloat (1.234, 2) => 1.23
	*  @return				the resulting string.
	*/
	String& printFloat (double value, uint32 maxPrecision = 6);
	/** Increment the trailing number if present else start with minNumber, width specifies the string width format (width 2 for number 3 is 03),
		applyOnlyFormat set to true will only format the string to the given width without incrementing the founded trailing number */
	bool incrementTrailingNumber (uint32 width = 2, tchar separator = STR (' '), uint32 minNumber = 1, bool applyOnlyFormat = false);
	//-------------------------------------------------------------------------

	// conversion--------------------------------------------------------------
	bool fromVariant (const FVariant& var);		///< Assigns string from FVariant
	void toVariant (FVariant& var) const;
	bool fromAttributes (IAttributes* a, IAttrID attrID);	///< Assigns string from FAttributes
	bool toAttributes (IAttributes* a, IAttrID attrID);

	void swapContent (String& s); 								///< Swaps ownership of the strings pointed to
	void take (String& str);      								///< Take ownership of the string of 'str'
	void take (void* _buffer, bool wide);      					///< Take ownership of buffer
	void* pass ();
	void passToVariant (FVariant& var);							///< Pass ownership of buffer to Variant - sets Variant ownership

	void toLower (uint32 index);								///< Lower case the character.
	void toLower ();											///< Lower case the string.
	void toUpper (uint32 index);								///< Upper case the character.
	void toUpper ();											///< Upper case the string.

	unsigned char* toPascalString (unsigned char* buf);			///< Pascal string conversion
	const String& fromPascalString (const unsigned char* buf);	///< Pascal string conversion

	bool toWideString (uint32 sourceCodePage = kCP_Default);	///< Converts to wide string according to sourceCodePage
	bool toMultiByte (uint32 destCodePage = kCP_Default);

	void fromUTF8 (const char8* utf8String);				///< Assigns from UTF8 string
	bool normalize (UnicodeNormalization = kUnicodeNormC);	///< On PC only kUnicodeNormC is working

#if SMTG_OS_WINDOWS
	String (const wchar_t* str, int32 length = -1, bool isTerminated = true) : String (wscast (str), length, isTerminated) {}
	String& operator= (const wchar_t* str) {return String::operator= (wscast (str)); }
#endif

#if SMTG_OS_MACOS
	virtual bool fromCFStringRef (const void*, uint32 encoding = 0xFFFF);	///< CFString conversion
#endif
	//-------------------------------------------------------------------------

	//-----------------------------------------------------------------------------
protected:
	bool resize (uint32 newSize, bool wide, bool fill = false);

private:
	bool _toWideString (const char8* src, int32 length, uint32 sourceCodePage = kCP_Default);
	void tryFreeBuffer ();
	bool checkToMultiByte (uint32 destCodePage = kCP_Default) const; // to remove debug code from inline - const_cast inside!!!
};

// String concatenation functions.
inline String operator+ (const ConstString& s1, const ConstString& s2) {return String (s1).append (s2);}
inline String operator+ (const ConstString& s1, const char8* s2) {return String (s1).append (s2);}
inline String operator+ (const ConstString& s1, const char16* s2) {return String (s1).append (s2);}
inline String operator+ (const char8* s1, const ConstString& s2) {return String (s1).append (s2);}
inline String operator+ (const char16* s1, const ConstString& s2) {return String (s1).append (s2);}
inline String operator+ (const ConstString& s1, const String& s2) {return String (s1).append (s2);}
inline String operator+ (const String& s1, const ConstString& s2) {return String (s1).append (s2);}
inline String operator+ (const String& s1, const String& s2) {return String (s1).append (s2);}
inline String operator+ (const String& s1, const char8* s2) {return String (s1).append (s2);}
inline String operator+ (const String& s1, const char16* s2) {return String (s1).append (s2);}
inline String operator+ (const char8* s1, const String& s2) {return String (s1).append (s2);}
inline String operator+ (const char16* s1, const String& s2) {return String (s1).append (s2);}

//-----------------------------------------------------------------------------
// ConstString
//-----------------------------------------------------------------------------
inline const tchar* ConstString::text () const
{
#ifdef UNICODE
	return text16 ();
#else
	return text8 ();
#endif
}

//-----------------------------------------------------------------------------
inline const char8* ConstString::text8 () const
{
	return (!isWide && buffer8) ? buffer8: kEmptyString8;
}

//-----------------------------------------------------------------------------
inline const char16* ConstString::text16 () const
{
	return (isWide && buffer16) ? buffer16 : kEmptyString16;
}

//-----------------------------------------------------------------------------
inline char8 ConstString::getChar8 (uint32 index) const
{
	if (index < len && buffer8 && !isWide)
		return buffer8[index];
	return 0;
}

//-----------------------------------------------------------------------------
inline char16 ConstString::getChar16 (uint32 index) const
{
	if (index < len && buffer16 && isWide)
		return buffer16[index];
	return 0;
}

//-----------------------------------------------------------------------------
inline tchar ConstString::getChar (uint32 index) const
{
#ifdef UNICODE
	return getChar16 (index);
#else
	return getChar8 (index);
#endif
}

//-----------------------------------------------------------------------------
inline tchar ConstString::getCharAt (uint32 index) const
{
#ifdef UNICODE
	if (isWide)
		return getChar16 (index);
#endif

	return static_cast<tchar> (getChar8 (index));
}

//-----------------------------------------------------------------------------
inline int64 ConstString::getNumber () const
{
	int64 tmp = 0;
	scanInt64 (tmp);
	return tmp;
}

//-----------------------------------------------------------------------------
inline bool ConstString::scanInt32_8 (const char8* text, int32& value, bool scanToEnd)
{
	int64 tmp;
	if (scanInt64_8 (text, tmp, scanToEnd))
	{
		value = (int32)tmp;
		return true;
	}
	return false;
}

//-----------------------------------------------------------------------------
inline bool ConstString::scanInt32_16 (const char16* text, int32& value, bool scanToEnd)
{
	int64 tmp;
	if (scanInt64_16 (text, tmp, scanToEnd))
	{
		value = (int32)tmp;
		return true;
	}
	return false;
}

//-----------------------------------------------------------------------------
inline bool ConstString::scanInt32 (const tchar* text, int32& value, bool scanToEnd)
{
	int64 tmp;
	if (scanInt64 (text, tmp, scanToEnd))
	{
		value = (int32)tmp;
		return true;
	}
	return false;
}

//-----------------------------------------------------------------------------
inline bool ConstString::scanUInt32_8 (const char8* text, uint32& value, bool scanToEnd)
{
	uint64 tmp;
	if (scanUInt64_8 (text, tmp, scanToEnd))
	{
		value = (uint32)tmp;
		return true;
	}
	return false;
}

//-----------------------------------------------------------------------------
inline bool ConstString::scanUInt32_16 (const char16* text, uint32& value, bool scanToEnd)
{
	uint64 tmp;
	if (scanUInt64_16 (text, tmp, scanToEnd))
	{
		value = (uint32)tmp;
		return true;
	}
	return false;
}

//-----------------------------------------------------------------------------
inline bool ConstString::scanUInt32 (const tchar* text, uint32& value, bool scanToEnd)
{
	uint64 tmp;
	if (scanUInt64 (text, tmp, scanToEnd))
	{
		value = (uint32)tmp;
		return true;
	}
	return false;
}

//-----------------------------------------------------------------------------
inline const char8* String::text8 () const
{
	if (isWide && !isEmpty ())
		checkToMultiByte (); // this should be avoided, since it can lead to information loss

	return ConstString::text8 ();
}

//-----------------------------------------------------------------------------
inline const char16* String::text16 () const
{
	if (!isWide && !isEmpty ())
	{
		const_cast<String&> (*this).toWideString ();
	}
	return ConstString::text16 ();
}

//-----------------------------------------------------------------------------
inline char8 String::getChar8 (uint32 index) const
{
	if (isWide && !isEmpty ())
		checkToMultiByte (); // this should be avoided, since it can lead to information loss

	return ConstString::getChar8 (index);
}

//-----------------------------------------------------------------------------
inline char16 String::getChar16 (uint32 index) const
{
	if (!isWide && !isEmpty ())
	{
		const_cast<String&> (*this).toWideString ();
	}
	return ConstString::getChar16 (index);
}

//-----------------------------------------------------------------------------


inline bool operator<  (const ConstString& s1, const ConstString& s2) {return (s1.compare (s2) < 0) ? true : false;}
inline bool operator<= (const ConstString& s1, const ConstString& s2) {return (s1.compare (s2) <= 0) ? true : false;}
inline bool operator>  (const ConstString& s1, const ConstString& s2) {return (s1.compare (s2) > 0) ? true : false;}
inline bool operator>= (const ConstString& s1, const ConstString& s2) {return (s1.compare (s2) >= 0) ? true : false;}
inline bool operator== (const ConstString& s1, const ConstString& s2) {return (s1.compare (s2) == 0) ? true : false;}
inline bool operator!= (const ConstString& s1, const ConstString& s2) {return (s1.compare (s2) != 0) ? true : false;}

inline bool operator<  (const ConstString& s1, const char8* s2) {return (s1.compare (s2) < 0) ? true : false;}
inline bool operator<= (const ConstString& s1, const char8* s2) {return (s1.compare (s2) <= 0) ? true : false;}
inline bool operator>  (const ConstString& s1, const char8* s2) {return (s1.compare (s2) > 0) ? true : false;}
inline bool operator>= (const ConstString& s1, const char8* s2) {return (s1.compare (s2) >= 0) ? true : false;}
inline bool operator== (const ConstString& s1, const char8* s2) {return (s1.compare (s2) == 0) ? true : false;}
inline bool operator!= (const ConstString& s1, const char8* s2) {return (s1.compare (s2) != 0) ? true : false;}
inline bool operator<  (const char8* s1, const ConstString& s2) {return (s2.compare (s1) > 0) ? true : false;}
inline bool operator<= (const char8* s1, const ConstString& s2) {return (s2.compare (s1) >= 0) ? true : false;}
inline bool operator>  (const char8* s1, const ConstString& s2) {return (s2.compare (s1) < 0) ? true : false;}
inline bool operator>= (const char8* s1, const ConstString& s2) {return (s2.compare (s1) <= 0) ? true : false;}
inline bool operator== (const char8* s1, const ConstString& s2) {return (s2.compare (s1) == 0) ? true : false;}
inline bool operator!= (const char8* s1, const ConstString& s2) {return (s2.compare (s1) != 0) ? true : false;}

inline bool operator<  (const ConstString& s1, const char16* s2) {return (s1.compare (s2) < 0) ? true : false;}
inline bool operator<= (const ConstString& s1, const char16* s2) {return (s1.compare (s2) <= 0) ? true : false;}
inline bool operator>  (const ConstString& s1, const char16* s2) {return (s1.compare (s2) > 0) ? true : false;}
inline bool operator>= (const ConstString& s1, const char16* s2) {return (s1.compare (s2) >= 0) ? true : false;}
inline bool operator== (const ConstString& s1, const char16* s2) {return (s1.compare (s2) == 0) ? true : false;}
inline bool operator!= (const ConstString& s1, const char16* s2) {return (s1.compare (s2) != 0) ? true : false;}
inline bool operator<  (const char16* s1, const ConstString& s2) {return (s2.compare (s1) > 0) ? true : false;}
inline bool operator<= (const char16* s1, const ConstString& s2) {return (s2.compare (s1) >= 0) ? true : false;}
inline bool operator>  (const char16* s1, const ConstString& s2) {return (s2.compare (s1) < 0) ? true : false;}
inline bool operator>= (const char16* s1, const ConstString& s2) {return (s2.compare (s1) <= 0) ? true : false;}
inline bool operator== (const char16* s1, const ConstString& s2) {return (s2.compare (s1) == 0) ? true : false;}
inline bool operator!= (const char16* s1, const ConstString& s2) {return (s2.compare (s1) != 0) ? true : false;}

// The following functions will only work with European Numbers!
// (e.g. Arabic, Tibetan, and Khmer digits are not supported)
extern int32 strnatcmp8 (const char8* s1, const char8* s2, bool caseSensitive = true);
extern int32 strnatcmp16 (const char16* s1, const char16* s2, bool caseSensitive = true);
inline int32 strnatcmp (const tchar* s1, const tchar* s2, bool caseSensitive = true)
{
#ifdef UNICODE
	return strnatcmp16 (s1, s2, caseSensitive);
#else
	return strnatcmp8 (s1, s2, caseSensitive);
#endif
}

//-----------------------------------------------------------------------------
/** StringObject implements IStringResult and IString methods.
    It can therefore be exchanged with other Steinberg objects using one or both of these
interfaces.

\see String, ConstString
*/
//-----------------------------------------------------------------------------
class StringObject : public FObject, public String, public IStringResult, public IString
{
public:
//-----------------------------------------------------------------------------
	StringObject () {}										
	StringObject (const char16* str, int32 n = -1, bool isTerminated = true) : String (str, n, isTerminated) {}
	StringObject (const char8* str, int32 n = -1, bool isTerminated = true) : String (str, n, isTerminated) {}
	StringObject (const StringObject& str, int32 n = -1) : String (str, n) {}		
	StringObject (const String& str, int32 n = -1) : String (str, n) {}		
	StringObject (const FVariant& var) : String (var) {}		

	using String::operator=;

	// IStringResult ----------------------------------------------------------
	void PLUGIN_API setText (const char8* text) SMTG_OVERRIDE;
	//-------------------------------------------------------------------------

	// IString-----------------------------------------------------------------
	void PLUGIN_API setText8 (const char8* text) SMTG_OVERRIDE;
	void PLUGIN_API setText16 (const char16* text) SMTG_OVERRIDE;

	const char8* PLUGIN_API getText8 () SMTG_OVERRIDE;
	const char16* PLUGIN_API getText16 () SMTG_OVERRIDE;

	void PLUGIN_API take (void* s, bool _isWide) SMTG_OVERRIDE;
	bool PLUGIN_API isWideString () const SMTG_OVERRIDE;
	//-------------------------------------------------------------------------

	OBJ_METHODS (StringObject, FObject)
	FUNKNOWN_METHODS2 (IStringResult, IString, FObject)
};

//------------------------------------------------------------------------
} // namespace Steinberg
//-------------------------------------------------------------------------------------
// Project     : SDK Base
// Version     : 1.0
//
// Category	   : Helpers
// Filename	   : base/source/hexbinary.h
// Created by  : Steinberg, 1/2012
// Description : HexBinary encoding and decoding
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// This Software Development Kit may not be distributed in parts or its entirety
// without prior written agreement by Steinberg Media Technologies GmbH.
// This SDK must not be used to re-engineer or manipulate any technology used
// in any Steinberg or Third-party application or software module,
// unless permitted by law.
// Neither the name of the Steinberg Media Technologies nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SDK IS PROVIDED BY STEINBERG MEDIA TECHNOLOGIES GMBH "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL STEINBERG MEDIA TECHNOLOGIES GMBH BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//----------------------------------------------------------------------------------

//----------------------------------------------------------------------------------
/** @file base/source/hexbinary.h
 HexBinary encoding and decoding. */
//----------------------------------------------------------------------------------
#pragma once

#include "base/source/fbuffer.h"

namespace Steinberg {

//------------------------------------------------------------------------------
namespace HexBinary
{
	//------------------------------------------------------------------------------
	/** convert the HexBinary input buffer to binary. Note that it is appended to the result buffer. */
	//------------------------------------------------------------------------------
	inline bool decode (const void* input, int32 inputSize, Buffer& result)
	{
		if ((inputSize & 1) == 1)
			return false;

		result.grow (result.getSize () + inputSize / 2);

		const char8* ptr = (const char8*)input;
		uint8 current = 0;
		for (int32 i = 0; i < inputSize; i++, ptr++)
		{
			current *= 16;
			if (*ptr >= 48 && *ptr <= 57) // 0, 1, 2, .., 9
			{
				current += *ptr - 48;
			}
			else if (*ptr >= 65 && *ptr <= 70) // A, B, .., F
			{
				current += *ptr - 55;
			}
			else if (*ptr >= 97 && *ptr <= 102) // a, b, .., f
			{
				current += *ptr - 87;
			}
			else
			{
				// malformed
				return false;
			}
			if (i % 2)
			{
				if (result.put (current) == false)
					return false;
				current = 0;
			}
		}
		return true;
	}

	//------------------------------------------------------------------------------
	/** convert the binary input buffer to HexBinary. Note that it is appended to the result buffer. */
	//------------------------------------------------------------------------------
	inline bool encode (const void* input, int32 inputSize, Buffer& result)
	{
		result.grow (result.getSize () + inputSize * 2);

		const char8* ptr = (const char8*)input;
		for (int32 i = 0; i < inputSize; i++, ptr++)
		{
			char8 high = (*ptr & 0xF0) >> 4;
			char8 low  = (*ptr & 0x0F);
			if (high > 9)
			{
				if (result.put ((char8)(high + 55)) == false)
					return false;
			}
			else
			{
				if (result.put ((char8)(high + 48)) == false)
					return false;
			}
			if (low > 9)
			{
				if (result.put ((char8)(low + 55)) == false)
					return false;
			}
			else
			{
				if (result.put ((char8)(low + 48)) == false)
					return false;
			}
		}
		return true;
	}
//------------------------------------------------------------------------
} // namespace HexBinary
} // namespace Steinberg
//------------------------------------------------------------------------
// Project     : SDK Base
// Version     : 1.0
// 
// Category    : Helpers
// Filename    : base/source/timer.h
// Created by  : Steinberg, 05/2006
// Description : Timer class for receiving tiggers at regular intervals
// 
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
// 
//   * Redistributions of source code must retain the above copyright notice, 
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation 
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this 
//     software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#pragma once

#include "fobject.h"
#include <limits>

#if SMTG_CPP17
#include <functional>
#endif

namespace Steinberg {
class Timer;

//------------------------------------------------------------------------
namespace SystemTime {

uint64 getTicks64 ();

inline uint64 getTicksDuration (uint64 old, uint64 now)
{
	if (old > now)
		return (std::numeric_limits<uint64>::max) () - old + now;
	return now - old;
}

int32 getTicks (); ///< deprecated, use getTicks64 ()

//------------------------------------------------------------------------
} // namespace SystemTime

//------------------------------------------------------------------------
/** @class ITimerCallback

    Implement this callback interface to receive triggers from a timer.
    Note: This interface is intended as a mix-in class and therefore does not provide ref-counting.

    @see Timer */
class ITimerCallback
{
public:
	virtual ~ITimerCallback () {}
	/** This method is called at the end of each interval.
	    \param timer The timer which calls. */
	virtual void onTimer (Timer* timer) = 0;
};

template <typename Call>
ITimerCallback* newTimerCallback (const Call& call)
{
	struct Callback : public ITimerCallback
	{
		Callback (const Call& call) : call (call) {}
		void onTimer (Timer* timer) SMTG_OVERRIDE { call (timer); }
		Call call;
	};
	return NEW Callback (call);
}

#if SMTG_CPP17
//------------------------------------------------------------------------
/** @class TimerCallback
 *
 *  a timer callback object using a funtion for the timer call
 */
struct TimerCallback final : ITimerCallback
{
    using CallbackFunc = std::function<void (Timer*)>;

    TimerCallback (CallbackFunc&& f) : f (std::move (f)) {}
    TimerCallback (const CallbackFunc& f) : f (f) {}

	void onTimer (Timer* timer) override { f (timer); }

private:
	CallbackFunc f;
};

#endif // SMTG_CPP17

// -----------------------------------------------------------------
/** @class Timer

    Timer is a class that allows you to receive triggers at regular intervals.
    Note: The timer class is an abstract base class with (hidden) platform specific subclasses.

    Usage:
    @code
    class TimerReceiver : public FObject, public ITimerCallback
    {
        ...
        virtual void onTimer (Timer* timer)
        {
            // do stuff
        }
        ...
    };

    TimerReceiver* receiver =  new TimerReceiver ();
    Timer* myTimer = Timer::create (receiver, 100); // interval: every 100ms
  
    ...
    ...

    if (myTimer)
        myTimer->release ();
    if (receiver)
        receiver->release ();
    @endcode

    @see ITimerCallback
 */
class Timer : public FObject
{
public:
	/** Create a timer with a given interval
	    \param callback The receiver of the timer calls.
	    \param intervalMilliseconds The timer interval in milliseconds.
	    \return The created timer if any, callers owns the timer. The timer starts immediately.
	*/
	static Timer* create (ITimerCallback* callback, uint32 intervalMilliseconds);

	virtual void stop () = 0; ///< Stop the timer.
};

// -----------------------------------------------------------------
/** @class DisableDispatchingTimers

    Disables dispatching of timers for the live time of this object
*/
class DisableDispatchingTimers
{
public:
	DisableDispatchingTimers ();
	~DisableDispatchingTimers ();

private:
	bool oldState;
};

#if SMTG_OS_LINUX
using CreateTimerFunc = Timer* (*)(ITimerCallback* callback, uint32 intervalMilliseconds);
void InjectCreateTimerFunction (CreateTimerFunc f);
#endif

//------------------------------------------------------------------------
} // namespace Steinberg
//------------------------------------------------------------------------
// Project     : SDK Base
// Version     : 1.0
//
// Category    : Helpers
// Filename    : base/source/updatehandler.h
// Created by  : Steinberg, 2008
// Description :
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
// 
//   * Redistributions of source code must retain the above copyright notice, 
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation 
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this 
//     software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#pragma once

#include "base/source/fobject.h"
#include "base/thread/include/flock.h"
#include "pluginterfaces/base/iupdatehandler.h"

namespace Steinberg {

/// @cond ignore
namespace Update { struct Table; }
/// @endcond

//------------------------------------------------------------------------
/** Handle Send and Cancel pending message for a given object*/
//------------------------------------------------------------------------
class IUpdateManager : public FUnknown
{
public:
//------------------------------------------------------------------------
	/** cancel pending messages send by \param object or by any if object is 0 */
	virtual tresult PLUGIN_API cancelUpdates (FUnknown* object) = 0;
	/** send pending messages send by \param object or by any if object is 0 */
	virtual tresult PLUGIN_API triggerDeferedUpdates (FUnknown* object = nullptr) = 0;
	static const FUID iid;
};

DECLARE_CLASS_IID (IUpdateManager, 0x030B780C, 0xD6E6418D, 0x8CE00BC2, 0x09C834D4)

//------------------------------------------------------------------------------
/**
UpdateHandler implements IUpdateManager and IUpdateHandler to handle dependencies
between objects to store and forward messages to dependent objects.

This implementation is thread save, so objects can send message, add or remove
dependents from different threads.
Do do so it uses mutex, so be aware of locking.
*/
//------------------------------------------------------------------------------
class UpdateHandler : public FObject, public IUpdateHandler, public IUpdateManager
{
public:
//------------------------------------------------------------------------------
	UpdateHandler ();
	~UpdateHandler () SMTG_OVERRIDE;

	using FObject::addDependent;
	using FObject::removeDependent;
	using FObject::deferUpdate;

	// IUpdateHandler
//private:
	friend class FObject;
	/** register \param dependent to get messages from \param object */
	tresult PLUGIN_API addDependent (FUnknown* object, IDependent* dependent) SMTG_OVERRIDE;
	/** unregister \param dependent to get no messages from \param object */
	tresult PLUGIN_API removeDependent (FUnknown* object, IDependent* dependent, size_t& earseCount);
	tresult PLUGIN_API removeDependent (FUnknown* object,
	                                            IDependent* dependent) SMTG_OVERRIDE;
public:
	/** send \param message to all dependents of \param object immediately */
	tresult PLUGIN_API triggerUpdates (FUnknown* object, int32 message) SMTG_OVERRIDE;
	/** send \param message to all dependents of \param object when idle */
	tresult PLUGIN_API deferUpdates (FUnknown* object, int32 message) SMTG_OVERRIDE;

	// IUpdateManager
	/** cancel pending messages send by \param object or by any if object is 0 */
	tresult PLUGIN_API cancelUpdates (FUnknown* object) SMTG_OVERRIDE;
	/** send pending messages send by \param object or by any if object is 0 */
	tresult PLUGIN_API triggerDeferedUpdates (FUnknown* object = nullptr) SMTG_OVERRIDE;

	/// @cond ignore
	// obsolete functions kept for compatibility
	void checkUpdates (FObject* object = nullptr)
	{
		triggerDeferedUpdates (object ? object->unknownCast () : nullptr);
	}
	void flushUpdates (FObject* object)
	{
		if (object)
			cancelUpdates (object->unknownCast ());
	}
	void deferUpdate (FObject* object, int32 message)
	{
		if (object)
			deferUpdates (object->unknownCast (), message);
	}
	void signalChange (FObject* object, int32 message, bool suppressUpdateDone = false)
	{
		if (object)
			doTriggerUpdates (object->unknownCast (), message, suppressUpdateDone);
	}
#if DEVELOPMENT
	bool checkDeferred (FUnknown* object);
	bool hasDependencies (FUnknown* object);
	void printForObject (FObject* object) const;
#endif
	/// @endcond
	size_t countDependencies (FUnknown* object = nullptr);
	
	OBJ_METHODS (UpdateHandler, FObject)
	FUNKNOWN_METHODS2 (IUpdateHandler, IUpdateManager, FObject)
	SINGLETON (UpdateHandler)
//------------------------------------------------------------------------------
private:
	tresult doTriggerUpdates (FUnknown* object, int32 message, bool suppressUpdateDone);

	Steinberg::Base::Thread::FLock lock;
	Update::Table* table = nullptr;
};


//------------------------------------------------------------------------
} // namespace Steinberg

//------------------------------------------------------------------------
// Project     : SDK Base
// Version     : 1.0
//
// Category    : Helpers
// Filename    : base/thread/include/fcondition.h
// Created by  : Steinberg, 1995
// Description : the threads and locks and signals...
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
// 
//   * Redistributions of source code must retain the above copyright notice, 
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation 
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this 
//     software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

//----------------------------------------------------------------------------------
/** @file base/thread/include/fcondition.h
	signal. */
/** @defgroup baseThread Thread Handling */
//----------------------------------------------------------------------------------
#pragma once

#include "pluginterfaces/base/ftypes.h"

#if SMTG_PTHREADS 
#include <pthread.h>
#endif

//------------------------------------------------------------------------
namespace Steinberg {
namespace Base {
namespace Thread {

//------------------------------------------------------------------------
/**	FCondition - wraps the signal and wait calls in win32
@ingroup baseThread	*/
//------------------------------------------------------------------------
class FCondition
{
public:
//------------------------------------------------------------------------

	/** Condition constructor.
	 *  @param name name of condition
	 */
	FCondition (const char8* name = nullptr /* "FCondition" */ );

	/** Condition destructor.
	 */
	~FCondition ();
	
	/** Signals one thread.
	 */
	void signal ();

	/** Signals all threads.
	 */
	void signalAll ();

	/** Waits for condition.
	 */
	void wait ();

	/** Waits for condition with timeout
	 *  @param timeout time out in milliseconds
	 *  @return false if timed out
	 */
	bool waitTimeout (int32 timeout = -1);

	/** Resets condition.
	 */
	void reset ();

#if SMTG_OS_WINDOWS
	/** Gets condition handle.
	 *  @return handle
	 */
	void* getHandle () { return event; }
#endif

//------------------------------------------------------------------------
private:
#if SMTG_PTHREADS
	pthread_mutex_t mutex;		///< Mutex object
	pthread_cond_t cond;		///< Condition object
	int32 state;				///< Use to hold the state of the signal
	int32 waiters;				///< Number of waiters

	#if DEVELOPMENT
	int32     waits;			///< Waits count
	int32     signalCount;		///< Signals count
	#endif
#elif SMTG_OS_WINDOWS
	void* event;				///< Event handle
#endif
};

} // Thread
} // Base
} // Steinberg

//------------------------------------------------------------------------
// Project     : SDK Base
// Version     : 1.0
//
// Category    : Helpers
// Filename    : base/thread/include/flock.h
// Created by  : Steinberg, 1995
// Description : locks
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
// 
//   * Redistributions of source code must retain the above copyright notice, 
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation 
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this 
//     software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

//----------------------------------------------------------------------------------
/** @file base/thread/include/flock.h
    Locks. */
/** @defgroup baseLocks Locks */
//----------------------------------------------------------------------------------
#pragma once

#include "base/source/fobject.h"
#include "pluginterfaces/base/ftypes.h"

#if SMTG_PTHREADS
#include <pthread.h>

#elif SMTG_OS_WINDOWS
struct CRITSECT							// CRITICAL_SECTION
{
	void* DebugInfo;					// PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
	Steinberg::int32 LockCount;			// LONG LockCount;
	Steinberg::int32 RecursionCount;	// LONG RecursionCount;
	void* OwningThread;					// HANDLE OwningThread
	void* LockSemaphore;				// HANDLE LockSemaphore
	Steinberg::int32 SpinCount;			// ULONG_PTR SpinCount
};
#endif

namespace Steinberg {
namespace Base {
namespace Thread {

//------------------------------------------------------------------------
/** Lock interface declaration.
@ingroup baseLocks	*/
//------------------------------------------------------------------------
struct ILock
{
//------------------------------------------------------------------------
	virtual ~ILock () {}

	/** Enables lock. */
	virtual void lock () = 0;

	/** Disables lock. */
	virtual void unlock () = 0;

	/** Tries to disable lock. */
	virtual bool trylock () = 0;
//------------------------------------------------------------------------
};

//------------------------------------------------------------------------
/**	FLock declaration.
@ingroup baseLocks	*/
//------------------------------------------------------------------------
class FLock : public ILock
{
public:
//------------------------------------------------------------------------

	/** Lock constructor.
	 *  @param name lock name
	 */
	FLock (const char8* name = "FLock");

	/** Lock destructor. */
	~FLock () SMTG_OVERRIDE;

	//-- ILock -----------------------------------------------------------
	void lock () SMTG_OVERRIDE;
	void unlock () SMTG_OVERRIDE;
	bool trylock () SMTG_OVERRIDE;

//------------------------------------------------------------------------
protected:
#if SMTG_PTHREADS
	pthread_mutex_t mutex; ///< Mutex object

#elif SMTG_OS_WINDOWS
	CRITSECT section; ///< Critical section object
#endif
};

//------------------------------------------------------------------------
/**	FLockObj declaration. Reference counted lock
@ingroup baseLocks	*/
//------------------------------------------------------------------------
class FLockObject : public FObject, public FLock
{
public:
	OBJ_METHODS (FLockObject, FObject)
};

//------------------------------------------------------------------------
/**	FGuard - automatic object for locks.
@ingroup baseLocks	*/
//------------------------------------------------------------------------
class FGuard
{
public:
//------------------------------------------------------------------------

	/** FGuard constructor.
	 *  @param _lock guard this lock
	 */
	FGuard (ILock& _lock) : lock (_lock) { lock.lock (); }

	/** FGuard destructor. */
	~FGuard () { lock.unlock (); }

//------------------------------------------------------------------------
private:
	ILock& lock; ///< guarded lock
};

//------------------------------------------------------------------------
/**	Conditional Guard - Locks only if valid lock is passed.
@ingroup baseLocks	*/
//------------------------------------------------------------------------
class FConditionalGuard
{
public:
//------------------------------------------------------------------------

	/** FConditionGuard constructor.
	 *  @param _lock guard this lock
	 */
	FConditionalGuard (FLock* _lock) : lock (_lock)
	{
		if (lock)
			lock->lock ();
	}

	/** FConditionGuard destructor. */
	~FConditionalGuard ()
	{
		if (lock)
			lock->unlock ();
	}

//------------------------------------------------------------------------
private:
	FLock* lock; ///< guarded lock
};

} // Thread
} // Base
} // Steinberg

//------------------------------------------------------------------------
// Project     : SDK Base
// Version     : 1.0
//
// Category    : Helpers
// Filename    : base/thread/source/fcondition.cpp
// Created by  : Steinberg, 1995
// Description : signals...
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
// 
//   * Redistributions of source code must retain the above copyright notice, 
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation 
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this 
//     software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#define DEBUG_LOCK 0

#include "base/thread/include/fcondition.h"
#include "base/source/fdebug.h"

#include <climits>
#include <cstdlib>

//------------------------------------------------------------------------
#if SMTG_PTHREADS
//------------------------------------------------------------------------
#if __MACH__
extern "C" {
#include <mach/clock.h>
#include <sched.h>
#include <mach/mach_init.h>
#include <mach/task.h>
#include <mach/task_policy.h>
#include <mach/thread_act.h>
#include <mach/semaphore.h>
#include <mach/thread_policy.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <unistd.h>
}
#include <dlfcn.h>

#else
#include <fcntl.h>
#endif

#include <errno.h>

#if SMTG_OS_MACOS
#include <TargetConditionals.h>
#if !SMTG_OS_IOS
#include <CoreServices/CoreServices.h>
#endif
#endif

#include <sys/time.h>
//------------------------------------------------------------------------
#elif SMTG_OS_WINDOWS
#include <windows.h>
#endif

namespace Steinberg {
namespace Base {
namespace Thread {

//------------------------------------------------------------------------
/**	FCondition CTOR.
 *  @param name - can be used to set the name of the event.
 */
FCondition::FCondition (const char8* name)
{
#if SMTG_PTHREADS
	(void)name; // unused
	pthread_mutex_init (&mutex, 0);
	pthread_cond_init (&cond, 0);
	state = 0;
	waiters = 0;
#if DEVELOPMENT
	waits = 0;
	signalCount = 0;
#endif

#elif SMTG_OS_WINDOWS
	// use name if existing
	event = CreateEventA (nullptr, FALSE, FALSE, name);

#endif
}

//------------------------------------------------------------------------
FCondition::~FCondition ()
{
#if SMTG_PTHREADS
	pthread_mutex_destroy (&mutex);
	pthread_cond_destroy (&cond);

#elif SMTG_OS_WINDOWS
	CloseHandle (event);

#endif
}

//------------------------------------------------------------------------
void FCondition::signal ()
{
#if SMTG_PTHREADS
	pthread_mutex_lock (&mutex);
	state = 1;
#if DEVELOPMENT
	signalCount++;
#endif
	pthread_cond_signal (&cond);
	pthread_mutex_unlock (&mutex);

#elif SMTG_OS_WINDOWS
	BOOL result = PulseEvent (event);
	if (!result)
	{
		SMTG_PRINTSYSERROR;
	}

#endif
}

//------------------------------------------------------------------------
void FCondition::signalAll ()
{
#if SMTG_PTHREADS
	pthread_mutex_lock (&mutex);
	state = waiters + 1;

#if DEVELOPMENT
	signalCount++;
#endif
	pthread_cond_broadcast (&cond);
	pthread_mutex_unlock (&mutex);

#elif SMTG_OS_WINDOWS
	BOOL result = SetEvent (event);
	if (!result)
	{
		SMTG_PRINTSYSERROR;
	}

#endif
}

//------------------------------------------------------------------------
void FCondition::wait ()
{
#if SMTG_PTHREADS
	pthread_mutex_lock (&mutex);
#if DEVELOPMENT
	waits++;
#endif
	waiters++;
	while (!state)
		pthread_cond_wait (&cond, &mutex);
	if (--waiters == 0)
		state = 0;
	else
		--state;
	pthread_mutex_unlock (&mutex);

#elif SMTG_OS_WINDOWS
	WaitForSingleObject (event, INFINITE);

#endif
}

//------------------------------------------------------------------------
bool FCondition::waitTimeout (int32 milliseconds)
{
#if SMTG_PTHREADS
    // this is the implementation running on mac (2018-07-18)
	if (milliseconds == -1)
	{ // infinite timeout
		wait ();
		return true;
	}

	struct timespec time;

#if __MACH__
    // this is the implementation running on mac (2018-07-18)
	time.tv_sec = milliseconds / 1000;
	time.tv_nsec = 1000000 * (milliseconds - (time.tv_sec * 1000));

	pthread_mutex_lock (&mutex);
#if DEVELOPMENT
	waits++;
#endif
    // this is the implementation running on mac (2018-07-18)
	waiters++;

	bool result = true;
	while (!state)
	{
		int32 err = pthread_cond_timedwait_relative_np (&cond, &mutex, &time);
		if (err == ETIMEDOUT)
		{
			result = false;
			break;
		}
		else
			result = true;
	}
	if (--waiters == 0)
		state = 0;
	else
		--state;
	pthread_mutex_unlock (&mutex);
	return result;

#else
    // dead code? not compiled in unit test and sequencer (2018-07-18)
	clock_gettime (CLOCK_REALTIME, &time);
	time.tv_nsec += milliseconds * 1000; // ?????????

	pthread_mutex_lock (&mutex);
	bool result = false;
	if (pthread_cond_timedwait (&cond, &mutex, &time) == 0)
		result = true;
	pthread_mutex_unlock (&mutex);
	return result;

#endif

#elif SMTG_OS_WINDOWS
	if (milliseconds == -1)
		milliseconds = INFINITE;

	DWORD result = WaitForSingleObject (event, milliseconds);
	return result == WAIT_TIMEOUT ? false : true;

#endif

#if !SMTG_OS_WINDOWS
	//	WARNING ("Return false on time out not implemented!")
	return true;
#endif
}

//------------------------------------------------------------------------
void FCondition::reset ()
{
#if SMTG_OS_WINDOWS
	ResetEvent (event);
#elif SMTG_PTHREADS
	state = 0;
#endif
}

} // Thread
} // Base
} // Steinberg
//------------------------------------------------------------------------
// Project     : SDK Base
// Version     : 1.0
//
// Category    : Helpers
// Filename    : base/thread/source/flock.cpp
// Created by  : Steinberg, 1995
// Description : Locks
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
// 
//   * Redistributions of source code must retain the above copyright notice, 
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation 
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this 
//     software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#define DEBUG_LOCK 0

#include "base/thread/include/flock.h"
#include "base/source/fdebug.h"

//------------------------------------------------------------------------
#if SMTG_OS_WINDOWS
//------------------------------------------------------------------------
#ifndef WINVER
#define WINVER 0x0500
#endif
#ifndef _WIN32_WINNT
#define _WIN32_WINNT WINVER
#endif

#include <windows.h>
#include <objbase.h>
#define INIT_CS(cs) \
	InitializeCriticalSection ((LPCRITICAL_SECTION)&cs);

#endif // SMTG_OS_WINDOWS

namespace Steinberg {
namespace Base {
namespace Thread {


//------------------------------------------------------------------------
//	FLock implementation
//------------------------------------------------------------------------
FLock::FLock (const char8* /*name*/)
{
#if SMTG_PTHREADS
	pthread_mutexattr_t mutexAttr;
	pthread_mutexattr_init (&mutexAttr);
	pthread_mutexattr_settype (&mutexAttr, PTHREAD_MUTEX_RECURSIVE);
	if (pthread_mutex_init (&mutex, &mutexAttr) != 0)
		{SMTG_WARNING("mutex_init failed")}
	pthread_mutexattr_destroy (&mutexAttr);

#elif SMTG_OS_WINDOWS
	INIT_CS (section)
#else
#warning implement FLock!
#endif
}

//------------------------------------------------------------------------
FLock::~FLock ()
{
#if SMTG_PTHREADS
	pthread_mutex_destroy (&mutex);

#elif SMTG_OS_WINDOWS
	DeleteCriticalSection ((LPCRITICAL_SECTION)&section);
		
#endif
}

//------------------------------------------------------------------------
void FLock::lock ()
{
#if DEBUG_LOCK
	FDebugPrint ("FLock::lock () %x\n", this);
#endif

#if SMTG_PTHREADS
	pthread_mutex_lock (&mutex);

#elif SMTG_OS_WINDOWS
	EnterCriticalSection ((LPCRITICAL_SECTION)&section);

#endif
}

//------------------------------------------------------------------------
void FLock::unlock ()
{
#if DEBUG_LOCK
	FDebugPrint ("FLock::unlock () %x\n", this);
#endif
	
#if SMTG_PTHREADS
	pthread_mutex_unlock (&mutex);

#elif SMTG_OS_WINDOWS
	LeaveCriticalSection ((LPCRITICAL_SECTION)&section);

#endif 
}

//------------------------------------------------------------------------
bool FLock::trylock ()
{
#if SMTG_PTHREADS
	return pthread_mutex_trylock (&mutex) == 0;

#elif SMTG_OS_WINDOWS
	return TryEnterCriticalSection ((LPCRITICAL_SECTION)&section) != 0 ? true : false;

#else
	return false;
#endif 
}

} // Thread
} // Base
} // Steinberg

//-----------------------------------------------------------------------------
// Project     : SDK Core
//
// Category    : SDK Core Interfaces
// Filename    : pluginterfaces/base/conststringtable.cpp
// Created by  : Steinberg, 09/2007
// Description : constant unicode string table
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses. 
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#include "conststringtable.h"
#include <cstring>
#include <map>

namespace Steinberg {

static std::map<const char8*, char16*>* stringMap;
static std::map<const char8, char16>* charMap;

static char16* generateUTF16 (const char8* str);

//----------------------------------------------------------------------------
ConstStringTable* ConstStringTable::instance ()
{
	static ConstStringTable stringTable;
	return &stringTable;
}

//----------------------------------------------------------------------------
const char16* ConstStringTable::getString (const char8* str) const
{
	std::map<const char8*, char16*>::iterator iter = stringMap->find (str);
	if (iter != stringMap->end ())
		return iter->second;
	char16* uStr = generateUTF16 (str);
	stringMap->insert (std::make_pair (str, uStr));
	return uStr;
}

//----------------------------------------------------------------------------
char16 ConstStringTable::getString (const char8 str) const
{
	std::map<const char8, char16>::iterator iter = charMap->find (str);
	if (iter != charMap->end ())
		return iter->second;
	char16 uStr = 0;
#if BYTEORDER == kBigEndian
	char8* puStr = (char8*)&uStr;
	puStr[1] = str;
#else
	uStr = str;
#endif
	charMap->insert (std::make_pair (str, uStr));
	return uStr;
}

//----------------------------------------------------------------------------
ConstStringTable::ConstStringTable ()
{
	stringMap = new std::map<const char8*, char16*>;
	charMap = new std::map<const char8, char16>;
}

//----------------------------------------------------------------------------
ConstStringTable::~ConstStringTable ()
{
	// free out allocated strings
	{
		std::map<const char8*, char16*>::iterator iter = stringMap->begin ();
		while (iter != stringMap->end ())
		{
			delete[] iter->second;
			iter++;
		}
	} // delete iterator on map before deleting the map

	delete stringMap;
	delete charMap;
}

//----------------------------------------------------------------------------
char16* generateUTF16 (const char8* str)
{
	int32 len = (int32)strlen (str);
	char16* result = new char16[len + 1];
	for (int32 i = 0; i < len; i++)
	{
#if BYTEORDER == kBigEndian
		char8* pChr = (char8*)&result[i];
		pChr[0] = 0;
		pChr[1] = str[i];
#else
		result[i] = str[i];
#endif
	}
	result[len] = 0;
	return result;
}
//------------------------------------------------------------------------
} // namespace Steinberg
//-----------------------------------------------------------------------------
// Project     : SDK Core
//
// Category    : SDK Core Interface IID definitions
// Filename    : pluginterfaces/base/coreiids.cpp
// Created by  : Steinberg, 01/2004
// Description : Basic Interface
//
//------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//------------------------------------------------------------------------

#include "pluginterfaces/base/funknown.h"
#include "pluginterfaces/base/ibstream.h"
#include "pluginterfaces/base/icloneable.h"
#include "pluginterfaces/base/ipluginbase.h"
#include "pluginterfaces/base/iupdatehandler.h"

//------------------------------------------------------------------------
namespace Steinberg {
DEF_CLASS_IID (IPluginBase)
DEF_CLASS_IID (IPluginFactory)
DEF_CLASS_IID (IPluginFactory2)
DEF_CLASS_IID (IPluginFactory3)

DEF_CLASS_IID (FUnknown)

DEF_CLASS_IID (ICloneable)

DEF_CLASS_IID (IDependent)
DEF_CLASS_IID (IUpdateHandler)

DEF_CLASS_IID (IBStream)
DEF_CLASS_IID (ISizeableStream)

//------------------------------------------------------------------------
} // Steinberg
//-----------------------------------------------------------------------------
// Project     : SDK Core
//
// Category    : SDK Core Interfaces
// Filename    : pluginterfaces/base/funknown.cpp
// Created by  : Steinberg, 01/2004
// Description : Basic Interface
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses. 
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#include "funknown.h"

#include "fstrdefs.h"

#include <cstdio>

#if SMTG_OS_WINDOWS
#include <objbase.h>
#endif

#if SMTG_OS_MACOS
#include <CoreFoundation/CoreFoundation.h>

#if !defined (SMTG_USE_STDATOMIC_H)
#if defined(MAC_OS_X_VERSION_10_11) && defined(MAC_OS_X_VERSION_MIN_REQUIRED)
#define SMTG_USE_STDATOMIC_H (MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_11)
#else
#define SMTG_USE_STDATOMIC_H 0
#endif
#endif // !defined (SMTG_USE_STDATOMIC_H)

#if !SMTG_USE_STDATOMIC_H
#include <libkern/OSAtomic.h>
#if defined(__GNUC__) && (__GNUC__ >= 4) && !__LP64__
// on 32 bit Mac OS X we can safely ignore the format warnings as sizeof(int) == sizeof(long)
#pragma GCC diagnostic ignored "-Wformat"
#endif 
#endif // !SMTG_USE_STDATOMIC_H
#endif // SMTG_OS_MACOS

#if SMTG_OS_LINUX
#if !defined (SMTG_USE_STDATOMIC_H)
#if defined (__ANDROID__) || defined(_LIBCPP_VERSION)
#define SMTG_USE_STDATOMIC_H 1
#else
#include <ext/atomicity.h>
#endif
#endif // !defined (SMTG_USE_STDATOMIC_H)
#include <stdlib.h>
#endif

#if defined (SMTG_USE_STDATOMIC_H) && SMTG_USE_STDATOMIC_H 
#include <stdatomic.h>
#endif

namespace Steinberg {

//------------------------------------------------------------------------
#if COM_COMPATIBLE
#if SMTG_OS_WINDOWS
#define GuidStruct GUID
#else
struct GuidStruct
{
	uint32 Data1;
	uint16 Data2;
	uint16 Data3;
	uint8 Data4[8];
};
#endif
#endif

static void toString8 (char8* string, const char* data, int32 i1, int32 i2);
static void fromString8 (const char8* string, char* data, int32 i1, int32 i2);
static uint32 makeLong (uint8 b1, uint8 b2, uint8 b3, uint8 b4);

//------------------------------------------------------------------------
//  FUnknownPrivate
//------------------------------------------------------------------------
namespace FUnknownPrivate {
//------------------------------------------------------------------------
int32 PLUGIN_API atomicAdd (int32& var, int32 d)
{
#if SMTG_USE_STDATOMIC_H
	return atomic_fetch_add (reinterpret_cast<atomic_int_least32_t*> (&var), d) + d;
#else
#if SMTG_OS_WINDOWS
#ifdef __MINGW32__
	return InterlockedExchangeAdd (reinterpret_cast<long volatile*>(&var), d) + d;
#else
	return InterlockedExchangeAdd ((LONG*)&var, d) + d;
#endif
#elif SMTG_OS_MACOS
	return OSAtomicAdd32Barrier (d, (int32_t*)&var);
#elif defined(__ANDROID__)
	return atomic_fetch_add ((atomic_int*)&var, d) + d;
#elif SMTG_OS_LINUX
	__gnu_cxx::__atomic_add (&var, d);
	return var;
#else
#warning implement me!
	var += d;
	return var;
#endif
#endif
}
} // FUnknownPrivate

//------------------------------------------------------------------------
//	FUID implementation
//------------------------------------------------------------------------

FUID::FUID ()
{
	memset (data, 0, sizeof (TUID));
}

//------------------------------------------------------------------------
FUID::FUID (uint32 l1, uint32 l2, uint32 l3, uint32 l4)
{
	from4Int (l1, l2, l3, l4);
}

//------------------------------------------------------------------------
FUID::FUID (const FUID& f)
{
	memcpy (data, f.data, sizeof (TUID));
}

//------------------------------------------------------------------------
#if SMTG_CPP11_STDLIBSUPPORT
FUID::FUID (FUID&& other)
{
	memcpy (data, other.data, sizeof (TUID));
}

FUID& FUID::operator= (FUID&& other)
{
	memcpy (data, other.data, sizeof (TUID));
	return *this;
}
#endif

//------------------------------------------------------------------------
bool FUID::generate ()
{
#if SMTG_OS_WINDOWS
#if defined(_M_ARM64) || defined(_M_ARM)
	//#warning implement me!
	return false;
#else
	GUID guid;
	HRESULT hr = CoCreateGuid (&guid);
	switch (hr)
	{
		case RPC_S_OK: memcpy (data, (char*)&guid, sizeof (TUID)); return true;

		case (HRESULT)RPC_S_UUID_LOCAL_ONLY:
		default: return false;
	}
#endif

#elif SMTG_OS_MACOS
	CFUUIDRef uuid = CFUUIDCreate (kCFAllocatorDefault);
	if (uuid)
	{
		CFUUIDBytes bytes = CFUUIDGetUUIDBytes (uuid);
		memcpy (data, (char*)&bytes, sizeof (TUID));
		CFRelease (uuid);
		return true;
	}
	return false;

#elif SMTG_OS_LINUX
	srand ((size_t)this);
	for (int32 i = 0; i < 16; i++)
		data[i] = static_cast<unsigned char>(rand ());
	return true;
#else
#warning implement me!
	return false;
#endif
}

//------------------------------------------------------------------------
bool FUID::isValid () const
{
	TUID nulluid = {0};

	return memcmp (data, nulluid, sizeof (TUID)) != 0;
}

//------------------------------------------------------------------------
FUID& FUID::operator= (const FUID& f)
{
	memcpy (data, f.data, sizeof (TUID));
	return *this;
}

//------------------------------------------------------------------------
void FUID::from4Int (uint32 l1, uint32 l2, uint32 l3, uint32 l4)
{
#if COM_COMPATIBLE
	data [0]  = (char)((l1 & 0x000000FF)      );
	data [1]  = (char)((l1 & 0x0000FF00) >>  8);
	data [2]  = (char)((l1 & 0x00FF0000) >> 16);
	data [3]  = (char)((l1 & 0xFF000000) >> 24);
	data [4]  = (char)((l2 & 0x00FF0000) >> 16);
	data [5]  = (char)((l2 & 0xFF000000) >> 24);
	data [6]  = (char)((l2 & 0x000000FF)      );
	data [7]  = (char)((l2 & 0x0000FF00) >>  8);
	data [8]  = (char)((l3 & 0xFF000000) >> 24);
	data [9]  = (char)((l3 & 0x00FF0000) >> 16);
	data [10] = (char)((l3 & 0x0000FF00) >>  8);
	data [11] = (char)((l3 & 0x000000FF)      );
	data [12] = (char)((l4 & 0xFF000000) >> 24);
	data [13] = (char)((l4 & 0x00FF0000) >> 16);
	data [14] = (char)((l4 & 0x0000FF00) >>  8);
	data [15] = (char)((l4 & 0x000000FF)      );
#else
	data [0]  = (char)((l1 & 0xFF000000) >> 24);
	data [1]  = (char)((l1 & 0x00FF0000) >> 16);
	data [2]  = (char)((l1 & 0x0000FF00) >>  8);
	data [3]  = (char)((l1 & 0x000000FF)      );
	data [4]  = (char)((l2 & 0xFF000000) >> 24);
	data [5]  = (char)((l2 & 0x00FF0000) >> 16);
	data [6]  = (char)((l2 & 0x0000FF00) >>  8);
	data [7]  = (char)((l2 & 0x000000FF)      );
	data [8]  = (char)((l3 & 0xFF000000) >> 24);
	data [9]  = (char)((l3 & 0x00FF0000) >> 16);
	data [10] = (char)((l3 & 0x0000FF00) >>  8);
	data [11] = (char)((l3 & 0x000000FF)      );
	data [12] = (char)((l4 & 0xFF000000) >> 24);
	data [13] = (char)((l4 & 0x00FF0000) >> 16);
	data [14] = (char)((l4 & 0x0000FF00) >>  8);
	data [15] = (char)((l4 & 0x000000FF)      );
#endif
}

//------------------------------------------------------------------------
void FUID::to4Int (uint32& d1, uint32& d2, uint32& d3, uint32& d4) const
{
	d1 = getLong1 ();
	d2 = getLong2 ();
	d3 = getLong3 ();
	d4 = getLong4 ();
}

//------------------------------------------------------------------------
uint32 FUID::getLong1 () const
{
#if COM_COMPATIBLE
	return makeLong (data[3], data[2], data[1], data[0]);
#else
	return makeLong (data[0], data[1], data[2], data[3]);
#endif
}

//------------------------------------------------------------------------
uint32 FUID::getLong2 () const
{
#if COM_COMPATIBLE
	return makeLong (data[5], data[4], data[7], data[6]);
#else
	return makeLong (data[4], data[5], data[6], data[7]);
#endif
}

//------------------------------------------------------------------------
uint32 FUID::getLong3 () const
{
#if COM_COMPATIBLE
	return makeLong (data[8], data[9], data[10], data[11]);
#else
	return makeLong (data[8], data[9], data[10], data[11]);
#endif
}

//------------------------------------------------------------------------
uint32 FUID::getLong4 () const
{
#if COM_COMPATIBLE
	return makeLong (data[12], data[13], data[14], data[15]);
#else
	return makeLong (data[12], data[13], data[14], data[15]);
#endif
}

//------------------------------------------------------------------------
void FUID::toString (char8* string) const
{
	if (!string)
		return;

#if COM_COMPATIBLE
	auto* g = (GuidStruct*)data;

	char8 s[17];
	Steinberg::toString8 (s, data, 8, 16);

	sprintf (string, "%08X%04X%04X%s", g->Data1, g->Data2, g->Data3, s);
#else
	Steinberg::toString8 (string, data, 0, 16);
#endif
}

//------------------------------------------------------------------------
bool FUID::fromString (const char8* string)
{
	if (!string || !*string)
		return false;
	if (strlen (string) != 32)
		return false;

#if COM_COMPATIBLE
	GuidStruct g;
	char s[33];

	strcpy (s, string);
	s[8] = 0;
	sscanf (s, "%x", &g.Data1);
	strcpy (s, string + 8);
	s[4] = 0;
	sscanf (s, "%hx", &g.Data2);
	strcpy (s, string + 12);
	s[4] = 0;
	sscanf (s, "%hx", &g.Data3);

	memcpy (data, &g, 8);
	Steinberg::fromString8 (string + 16, data, 8, 16);
#else
	Steinberg::fromString8 (string, data, 0, 16);
#endif

	return true;
}

//------------------------------------------------------------------------
bool FUID::fromRegistryString (const char8* string)
{
	if (!string || !*string)
		return false;
	if (strlen (string) != 38)
		return false;

// e.g. {c200e360-38c5-11ce-ae62-08002b2b79ef}

#if COM_COMPATIBLE
	GuidStruct g;
	char8 s[10];

	strncpy (s, string + 1, 8);
	s[8] = 0;
	sscanf (s, "%x", &g.Data1);
	strncpy (s, string + 10, 4);
	s[4] = 0;
	sscanf (s, "%hx", &g.Data2);
	strncpy (s, string + 15, 4);
	s[4] = 0;
	sscanf (s, "%hx", &g.Data3);
	memcpy (data, &g, 8);

	Steinberg::fromString8 (string + 20, data, 8, 10);
	Steinberg::fromString8 (string + 25, data, 10, 16);
#else
	Steinberg::fromString8 (string + 1, data, 0, 4);
	Steinberg::fromString8 (string + 10, data, 4, 6);
	Steinberg::fromString8 (string + 15, data, 6, 8);
	Steinberg::fromString8 (string + 20, data, 8, 10);
	Steinberg::fromString8 (string + 25, data, 10, 16);
#endif

	return true;
}

//------------------------------------------------------------------------
void FUID::toRegistryString (char8* string) const
{
// e.g. {c200e360-38c5-11ce-ae62-08002b2b79ef}

#if COM_COMPATIBLE
	auto* g = (GuidStruct*)data;

	char8 s1[5];
	Steinberg::toString8 (s1, data, 8, 10);

	char8 s2[13];
	Steinberg::toString8 (s2, data, 10, 16);

	sprintf (string, "{%08X-%04X-%04X-%s-%s}", g->Data1, g->Data2, g->Data3, s1, s2);
#else
	char8 s1[9];
	Steinberg::toString8 (s1, data, 0, 4);
	char8 s2[5];
	Steinberg::toString8 (s2, data, 4, 6);
	char8 s3[5];
	Steinberg::toString8 (s3, data, 6, 8);
	char8 s4[5];
	Steinberg::toString8 (s4, data, 8, 10);
	char8 s5[13];
	Steinberg::toString8 (s5, data, 10, 16);

	snprintf (string, 40, "{%s-%s-%s-%s-%s}", s1, s2, s3, s4, s5);
#endif
}

//------------------------------------------------------------------------
void FUID::print (char8* string, int32 style) const
{
	print (style, string, 62);
}

//------------------------------------------------------------------------
void FUID::print (int32 style, char8* string, size_t stringBufferSize) const
{
	if (!string || stringBufferSize == 0) // no string: debug output
	{
		char8 str[128];
		print (style, str, 128);

#if SMTG_OS_WINDOWS
		OutputDebugStringA (str);
		OutputDebugStringA ("\n");
#else
		fprintf (stdout, "%s\n", str);
#endif
		return;
	}

	uint32 l1, l2, l3, l4;
	to4Int (l1, l2, l3, l4);

	switch (style)
	{
		case kINLINE_UID:
			snprintf (string, stringBufferSize, "INLINE_UID (0x%08X, 0x%08X, 0x%08X, 0x%08X)", l1,
			          l2, l3, l4);
			break;

		case kDECLARE_UID:
			snprintf (string, stringBufferSize, "DECLARE_UID (0x%08X, 0x%08X, 0x%08X, 0x%08X)", l1,
			          l2, l3, l4);
			break;

		case kFUID:
			snprintf (string, stringBufferSize, "FUID (0x%08X, 0x%08X, 0x%08X, 0x%08X)", l1, l2, l3,
			          l4);
			break;

		case kCLASS_UID:
		default:
			snprintf (string, stringBufferSize,
			          "DECLARE_CLASS_IID (Interface, 0x%08X, 0x%08X, 0x%08X, 0x%08X)", l1, l2, l3,
			          l4);
			break;
	}
}

//------------------------------------------------------------------------
//  helpers
//------------------------------------------------------------------------
static uint32 makeLong (uint8 b1, uint8 b2, uint8 b3, uint8 b4)
{
	return (uint32 (b1) << 24) | (uint32 (b2) << 16) | (uint32 (b3) << 8) | uint32 (b4);
}

//------------------------------------------------------------------------
static void toString8 (char8* string, const char* data, int32 i1, int32 i2)
{
	*string = 0;
	for (int32 i = i1; i < i2; i++)
	{
		char8 s[3];
		snprintf (s, 3, "%02X", (uint8)data[i]);
		strcat (string, s);
	}
}

//------------------------------------------------------------------------
static void fromString8 (const char8* string, char* data, int32 i1, int32 i2)
{
	for (int32 i = i1; i < i2; i++)
	{
		char8 s[3];
		s[0] = *string++;
		s[1] = *string++;
		s[2] = 0;

		int32 d = 0;
		sscanf (s, "%2x", &d);
		data[i] = (char)d;
	}
}

//------------------------------------------------------------------------
} // namespace Steinberg
//-----------------------------------------------------------------------------
// Project     : SDK Core
//
// Category    : Helpers
// Filename    : pluginterfaces/base/ustring.cpp
// Created by  : Steinberg, 12/2005
// Description : UTF-16 String class
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses. 
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#include "ustring.h"

#if SMTG_OS_WINDOWS
#include <cstdio>

#ifdef _MSC_VER
#pragma warning (disable : 4996) // deprecated functions
#endif

#elif SMTG_OS_MACOS
#include <CoreFoundation/CoreFoundation.h>

#elif SMTG_OS_LINUX
#include <cstring>
#include <string>
#include <codecvt>
#include <sstream>
#include <locale>

#include <wctype.h>
#include <wchar.h>

#endif

//------------------------------------------------------------------------
namespace Steinberg {

//------------------------------------------------------------------------
#if SMTG_OS_LINUX

//------------------------------------------------------------------------
namespace {

using Converter = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>;

//------------------------------------------------------------------------
Converter& converter ()
{
	static Converter instance;
	return instance;
}

//------------------------------------------------------------------------
} // anonymous

//------------------------------------------------------------------------
#endif // SMTG_OS_LINUX

//------------------------------------------------------------------------
/** Copy strings of different character width. */
//------------------------------------------------------------------------
template <class TDstChar, class TSrcChar>
void StringCopy (TDstChar* dst, int32 dstSize, const TSrcChar* src, int32 srcSize = -1)
{
	int32 count = dstSize;
	if (srcSize >= 0 && srcSize < dstSize)
		count = srcSize;
	for (int32 i = 0; i < count; i++)
	{
		dst[i] = (TDstChar)src[i];
		if (src[i] == 0)
			break;
	}
	dst[dstSize - 1] = 0;
}

//------------------------------------------------------------------------
/** Find length of null-terminated string, i.e. StringLength (L"ABC\0") => 3 */
//------------------------------------------------------------------------
template <class TSrcChar>
int32 StringLength (const TSrcChar* src, int32 srcSize = -1)
{
	if (srcSize == 0)
		return 0;
	int32 length = 0;
	while (src[length])
	{
		length++;
		if (srcSize > 0 && length >= srcSize)
			break;
	}
	return length;
}

//------------------------------------------------------------------------
// UString
//------------------------------------------------------------------------
int32 UString::getLength () const
{
	return StringLength<char16> (thisBuffer, thisSize);
}

//------------------------------------------------------------------------
UString& UString::assign (const char16* src, int32 srcSize)
{
	StringCopy<char16, char16> (thisBuffer, thisSize, src, srcSize);
	return *this;
}

//------------------------------------------------------------------------
UString& UString::append (const char16* src, int32 srcSize)
{
	int32 length = getLength ();
	StringCopy<char16, char16> (thisBuffer + length, thisSize - length, src, srcSize);
	return *this;
}

//------------------------------------------------------------------------
const UString& UString::copyTo (char16* dst, int32 dstSize) const
{
	StringCopy<char16, char16> (dst, dstSize, thisBuffer, thisSize);
	return *this;
}

//------------------------------------------------------------------------
UString& UString::fromAscii (const char* src, int32 srcSize)
{
	StringCopy<char16, char> (thisBuffer, thisSize, src, srcSize);
	return *this;
}

//------------------------------------------------------------------------
const UString& UString::toAscii (char* dst, int32 dstSize) const
{
	StringCopy<char, char16> (dst, dstSize, thisBuffer, thisSize);
	return *this;
}

//------------------------------------------------------------------------
bool UString::scanFloat (double& value) const
{
#if SMTG_OS_WINDOWS
	return swscanf ((const wchar_t*)thisBuffer, L"%lf", &value) != -1;

#elif TARGET_API_MAC_CARBON
	CFStringRef cfStr = CFStringCreateWithBytes (0, (const UInt8 *)thisBuffer, getLength () * 2, kCFStringEncodingUTF16, false);
	if (cfStr)
	{
		value = CFStringGetDoubleValue (cfStr);
		CFRelease (cfStr);
		return true;
	}
	return false;

#elif SMTG_OS_LINUX
	auto str = converter ().to_bytes (thisBuffer);
	return sscanf (str.data (), "%lf", &value) == 1;

#else
#warning Implement me
	// implement me!
	return false;
#endif
}

//------------------------------------------------------------------------
bool UString::printFloat (double value, int32 precision)
{
#if SMTG_OS_WINDOWS
	return swprintf ((wchar_t*)thisBuffer, L"%.*lf", precision, value) != -1;
#elif SMTG_OS_MACOS
	bool result = false;
	CFStringRef cfStr = CFStringCreateWithFormat (0, 0, CFSTR("%.*lf"), precision, value);
	if (cfStr)
	{
		memset (thisBuffer, 0, thisSize);
		CFRange range = {0, CFStringGetLength (cfStr)};
		CFStringGetBytes (cfStr, range, kCFStringEncodingUTF16, 0, false, (UInt8*)thisBuffer, thisSize, 0);
		CFRelease (cfStr);
		return true;
	}
	return result;
#elif SMTG_OS_LINUX
	auto utf8Buffer = reinterpret_cast<char*> (thisBuffer);
	auto len = snprintf (utf8Buffer, thisSize, "%.*lf", precision, value);
	if (len > 0)
	{
		auto utf16Buffer = reinterpret_cast<char16*> (thisBuffer);
		utf16Buffer[len] = 0;
		while (--len >= 0)
		{
			utf16Buffer[len] = utf8Buffer[len];
		}
		return true;
	}
	return false;
#else
#warning Implement me
	// implement me!
	return false;
#endif
}

//------------------------------------------------------------------------
bool UString::scanInt (int64& value) const
{
#if SMTG_OS_WINDOWS
	return swscanf ((const wchar_t*)thisBuffer, L"%I64d", &value) != -1;

#elif SMTG_OS_MACOS
	CFStringRef cfStr = CFStringCreateWithBytes (0, (const UInt8 *)thisBuffer, getLength () * 2, kCFStringEncodingUTF16, false);
	if (cfStr)
	{
		value = CFStringGetIntValue (cfStr);
		CFRelease (cfStr);
		return true;
	}
	return false;

#elif SMTG_OS_LINUX
	auto str = converter ().to_bytes (thisBuffer);
	return sscanf (str.data (), "%lld", &value) == 1;

#else
#warning Implement me
	// implement me!
	return false;
#endif
}

//------------------------------------------------------------------------
bool UString::printInt (int64 value)
{
#if SMTG_OS_WINDOWS
	return swprintf ((wchar_t*)thisBuffer, L"%I64d", value) != -1;

#elif SMTG_OS_MACOS
	CFStringRef cfStr = CFStringCreateWithFormat (0, 0, CFSTR("%lld"), value);
	if (cfStr)
	{
		memset (thisBuffer, 0, thisSize);
		CFRange range = {0, CFStringGetLength (cfStr)};
		CFStringGetBytes (cfStr, range, kCFStringEncodingUTF16, 0, false, (UInt8*)thisBuffer, thisSize, 0);
		CFRelease (cfStr);
		return true;
	}
	return false;
#elif SMTG_OS_LINUX
	auto utf8Buffer = reinterpret_cast<char*> (thisBuffer);
	auto len = snprintf (utf8Buffer, thisSize, "%lld", value);
	if (len > 0)
	{
		auto utf16Buffer = reinterpret_cast<char16*> (thisBuffer);
		utf16Buffer[len] = 0;
		while (--len >= 0)
		{
			utf16Buffer[len] = utf8Buffer[len];
		}
		return true;
	}
	return false;

#else
#warning Implement me
	// implement me!
	return false;
#endif
}
} // namespace Steinberg
//-----------------------------------------------------------------------------
// Project     : SDK Core
//
// Category    : SDK Core Interfaces
// Filename    : pluginterfaces/base/conststringtable.h
// Created by  : Steinberg, 09/2007
// Description : constant unicode string table
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses. 
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#pragma once

#include "ftypes.h"

namespace Steinberg {

//------------------------------------------------------------------------
/**	Constant unicode string table.
Used for conversion from ASCII string literals to char16.
*/
class ConstStringTable
{
public:
	static ConstStringTable* instance ();

	/** Returns a char16 string of a ASCII string literal*/
	const char16* getString (const char8* str) const;
	/** Returns a char16 character of a ASCII character */
	char16 getString (const char8 str) const;

protected:
	ConstStringTable ();
	~ConstStringTable ();
};

//------------------------------------------------------------------------
} // namespace Steinberg

//------------------------------------------------------------------------
// Project     : SDK Core
//
// Category    : Basic Host Service Interfaces
// Filename    : pluginterfaces/base/doc.h
// Created by  : Steinberg, 01/2004
// Description : doc for doxygen
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses. 
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

/** 
********************************************************************
\mainpage VST Module Architecture
********************************************************************
\tableofcontents

********************************************************************
\section piVstMa Introduction
********************************************************************
\b VST-MA is a component model system which is used in all <a href="http://www.steinberg.net" target=_blank>Steinberg</a> host applications 
as the basic layer for plug-in support as well as for internal application components.\n
It is object-oriented, cross-platform and (almost) compiler-independent. \n
The basics are very much like Microsoft(R) COM, so if you are familiar with
this technology, understanding VST-MA should be quite easy. 

\b VST-MA is provided in C++ only. Interfaces in C++ are expressed as pure virtual class
(which is a class with nothing but abstract methods). Unlike COM there is no support for C or other
languages yet - simply because there has been no need for this so far. But all \b VST-MA interfaces
can be transformed into different representations in case this should be inevitable some day. \n
It is currently available for Windows and Mac OS X. 

The C++ files belonging to \b VST-MA are located in the following folders:
- <a href="../../pluginterfaces/base" target=_blank>pluginterfaces/base</a>
- <a href="../../pluginterfaces/gui" target=_blank>pluginterfaces/gui</a>

\b Note: The name 'VST Module Architecture' has only little relation to the 'Virtual Studio Technology' itself. \n
It describes the basic layer for any plug-in category supported in <a href="http://www.steinberg.net" target=_blank>Steinberg</a> hosts. \b VST-MA 
existed long before it was used as a base for VST 3 ifself.
\n
\n
********************************************************************
\section piInterfaces Interfaces
********************************************************************

**********************************
\subsection funknown FUnknown
**********************************
  Steinberg::FUnknown is the basic interface of \b VST-MA. All other interfaces are directly or 
  indirectly derived from it. 
\n
\n
**********************************
\subsection iid IID/CID
**********************************
  Each interface has a unique identifier (IID) of type Steinberg::FUID. It is used to retrieve a new 
  interface from another one (Steinberg::FUnknown::queryInterface). It is important to understand the 
  difference between interface identifier and component identifier.\n
  A component-ID or class-ID (CID) is used to identify a concrete implementation class and is usually passed to a class factory in order 
  to create the corresponding component. \n
  So a lot of different classes (with different class identifiers) can implement the same interfaces. 
\n 
\n
**********************************
\subsection direction Direction
**********************************
An interface may have a \b direction, meaning that the interface is expected to be 
  implemented either in the plug-in or in the host. The nature of an interface is documented like this:
  \n \n
  - <b>[host imp] </b>: the host implements the interface
  - <b>[plug imp] </b>: the plug-in implements the interface
  .
  \n
  When neither of these is specified, the interface can be used in both ways.
\n
\n
**********************************
\subsection version Versioning and inheritance
**********************************
  Unlike C++ classes, interfaces do not use inheritance to express specializations of objects. 
  Inheritance is used for versioning only. One of the strict rules is, that once an interface has been 
  released, it must never change again. Adding new functionality to an interface requires a new version
  (usually an ordinal number is added to its name in this case).\n
  A new version inherits the old version(s)
  of the interface, so the old and the new methods are combined in one interface. This is why specializations 
  need to be modeled as separate interfaces! If a specialized interface were to inherit from the basic interface
  as well, an implementation class that needs to implement all of these interfaces would inherit the base 
  interface twice, causing the compiler to run into ambiguities. So the specialization relation 
  to a basic interface can only be expressed in the documentation: \n \n
  - ISpecialInterface [\b extends IBaseInterface] => means IBaseInterface::queryInterface (ISpecialInterface::iid, ...) can be used to retrieve the derived 
	interface.
  . 
  \n
  You can find some example code here: \ref versionInheritance 
\n
\n
**********************************
\subsection com COM Compatibility
**********************************
  The first layer of \b VST-MA is binary-compatible to \b COM. The Vtable and interface identifier of 
  FUnknown match with the corresponding COM interface IUnknown. The main difference is the organization 
  and creation of components by a host application. \b VST-MA does not require any Microsoft(R) COM source file. 
  You can find information about \b COM on pages like: \n
	- \htmlonly <a href="http://www.microsoft.com/Com/resources/comdocs.asp" target="_blank">http://www.microsoft.com/Com/resources/comdocs.asp</a>\endhtmlonly.
\n
\n
**********************************
\subsection basic Basic Interfaces
**********************************
  - Steinberg::FUnknown
  - Steinberg::IPluginBase 
  - Steinberg::IPluginFactory
 .
\n
\n
**********************************
\subsection helper Helper Classes
**********************************
  - Steinberg::FUID
  - Steinberg::FUnknownPtr
.
\n
\see \ref howtoClass
\n
\n
********************************************************************
\section piPlugins Plug-ins
********************************************************************

**********************************
\subsection module Module Factory
**********************************
  A module (Windows: Dynamic Link Library, MAC: Mach-O Bundle) contains the implementation 
  of one or more components (e.g. VST 3 effects). A \b VST-MA module must contain a class factory
  where meta-data and create-methods for the components are registered. \n
  The host has access to this factory through the Steinberg::IPluginFactory interface. This is the anchor point for the module
  and it is realized as a C-style export function named GetPluginFactory. You can find an export 
  definition file in folder - <a href="../../public.sdk/win/stdplug.def" target=_blank>public.sdk/win/stdplug.def</a> 
  which can be used to export this function. \n
  GetPluginFactory is declared as follows:

\code{.cpp}
IPluginFactory* PLUGIN_API GetPluginFactory ()
\endcode
\n

**********************************
\subsection Locations
**********************************
  Component modules do not require registration like DirectX. The host application expects component modules to be located in predefined folders of the file system.
  These folders and their subfolders are scanned for \b VST-MA modules during application startup. Each folder serves a special purpose:
	- The application's \c Components subfolder (e.g. "C:\Program Files\Steinberg\Cubase SX\Components") is used for components tightly bound to the application. No other application should use it.
	- Components that are shared between all <a href="http://www.steinberg.net" target=_blank>Steinberg</a> hosts are located at:
		- Win: "/Program Files/Common Files/Steinberg/shared components"
		- Mac: "/Library/Application Support/Steinberg/Components/"
	- For special purpose plug-in types, additional locations can be defined. Please refer to the corresponding documentation to find out if additional folders are used and where to find them.
\n
\n
**********************************
\subsection Categories
**********************************
  Any class that the factory can create is assigned to a category. It is this category that tells 
  the host the purpose of the class (and gives a hint of which interfaces it might implement). \n
  A class is also described with a name and it has a unique id.
  - For example, the category for import/export filters is <b>"Project Filter"</b> and for VST 3 audio plug-ins <b>"Audio Module Class"</b>. 
  - "Service" is a special category. The purpose of a class of this category is completely 
    unknown to the host. It is be loaded automatically during program start 
	(provided that the user did not deactivate it).
  - Since the factory can create any number of classes, one component library can contain 
    multiple components of any type.
\n
\n
**********************************
\subsection IPluginBase
**********************************
  The entry-point interface for any component class is Steinberg::IPluginBase. The host uses this 
  interface to initialize and to terminate the plug-in component. When the host initializes the 
  plug-in, it passes a so called context. This context contains any interface to the host that 
  the plug-in will need to work.
\n
\n
**********************************
\subsection purpose Purpose-specific interfaces
**********************************
Each plug-in category (VST 3 Effects, Project import/export Filters, Audio Codecs, etc...) defines its own set of purpose-specific interfaces. These are not part of the basic VST-MA layer.
\n
\n
\see \ref loadPlugin
\n
\n
********************************************************************
\section Unicode Unicode
********************************************************************

Beginning with version 5 of Cubase and Nuendo, the internal structure of the host was modified to
better support internationalization. Therefore, string handling was changed to utilize
<A HREF="http://en.wikipedia.org/wiki/Unicode">Unicode</A> strings whenever strings are passed
around. As a consequence, all the interfaces to plug-ins have changed from using ASCI to Unicode strings
for call and return parameters. So in turn, all plug-in must be adapted to support
Unicode. This has major consequences in that:
- Unicode hosts (Cubase 5 or later) will only work with Unicode plug-ins. When loading a plug-in,
a Unicode host checks the plug-in's type and will not load any non-Unicode plug-ins.
\n
- Unicode plug-ins will <b>not</b> load in non-Unicode hosts. When loading a Unicode plug-in
requests information from the host and will not load, if no Unicode host is detected.
Therefore, if a plug-in is supposed to work with both older and newer hosts, it is best to provide
two versions of the plug-in. 
\n
\n
**********************************
\subsection plugunicode Plug-ins for Unicode hosts
**********************************
Writing plug-ins that are supposed to work only with Unicode hosts is easy. Use a current version
of this SDK and develop a plug-in as usual. Make sure that you only ever pass Unicode 
<a href="http://en.wikipedia.org/wiki/UTF-16">UTF-16</a> strings to interfaces that
have strings as call parameters and also be prepared that strings returned by these interfaces are
always <a href="http://en.wikipedia.org/wiki/UTF-16">UTF-16</a>. Therefore, to make things easier, it is recommended that Unicode strings are used throughout the
plug-in's implementation, in order to avoid back and forth conversions.
Also, use the Steinberg::String and Steinberg::ConstString classes from
the Base module, they have been designed to work universally on both Mac and Win. 
\n
\n
**********************************
\subsection migrating Migrating from non-Unicode to Unicode
**********************************
In <a href="http://www.steinberg.net" target="_blank">Steinberg</a> SDKs released before Cubase 5 the interface functions were using pointers of type <c> char </c>
for passing strings to and from the host. These have been changed now to using Steinberg's defined
type <c> tchar </c> which is equivalent to <c> char16 </c>, i.e. 16 bit character. In theory, there are 
many ways for representing 16 bits characters, but we chose
to use the industry standard <a href="http://en.wikipedia.org/wiki/Unicode">Unicode</a>, so strings
are expected to be encoded in <a href="http://en.wikipedia.org/wiki/UTF-16">UTF-16</a>. \n
Accordingly, also the implementation of a plug-in needs to be adapted to deal correctly with Unicode
encoded strings, as well as only ever passing Unicode strings to the host. 
\n
\n
<b>Technical note</b>: Changing a function from using 8 bit to 16 bit character pointers may seem as only a
minor modification, but in interface design this is a major intrusion, because an interface is
a contract to the outside world that is never to be changed. Therefore, classes that are changed
to use Unicode strings are distinguished and also receive a new unique class ID.
\n
\n
**********************************
\subsection backward SDK backward compatibility
**********************************
Even with the current SDK it is still possible to develop non-Unicode plug-ins. In the
file <a href="../../pluginterfaces/base/ftypes.h">pluginterfaces/base/ftypes.h</a>, the line "#define UNICODE_OFF"
is commented out, by uncommenting it you can revert all interfaces to using single byte ASCII strings.
Alternatively you can also specify UNICODE_OFF as a preprocessor definition in your project file.\n
Also, the plug-in's factory info now does not define the Unicode flag anymore, so a Unicode host sees the compiled
plug-in as non-Unicode. Also, when reverting to single byte strings the plug-in's implementation also
has to be changed to behave correctly. 
\n
\n
<b>Technical note</b>: When undefining Unicode, the class IDs also revert to the old ones.
\n
\n
*/

//******************************************************
/**
\page howtoClass How to derive a class from an interface
*********************************************************
In the first example we derive a class directly from FUnknown, using some of the helper macros provided by the SDK. 

\code{.cpp}
class CMyClass: public FUnknown
{
public:
	CMyClass ();
	virtual ~CMyClass ();

	DECLARE_FUNKNOWN_METHODS  // declares queryInterface, addRef and release
};


CMyClass::CMyClass ()
{
	FUNKNOWN_CTOR // init reference counter, increment global object counter
}

CMyClass::~CMyClass ()
{
	FUNKNOWN_DTOR // decrement global object counter
}

IMPLEMENT_REFCOUNT (CMyClass) // implements reference counting

tresult CMyClass::queryInterface (const char* iid, void** obj)
{
	QUERY_INTERFACE (iid, obj, ::FUnknown::iid, CMyClass)
	return kNoInterface;
}
\endcode

Developing a class with more than one interface is done by multiple inheritance. Additionally you have to provide an appropriate cast for each interface in the queryInterface method. 

\code{.cpp}
class CMyMultiClass : public Steinberg::IPluginBase, 
					  public Steinberg::IPlugController,
					  public Steinberg::IEditorFactory
{
public:
	DECLARE_FUNKNOWN_METHODS

	// declare the methods of all inherited interfaces here...
};

IMPLEMENT_REFCOUNT (CMyMultiClass) // implements reference counting

tresult CMyMultiClass::queryInterface (const char* iid, void** obj)
{
	QUERY_INTERFACE (iid, obj, Steinberg::FUnknown::iid, IPluginBase)
	QUERY_INTERFACE (iid, obj, Steinberg::IPluginBase::iid, IPluginBase)
	QUERY_INTERFACE (iid, obj, Steinberg::IPlugController::iid, IPlugController)
	QUERY_INTERFACE (iid, obj, Steinberg::IEditorFactory::iid, IEditorFactory)
	*obj = 0;
	return kNoInterface;
}
\endcode
*/

//********************************************************************
/** \page versionInheritance Interface Versions and Inheritance
********************************************************************
\par

Unlike C++ classes, \b VST-MA interfaces do not use inheritance to express
specializations of objects. Usually all interfaces are derived from
FUnknown. This is because interfaces must \b never change after they
have been released. The VST Module Architecture Interfaces use inheritance
only for versioning! All specializations will be modeled as
separate interfaces!

For example the C++ classes
\code{.cpp}
class Shape
{
public:
	void setPosition (long x, long y);
protected:
	long x;
	long y;
};
class Rect : public Shape
{
public:
	void setDimension (long width, long height);
protected:
	long width;
	long height;
};
\endcode

expressed in \b VST-MA, define an interface for each inheritance level:

\code{.cpp}
class IShape : public FUnknown
{
public:
	virtual void setPosition (long x, long y) = 0;
};
class IRect : public FUnknown
{
public:
	virtual void setDimension (long width, long height) = 0;
};
\endcode

In the next program version there need to be changes to 
the \c Shape class that look like this:
\code{.cpp}
class Shape
{
public:
	void setPosition (long x, long y);
	void setColor (Color color);
protected:
	long x;
	long y;
	Color color;
};
\endcode

The \b VST-MA representation now reflect the changes to Shape by adding a
new interface that inherits from IShape and looks like the following
code, while the former interface definitions remain the same:
\code{.cpp}
class IShape2 : public IShape
{
public:
	virtual void setColor (Color color) = 0;
};
\endcode
*/

//********************************************************************
/** \page loadPlugin How the host will load a plug-in
********************************************************************
\par

The host application will handle a plug-in in the following manner 
(some code is Windows-specific!): 

\code{.cpp}
HMODULE hModule = LoadLibrary ("SomePlugin.dll");
if (hModule)
{
	InitModuleProc initProc = (InitModuleProc)GetProcAddress (hModule, "InitDll");
	if (initProc)
	{
		if (initProc () == false)
		{
			FreeLibrary (module);
			return false;
		}
	}

	GetFactoryProc proc = (GetFactoryProc)GetProcAddress (hModule, "GetPluginFactory");

	IPluginFactory* factory = proc ? proc () : 0;
	if (factory)
	{
		for (int32 i = 0; i < factory->countClasses (); i++)
		{
			PClassInfo ci;
			factory->getClassInfo (i, &ci);

			FUnknown* obj;
			factory->createInstance (ci.cid, FUnknown::iid, (void**)&obj);
			...
			obj->release ();
		}

		factory->release ();
	}

	ExitModuleProc exitProc = (ExitModuleProc)GetProcAddress (hModule, "ExitDll");
	if (exitProc)
		exitProc ();

	FreeLibrary (hModule);
}
\endcode
*/
//-----------------------------------------------------------------------------
// Project     : SDK Core
//
// Category    : SDK Core Interfaces
// Filename    : pluginterfaces/base/falignpop.h
// Created by  : Steinberg, 01/2004
// Description : Restore alignment settings
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses. 
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

//---------------------------------------------------------------------------------------------------
#if SMTG_OS_MACOS
	#pragma pack(pop)
#elif defined __BORLANDC__
	#pragma -a-
#elif SMTG_OS_WINDOWS
	#pragma pack(pop)
#endif
//---------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Project     : SDK Core
//
// Category    : SDK Core Interfaces
// Filename    : pluginterfaces/base/falignpush.h
// Created by  : Steinberg, 01/2004
// Description : Set alignment settings
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses. 
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

//----------------------------------------------------------------------------------------------
#if SMTG_OS_MACOS
	#pragma GCC diagnostic ignored "-Wunknown-warning-option"
	#pragma GCC diagnostic ignored "-Wpragma-pack"
	#if SMTG_PLATFORM_64
		#pragma pack(push, 16)
	#else
		#pragma pack(push, 1)
	#endif
#elif defined __BORLANDC__
	#pragma -a8
#elif SMTG_OS_WINDOWS
	//! @brief warning C4103: alignment changed after including header, may be due to missing #pragma pack(pop)
	#ifdef _MSC_VER
		#pragma warning(disable : 4103)
	#endif
	#pragma pack(push)
	#if SMTG_PLATFORM_64
		#pragma pack(16)
	#else
		#pragma pack(8)
	#endif
#endif
//----------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Project     : SDK Core
//
// Category    : SDK Core Interfaces
// Filename    : pluginterfaces/base/fplatform.h
// Created by  : Steinberg, 01/2004
// Description : Detect platform and set define
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses. 
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#pragma once

// values for BYTEORDER according to the used platform
#define kLittleEndian	0
#define kBigEndian		1

#undef PLUGIN_API

#if !defined (__INTEL_CXX11_MODE__)
#define SMTG_INTEL_CXX11_MODE 0
#else
#define SMTG_INTEL_CXX11_MODE __INTEL_CXX11_MODE__
#endif

#if !defined (__INTEL_COMPILER)
#define SMTG_INTEL_COMPILER 0
#else
#define SMTG_INTEL_COMPILER __INTEL_COMPILER
#endif

//-----------------------------------------------------------------------------
// WIN32 AND WIN64 (WINDOWS)
//-----------------------------------------------------------------------------
#if defined (_WIN32)
	//-----------------------------------------------------------------------------
	#define SMTG_OS_LINUX	0
	#define SMTG_OS_MACOS	0
	#define SMTG_OS_WINDOWS	1
	#define SMTG_OS_IOS		0
	#define SMTG_OS_OSX		0

	#if defined(_M_IX86)
		#define SMTG_CPU_X86	1
	#else
		#define SMTG_CPU_X86	0
	#endif
	#if defined(_M_AMD64)
		#define SMTG_CPU_X86_64	1
	#else
		#define SMTG_CPU_X86_64	0
	#endif
	#if defined(_M_ARM)
		#define SMTG_CPU_ARM	1
	#else
		#define SMTG_CPU_ARM	0
	#endif
	#if defined(_M_ARM64)
		#define SMTG_CPU_ARM_64	1
	#else
		#define SMTG_CPU_ARM_64	0
	#endif
	#if defined(_M_ARM64EC)
		#define SMTG_CPU_ARM_64EC	1
	#else
		#define SMTG_CPU_ARM_64EC	0
	#endif

	#define SMTG_OS_WINDOWS_ARM	(SMTG_CPU_ARM_64EC || SMTG_CPU_ARM_64 || SMTG_CPU_ARM)

	#define BYTEORDER kLittleEndian
	
	#define COM_COMPATIBLE	1
	#define PLUGIN_API __stdcall
	#define SMTG_PTHREADS	0

	#define SMTG_EXPORT_SYMBOL __declspec (dllexport)
	#define SMTG_HIDDEN_SYMBOL

	#ifndef _CRT_SECURE_NO_WARNINGS
		#define _CRT_SECURE_NO_WARNINGS
	#endif

	#ifdef _MSC_VER
		#pragma warning (disable : 4244) //! @brief warning C4244: Conversion from 'type1' to 'type2', possible loss of data.
		#pragma warning (disable : 4250) //! @brief warning C4250: Inheritance via dominance is allowed
		#pragma warning (disable : 4996) //! @brief warning C4996: deprecated functions

		#pragma warning (3 : 4189) //! @brief warning C4189: local variable is initialized but not referenced
		#pragma warning (3 : 4238) //! @brief warning C4238: nonstandard extension used : class rvalue used as lvalue
	#endif

	#if defined (_WIN64) || defined (_M_ARM64)
		#define SMTG_PLATFORM_64 1
	#else
		#define SMTG_PLATFORM_64 0
	#endif

	#ifndef WIN32
		#define WIN32	1
	#endif

	#ifdef __cplusplus
		#define SMTG_CPP11 (__cplusplus >= 201103L || _MSC_VER > 1600 || SMTG_INTEL_CXX11_MODE)
		#define SMTG_CPP11_STDLIBSUPPORT SMTG_CPP11
		#define SMTG_CPP14 (__cplusplus >= 201402L || ((_MSC_FULL_VER >= 190024210L) && (_MSVC_LANG >= 201402L)))
		#define SMTG_CPP17 (__cplusplus >= 201703L || ((_MSC_FULL_VER >= 190024210L) && (_MSVC_LANG >= 201703L)))
		#define SMTG_CPP20 (__cplusplus >= 202002L)
		#define SMTG_HAS_NOEXCEPT ((_MSC_FULL_VER >= 190023026L) || (SMTG_INTEL_CXX11_MODE && SMTG_INTEL_COMPILER >= 1300))
		#if ((_MSC_FULL_VER >= 190024210L) || (SMTG_INTEL_CXX11_MODE && SMTG_INTEL_COMPILER >= 1500) || (defined(__MINGW32__) && SMTG_CPP11))
			#define SMTG_HAS_CPP11_CONSTEXPR 1
		#else
			#define SMTG_HAS_CPP11_CONSTEXPR 0
		#endif
		#if (((_MSC_VER >= 1915L) && (_MSVC_LANG >= 201402L)) || (SMTG_INTEL_CXX11_MODE && SMTG_INTEL_COMPILER > 1700) || (defined(__MINGW32__) && SMTG_CPP14))
			#define SMTG_HAS_CPP14_CONSTEXPR 1
		#else
			#define SMTG_HAS_CPP14_CONSTEXPR 0
		#endif
	#endif //__cplusplus

	#define SMTG_DEPRECATED_ATTRIBUTE(message) __declspec (deprecated ("Is Deprecated: " message))
//-----------------------------------------------------------------------------
// LINUX
//-----------------------------------------------------------------------------
#elif __gnu_linux__ || __linux__
	#define SMTG_OS_LINUX		1
	#define SMTG_OS_MACOS		0
	#define SMTG_OS_WINDOWS		0
	#define SMTG_OS_WINDOWS_ARM	0
	#define SMTG_OS_IOS			0
	#define SMTG_OS_OSX			0

	#define SMTG_CPU_X86	__i386__
	#define SMTG_CPU_X86_64	__x86_64__
	#if defined(__arm__)
		#define SMTG_CPU_ARM	__arm__
	#else
		#define SMTG_CPU_ARM	0
	#endif
	#if defined(__aarch64__)
		#define SMTG_CPU_ARM_64	__aarch64__
	#else
		#define SMTG_CPU_ARM_64	0
	#endif
	#define SMTG_CPU_ARM_64EC 0

	#include <endian.h>
	#if __BYTE_ORDER == __LITTLE_ENDIAN
		#define BYTEORDER kLittleEndian
	#else
		#define BYTEORDER kBigEndian
	#endif

	#define COM_COMPATIBLE	0
	#define PLUGIN_API
	#define SMTG_PTHREADS	1

	#define SMTG_EXPORT_SYMBOL __attribute__ ((visibility ("default")))
	#define SMTG_HIDDEN_SYMBOL __attribute__ ((visibility ("hidden")))

	#if __LP64__
		#define SMTG_PLATFORM_64 1
	#else
		#define SMTG_PLATFORM_64 0
	#endif
	#ifdef __cplusplus
		#include <cstddef>
		#define SMTG_CPP11 (__cplusplus >= 201103L)
		#ifndef SMTG_CPP11
			#error unsupported compiler
		#endif
		#define SMTG_CPP14 (__cplusplus >= 201402L)
		#define SMTG_CPP17 (__cplusplus >= 201703L)
		#define SMTG_CPP20 (__cplusplus >= 202002L)
		#if defined(__GNUG__) && __GNUG__ < 8
			#define SMTG_CPP11_STDLIBSUPPORT 0
		#else
			#define SMTG_CPP11_STDLIBSUPPORT 1
		#endif
		#define SMTG_HAS_NOEXCEPT 1
		#define SMTG_HAS_CPP11_CONSTEXPR SMTG_CPP11
		#define SMTG_HAS_CPP14_CONSTEXPR SMTG_CPP14
	#endif // __cplusplus
//-----------------------------------------------------------------------------
// Mac and iOS
//-----------------------------------------------------------------------------
#elif __APPLE__
	#include <TargetConditionals.h>
	#define SMTG_OS_LINUX		0
	#define SMTG_OS_MACOS		1
	#define SMTG_OS_WINDOWS		0
	#define SMTG_OS_WINDOWS_ARM	0
	#define SMTG_OS_IOS			TARGET_OS_IPHONE
	#define SMTG_OS_OSX			TARGET_OS_MAC && !TARGET_OS_IPHONE

	#define SMTG_CPU_X86		TARGET_CPU_X86
	#define SMTG_CPU_X86_64		TARGET_CPU_X86_64
	#define SMTG_CPU_ARM		TARGET_CPU_ARM
	#define SMTG_CPU_ARM_64		TARGET_CPU_ARM64
	#define SMTG_CPU_ARM_64EC 	0

	#if !SMTG_OS_IOS
		#ifndef __CF_USE_FRAMEWORK_INCLUDES__
			#define __CF_USE_FRAMEWORK_INCLUDES__
		#endif
		#ifndef TARGET_API_MAC_CARBON
			#define TARGET_API_MAC_CARBON 1
		#endif
	#endif
	#if __LP64__
		#define SMTG_PLATFORM_64 1
	#else
		#define SMTG_PLATFORM_64 0
	#endif
	#if defined (__BIG_ENDIAN__)
		#define BYTEORDER kBigEndian
	#else
		#define BYTEORDER kLittleEndian
	#endif

	#define COM_COMPATIBLE	0
	#define PLUGIN_API
	#define SMTG_PTHREADS	1

	#define SMTG_EXPORT_SYMBOL __attribute__ ((visibility ("default")))
	#define SMTG_HIDDEN_SYMBOL __attribute__ ((visibility ("hidden")))

	#if !defined(__PLIST__) && !defined(SMTG_DISABLE_DEFAULT_DIAGNOSTICS)
		#ifdef __clang__
			#pragma GCC diagnostic ignored "-Wswitch-enum"
			#pragma GCC diagnostic ignored "-Wparentheses"
			#pragma GCC diagnostic ignored "-Wuninitialized"
			#if __clang_major__ >= 3
				#pragma GCC diagnostic ignored "-Wtautological-compare"
				#pragma GCC diagnostic ignored "-Wunused-value"
				#if __clang_major__ >= 4 || __clang_minor__ >= 1
					#pragma GCC diagnostic ignored "-Wswitch"
					#pragma GCC diagnostic ignored "-Wcomment"
				#endif
				#if __clang_major__ >= 5
					#pragma GCC diagnostic ignored "-Wunsequenced"
					#if __clang_minor__ >= 1
						#pragma GCC diagnostic ignored "-Wunused-const-variable"
					#endif
				#endif
			#endif
		#endif
	#endif
	#ifdef __cplusplus
		#include <cstddef>
		#define SMTG_CPP11 (__cplusplus >= 201103L || SMTG_INTEL_CXX11_MODE)
		#define SMTG_CPP14 (__cplusplus >= 201402L)
		#define SMTG_CPP17 (__cplusplus >= 201703L)
		#define SMTG_CPP20 (__cplusplus >= 202002L)
		#if defined (_LIBCPP_VERSION) && SMTG_CPP11
			#define SMTG_CPP11_STDLIBSUPPORT 1
			#define SMTG_HAS_NOEXCEPT 1
		#else
			#define SMTG_CPP11_STDLIBSUPPORT 0
			#define SMTG_HAS_NOEXCEPT 0
		#endif
		#define SMTG_HAS_CPP11_CONSTEXPR SMTG_CPP11
		#define SMTG_HAS_CPP14_CONSTEXPR SMTG_CPP14
	#endif // __cplusplus
//-----------------------------------------------------------------------------
// Unknown Platform
//-----------------------------------------------------------------------------
#else
	#pragma error unknown platform
#endif

//-----------------------------------------------------------------------------
#if !SMTG_RENAME_ASSERT
#undef WINDOWS
#undef MAC
#undef PTHREADS
#undef PLATFORM_64

#if SMTG_OS_WINDOWS
#define WINDOWS			SMTG_OS_WINDOWS
#endif
#if SMTG_OS_MACOS
#define MAC				SMTG_OS_MACOS
#endif
#define PLATFORM_64		SMTG_PLATFORM_64
#define PTHREADS		SMTG_PTHREADS
#endif
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
#if SMTG_CPP11
#define SMTG_OVERRIDE override
#else
#define SMTG_OVERRIDE
#endif

#if SMTG_HAS_CPP11_CONSTEXPR
#define SMTG_CONSTEXPR constexpr
#else
#define SMTG_CONSTEXPR
#endif

#if SMTG_HAS_CPP14_CONSTEXPR
#define SMTG_CONSTEXPR14 constexpr
#else
#define SMTG_CONSTEXPR14
#endif

#if SMTG_HAS_NOEXCEPT
#define SMTG_NOEXCEPT noexcept
#else
#define SMTG_NOEXCEPT
#endif

//-----------------------------------------------------------------------------
// Deprecation setting
//-----------------------------------------------------------------------------
#ifndef SMTG_DEPRECATED_ATTRIBUTE
#define SMTG_DEPRECATED_ATTRIBUTE(msg)
#endif

#define SMTG_DEPRECATED_MSG(msg) SMTG_DEPRECATED_ATTRIBUTE(msg)
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Project     : SDK Core
//
// Category    : SDK Core Interfaces
// Filename    : pluginterfaces/base/fstrdefs.h
// Created by  : Steinberg, 01/2004
// Description : Definitions for handling strings (Unicode / ASCII / Platforms)
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses. 
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#pragma once

#include "ftypes.h"

//----------------------------------------------------------------------------
/** string methods defines unicode / ASCII */
//----------------------------------------------------------------------------

// 16 bit string operations
#if SMTG_CPP11	// if c++11 unicode string literals
	#define SMTG_CPP11_CAT_PRIVATE_DONT_USE(a,b)			a ## b
	#define STR16(x) SMTG_CPP11_CAT_PRIVATE_DONT_USE(u,x)
#else
	#include "conststringtable.h"
	#define STR16(x) Steinberg::ConstStringTable::instance ()->getString (x)
#endif

#ifdef UNICODE
	#define STR(x) STR16(x)
	#define tStrBufferSize(buffer) (sizeof(buffer)/sizeof(Steinberg::tchar))

#else
	#define STR(x) x
	#define tStrBufferSize(buffer) (sizeof(buffer))
#endif

#define str8BufferSize(buffer) (sizeof(buffer)/sizeof(Steinberg::char8))
#define str16BufferSize(buffer) (sizeof(buffer)/sizeof(Steinberg::char16))

#if SMTG_OS_WINDOWS
#define FORMAT_INT64A "I64d"
#define FORMAT_UINT64A "I64u"

#elif SMTG_OS_MACOS || SMTG_OS_LINUX
#define FORMAT_INT64A  "lld"
#define FORMAT_UINT64A "llu"
#define stricmp		strcasecmp
#define strnicmp	strncasecmp
#endif

#ifdef UNICODE
#define FORMAT_INT64W STR(FORMAT_INT64A)
#define FORMAT_UINT64W STR(FORMAT_UINT64A)

#define FORMAT_INT64 FORMAT_INT64W
#define FORMAT_UINT64 FORMAT_UINT64W
#else
#define FORMAT_INT64 FORMAT_INT64A
#define FORMAT_UINT64 FORMAT_UINT64A
#endif


//----------------------------------------------------------------------------
// newline
//----------------------------------------------------------------------------
#if SMTG_OS_WINDOWS
#define ENDLINE_A   "\r\n"
#define ENDLINE_W   STR ("\r\n")
#elif SMTG_OS_MACOS
#define ENDLINE_A   "\r"
#define ENDLINE_W   STR ("\r")
#elif SMTG_OS_LINUX
#define ENDLINE_A   "\n"
#define ENDLINE_W   STR ("\n")
#endif

#ifdef UNICODE
#define ENDLINE ENDLINE_W
#else
#define ENDLINE ENDLINE_A
#endif

#if SMTG_OS_WINDOWS && !defined(__GNUC__) && defined(_MSC_VER) && (_MSC_VER < 1900)
#define stricmp _stricmp
#define strnicmp _strnicmp
#define snprintf _snprintf
#endif

namespace Steinberg {

//----------------------------------------------------------------------------
static SMTG_CONSTEXPR const tchar kEmptyString[] = { 0 };
static SMTG_CONSTEXPR const char8 kEmptyString8[] = { 0 };
static SMTG_CONSTEXPR const char16 kEmptyString16[] = { 0 };

#ifdef UNICODE
static SMTG_CONSTEXPR const tchar kInfiniteSymbol[] = { 0x221E, 0 };
#else
static SMTG_CONSTEXPR const tchar* const kInfiniteSymbol = STR ("oo");
#endif

//----------------------------------------------------------------------------
template <class T>
inline SMTG_CONSTEXPR14 int32 _tstrlen (const T* wcs)
{
	const T* eos = wcs;

	while (*eos++)
		;

	return (int32) (eos - wcs - 1);
}

inline SMTG_CONSTEXPR14 int32 tstrlen (const tchar* str) {return _tstrlen (str);}
inline SMTG_CONSTEXPR14 int32 strlen8 (const char8* str) {return _tstrlen (str);}
inline SMTG_CONSTEXPR14 int32 strlen16 (const char16* str) {return _tstrlen (str);}

//----------------------------------------------------------------------------
template <class T>
inline SMTG_CONSTEXPR14 int32 _tstrcmp (const T* src, const T* dst)
{
	while (*src == *dst && *dst)
	{
		src++;
		dst++;
	}

	if (*src == 0 && *dst == 0)
		return 0;
	if (*src == 0)
		return -1;
	if (*dst == 0)
		return 1;
	return (int32) (*src - *dst);
}

inline SMTG_CONSTEXPR14 int32 tstrcmp (const tchar* src, const tchar* dst) {return _tstrcmp (src, dst);}
inline SMTG_CONSTEXPR14 int32 strcmp8 (const char8* src, const char8* dst) {return _tstrcmp (src, dst);}
inline SMTG_CONSTEXPR14 int32 strcmp16 (const char16* src, const char16* dst) {return _tstrcmp (src, dst);}

template <typename T>
inline SMTG_CONSTEXPR14 int32 strcmpT (const T* first, const T* last);

template <>
inline SMTG_CONSTEXPR14 int32 strcmpT<char8> (const char8* first, const char8* last) { return _tstrcmp (first, last); }

template <>
inline SMTG_CONSTEXPR14 int32 strcmpT<char16> (const char16* first, const char16* last) { return _tstrcmp (first, last); }

//----------------------------------------------------------------------------
template <class T>
inline SMTG_CONSTEXPR14 int32 _tstrncmp (const T* first, const T* last, uint32 count)
{
	if (count == 0)
		return 0;

	while (--count && *first && *first == *last)
	{
		first++;
		last++;
	}

	if (*first == 0 && *last == 0)
		return 0;
	if (*first == 0)
		return -1;
	if (*last == 0)
		return 1;
	return (int32) (*first - *last);
}

inline SMTG_CONSTEXPR14 int32 tstrncmp (const tchar* first, const tchar* last, uint32 count) {return _tstrncmp (first, last, count);}
inline SMTG_CONSTEXPR14 int32 strncmp8 (const char8* first, const char8* last, uint32 count) {return _tstrncmp (first, last, count);}
inline SMTG_CONSTEXPR14 int32 strncmp16 (const char16* first, const char16* last, uint32 count) {return _tstrncmp (first, last, count);}

template <typename T>
inline SMTG_CONSTEXPR14 int32 strncmpT (const T* first, const T* last, uint32 count);

template <>
inline SMTG_CONSTEXPR14 int32 strncmpT<char8> (const char8* first, const char8* last, uint32 count) { return _tstrncmp (first, last, count); }

template <>
inline SMTG_CONSTEXPR14 int32 strncmpT<char16> (const char16* first, const char16* last, uint32 count) {return _tstrncmp (first, last, count); }

//----------------------------------------------------------------------------
template <class T>
inline SMTG_CONSTEXPR14 T* _tstrcpy (T* dst, const T* src)
{
	T* cp = dst;
	while ((*cp++ = *src++) != 0) // copy string
		;
	return dst;
}
inline SMTG_CONSTEXPR14 tchar* tstrcpy (tchar* dst, const tchar* src) {return _tstrcpy (dst, src);}
inline SMTG_CONSTEXPR14 char8* strcpy8 (char8* dst, const char8* src) {return _tstrcpy (dst, src);}
inline SMTG_CONSTEXPR14 char16* strcpy16 (char16* dst, const char16* src) {return _tstrcpy (dst, src);}

//----------------------------------------------------------------------------
template <class T>
inline SMTG_CONSTEXPR14 T* _tstrncpy (T* dest, const T* source, uint32 count)
{
	T* start = dest;
	while (count && (*dest++ = *source++) != 0) // copy string
		count--;

	if (count) // pad out with zeros
	{
		while (--count)
			*dest++ = 0;
	}
	return start;
}

inline SMTG_CONSTEXPR14 tchar* tstrncpy (tchar* dest, const tchar* source, uint32 count) {return _tstrncpy (dest, source, count);}
inline SMTG_CONSTEXPR14 char8* strncpy8 (char8* dest, const char8* source, uint32 count) {return _tstrncpy (dest, source, count);}
inline SMTG_CONSTEXPR14 char16* strncpy16 (char16* dest, const char16* source, uint32 count) {return _tstrncpy (dest, source, count);}

//----------------------------------------------------------------------------
template <class T>
inline SMTG_CONSTEXPR14 T* _tstrcat (T* dst, const T* src)
{
	T* cp = dst;

	while (*cp)
		cp++; // find end of dst

	while ((*cp++ = *src++) != 0) // Copy src to end of dst
		;

	return dst;
}

inline SMTG_CONSTEXPR14 tchar* tstrcat (tchar* dst, const tchar* src) {return _tstrcat (dst, src); }
inline SMTG_CONSTEXPR14 char8* strcat8 (char8* dst, const char8* src) {return _tstrcat (dst, src); }
inline SMTG_CONSTEXPR14 char16* strcat16 (char16* dst, const char16* src) {return _tstrcat (dst, src); }

//----------------------------------------------------------------------------
inline SMTG_CONSTEXPR14 void str8ToStr16 (char16* dst, const char8* src, int32 n = -1)
{
	int32 i = 0;
	for (;;)
	{
		if (i == (n - 1))
		{
			dst[i] = 0;
			return;
		}

#if BYTEORDER == kBigEndian
		char8* pChr = (char8*)&dst[i];
		pChr[0] = 0;
		pChr[1] = src[i];
#else
		dst[i] = static_cast<char16> (src[i]);
#endif
		if (src[i] == 0)
			break;
		i++;
	}

	while (n > i)
	{
		dst[i] = 0;
		i++;
	}
}

//------------------------------------------------------------------------
inline SMTG_CONSTEXPR14 bool FIDStringsEqual (FIDString id1, FIDString id2)
{
	return (id1 && id2) ? (strcmp8 (id1, id2) == 0) : false;
}

static SMTG_CONSTEXPR const uint32 kPrintfBufferSize = 4096;

#if SMTG_OS_WINDOWS
/* cast between wchar_t and char16 */
inline wchar_t* wscast (char16* s) { static_assert (sizeof (wchar_t) == sizeof (char16), ""); return reinterpret_cast<wchar_t*> (s); }
inline char16* wscast (wchar_t* s) { static_assert (sizeof (wchar_t) == sizeof (char16), ""); return reinterpret_cast<char16*> (s);}
inline const wchar_t* wscast (const char16* s) { static_assert (sizeof (wchar_t) == sizeof (char16), ""); return reinterpret_cast<const wchar_t*> (s); }
inline const char16* wscast (const wchar_t* s) { static_assert (sizeof (wchar_t) == sizeof (char16), ""); return reinterpret_cast<const char16*> (s); }
#endif

//------------------------------------------------------------------------
} // namespace Steinberg
//-----------------------------------------------------------------------------
// Project     : SDK Core
//
// Category    : SDK Core Interfaces
// Filename    : pluginterfaces/base/ftypes.h
// Created by  : Steinberg, 01/2004
// Description : Basic data types
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses. 
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#pragma once

#include "fplatform.h"

#include <cstdint>

//#define UNICODE_OFF 	// disable / enable unicode

#ifdef UNICODE_OFF
	#ifdef UNICODE
	#undef UNICODE
	#endif
#else
	#define UNICODE 1
#endif

#ifdef UNICODE
#define _UNICODE 1
#endif

namespace Steinberg
{
//-----------------------------------------------------------------
// Integral Types
	typedef char int8;
	typedef uint8_t uint8;
	typedef unsigned char uchar;

	typedef int16_t int16;
	typedef uint16_t uint16;

	typedef int32_t int32;
	typedef uint32_t uint32;

	static const int32 kMaxInt32 = INT32_MAX;
	static const int32 kMinInt32 = INT32_MIN;
	static const int32 kMaxLong = kMaxInt32;
	static const int32 kMinLong = kMinInt32;
	static const uint32 kMaxInt32u = UINT32_MAX;

	typedef int64_t int64;
	typedef uint64_t uint64;
	static const int64 kMaxInt64 = INT64_MAX;
	static const int64 kMinInt64 = INT64_MIN;
	static const uint64 kMaxInt64u = UINT64_MAX;

//-----------------------------------------------------------------
// other Semantic Types
	typedef int64 TSize;   // byte (or other) sizes
	typedef int32 tresult; // result code
//-----------------------------------------------------------------
	static const float kMaxFloat = 3.40282346638528860E38;
	static const double kMaxDouble = 1.7976931348623158E308;

#if SMTG_PLATFORM_64
	typedef uint64 TPtrInt;
#else
	typedef uint32 TPtrInt;
#endif

//------------------------------------------------------------------
// Boolean
	typedef uint8 TBool;

//------------------------------------------------------------------
// Char / Strings
	typedef char char8;
	typedef char16_t char16;

#ifdef UNICODE
	typedef char16 tchar;
#else
	typedef char8 tchar;
#endif

	typedef const char8* CStringA;
	typedef const char16* CStringW;
	typedef const tchar* CString;
	inline bool strEmpty (const tchar* str) { return (!str || *str == 0); }
	inline bool str8Empty (const char8* str) { return (!str || *str == 0); }
	inline bool str16Empty (const char16* str) { return (!str || *str == 0); }

	typedef const char8* FIDString; // identifier as string (used for attributes, messages)

	const FIDString kPlatformStringWin = "WIN";
	const FIDString kPlatformStringMac = "MAC";
	const FIDString kPlatformStringIOS = "IOS";
	const FIDString kPlatformStringLinux = "Linux";
#if SMTG_OS_WINDOWS
	const FIDString kPlatformString = kPlatformStringWin;
#elif SMTG_OS_IOS
	const FIDString kPlatformString = kPlatformStringIOS;
#elif SMTG_OS_MACOS
	const FIDString kPlatformString = kPlatformStringMac;
#elif SMTG_OS_LINUX
	const FIDString kPlatformString = kPlatformStringLinux;
#endif

//------------------------------------------------------------------------
/** Coordinates	*/
	typedef int32 UCoord;
	static const UCoord kMaxCoord = ((UCoord)0x7FFFFFFF);
	static const UCoord kMinCoord = ((UCoord)-0x7FFFFFFF);
}	// namespace Steinberg


//----------------------------------------------------------------------------
/** Byte-order Conversion Macros 
*/
#define SWAP_32(l) { \
	unsigned char* p = (unsigned char*)& (l); \
	unsigned char t; \
	t = p[0]; p[0] = p[3]; p[3] = t; t = p[1]; p[1] = p[2]; p[2] = t; }

#define SWAP_16(w) { \
	unsigned char* p = (unsigned char*)& (w); \
	unsigned char t; \
	t = p[0]; p[0] = p[1]; p[1] = t; }

#define SWAP_64(i) { \
	unsigned char* p = (unsigned char*)& (i); \
	unsigned char t; \
	t = p[0]; p[0] = p[7]; p[7] = t; t = p[1]; p[1] = p[6]; p[6] = t; \
	t = p[2]; p[2] = p[5]; p[5] = t; t = p[3]; p[3] = p[4]; p[4] = t;}

namespace Steinberg
{
	static inline void FSwap (int8&) {}
	static inline void FSwap (uint8&) {}
	static inline void FSwap (int16& i16) { SWAP_16 (i16) }
	static inline void FSwap (uint16& i16) { SWAP_16 (i16) }
	static inline void FSwap (int32& i32) { SWAP_32 (i32) }
	static inline void FSwap (uint32& i32) { SWAP_32 (i32) }
	static inline void FSwap (int64& i64) { SWAP_64 (i64) }
	static inline void FSwap (uint64& i64) { SWAP_64 (i64) }
}

// always inline macros (only when RELEASE is 1)
//----------------------------------------------------------------------------
#if RELEASE
    #if SMTG_OS_MACOS || SMTG_OS_LINUX || defined(__MINGW32__)
		#define SMTG_ALWAYS_INLINE	__inline__ __attribute__((__always_inline__))
		#define SMTG_NEVER_INLINE __attribute__((noinline))
	#elif SMTG_OS_WINDOWS
		#define SMTG_ALWAYS_INLINE	__forceinline
		#define SMTG_NEVER_INLINE __declspec(noinline)
	#endif
#endif

#ifndef SMTG_ALWAYS_INLINE
	#define SMTG_ALWAYS_INLINE	inline
#endif
#ifndef SMTG_NEVER_INLINE
	#define SMTG_NEVER_INLINE
#endif

#ifndef SMTG_CPP11_STDLIBSUPPORT
// Enable this for old compilers
// #define nullptr NULL
#endif
//-----------------------------------------------------------------------------
// Project     : SDK Core
//
// Category    : SDK Core Interfaces
// Filename    : pluginterfaces/base/funknown.h
// Created by  : Steinberg, 01/2004
// Description : Basic Interface
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses. 
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#pragma once

#include "pluginterfaces/base/fplatform.h"
#include "pluginterfaces/base/ftypes.h"
#include "pluginterfaces/base/smartpointer.h"
#include <cstring>

#if SMTG_CPP11_STDLIBSUPPORT
#include <type_traits>
#endif

//------------------------------------------------------------------------
/*! \defgroup pluginBase Basic Interfaces
*/

//------------------------------------------------------------------------
//  Unique Identifier macros
//------------------------------------------------------------------------

#if COM_COMPATIBLE
#define INLINE_UID(l1, l2, l3, l4) \
{ \
	(::Steinberg::int8)(((::Steinberg::uint32)(l1) & 0x000000FF)      ), (::Steinberg::int8)(((::Steinberg::uint32)(l1) & 0x0000FF00) >>  8), \
	(::Steinberg::int8)(((::Steinberg::uint32)(l1) & 0x00FF0000) >> 16), (::Steinberg::int8)(((::Steinberg::uint32)(l1) & 0xFF000000) >> 24), \
	(::Steinberg::int8)(((::Steinberg::uint32)(l2) & 0x00FF0000) >> 16), (::Steinberg::int8)(((::Steinberg::uint32)(l2) & 0xFF000000) >> 24), \
	(::Steinberg::int8)(((::Steinberg::uint32)(l2) & 0x000000FF)      ), (::Steinberg::int8)(((::Steinberg::uint32)(l2) & 0x0000FF00) >>  8), \
	(::Steinberg::int8)(((::Steinberg::uint32)(l3) & 0xFF000000) >> 24), (::Steinberg::int8)(((::Steinberg::uint32)(l3) & 0x00FF0000) >> 16), \
	(::Steinberg::int8)(((::Steinberg::uint32)(l3) & 0x0000FF00) >>  8), (::Steinberg::int8)(((::Steinberg::uint32)(l3) & 0x000000FF)      ), \
	(::Steinberg::int8)(((::Steinberg::uint32)(l4) & 0xFF000000) >> 24), (::Steinberg::int8)(((::Steinberg::uint32)(l4) & 0x00FF0000) >> 16), \
	(::Steinberg::int8)(((::Steinberg::uint32)(l4) & 0x0000FF00) >>  8), (::Steinberg::int8)(((::Steinberg::uint32)(l4) & 0x000000FF)      )  \
}
#else
#define INLINE_UID(l1, l2, l3, l4) \
{ \
	(::Steinberg::int8)(((::Steinberg::uint32)(l1) & 0xFF000000) >> 24), (::Steinberg::int8)(((::Steinberg::uint32)(l1) & 0x00FF0000) >> 16), \
	(::Steinberg::int8)(((::Steinberg::uint32)(l1) & 0x0000FF00) >>  8), (::Steinberg::int8)(((::Steinberg::uint32)(l1) & 0x000000FF)      ), \
	(::Steinberg::int8)(((::Steinberg::uint32)(l2) & 0xFF000000) >> 24), (::Steinberg::int8)(((::Steinberg::uint32)(l2) & 0x00FF0000) >> 16), \
	(::Steinberg::int8)(((::Steinberg::uint32)(l2) & 0x0000FF00) >>  8), (::Steinberg::int8)(((::Steinberg::uint32)(l2) & 0x000000FF)      ), \
	(::Steinberg::int8)(((::Steinberg::uint32)(l3) & 0xFF000000) >> 24), (::Steinberg::int8)(((::Steinberg::uint32)(l3) & 0x00FF0000) >> 16), \
	(::Steinberg::int8)(((::Steinberg::uint32)(l3) & 0x0000FF00) >>  8), (::Steinberg::int8)(((::Steinberg::uint32)(l3) & 0x000000FF)      ), \
	(::Steinberg::int8)(((::Steinberg::uint32)(l4) & 0xFF000000) >> 24), (::Steinberg::int8)(((::Steinberg::uint32)(l4) & 0x00FF0000) >> 16), \
	(::Steinberg::int8)(((::Steinberg::uint32)(l4) & 0x0000FF00) >>  8), (::Steinberg::int8)(((::Steinberg::uint32)(l4) & 0x000000FF)      )  \
}
#endif

//------------------------------------------------------------------------
#define DECLARE_UID(name, l1, l2, l3, l4) SMTG_CONSTEXPR14 ::Steinberg::TUID name = INLINE_UID (l1, l2, l3, l4);

//------------------------------------------------------------------------
#define EXTERN_UID(name) extern const ::Steinberg::TUID name;

#ifdef INIT_CLASS_IID
#define DECLARE_CLASS_IID(ClassName, l1, l2, l3, l4)                                               \
	static SMTG_CONSTEXPR14 const ::Steinberg::TUID ClassName##_iid = INLINE_UID (l1, l2, l3, l4); \
	const ::Steinberg::FUID ClassName::iid (ClassName##_iid);
#else
#define DECLARE_CLASS_IID(ClassName, l1, l2, l3, l4) \
	static SMTG_CONSTEXPR14 const ::Steinberg::TUID ClassName##_iid = INLINE_UID (l1, l2, l3, l4);
#endif

#define DEF_CLASS_IID(ClassName) const ::Steinberg::FUID ClassName::iid (ClassName##_iid);

#define INLINE_UID_OF(ClassName) ClassName##_iid

#define INLINE_UID_FROM_FUID(x) \
	INLINE_UID (x.getLong1 (), x.getLong2 (), x.getLong3 (), x.getLong4 ())

//------------------------------------------------------------------------
//  FUnknown implementation macros
//------------------------------------------------------------------------

#define DECLARE_FUNKNOWN_METHODS                                                                      \
public:	                                                                                              \
	virtual ::Steinberg::tresult PLUGIN_API queryInterface (const ::Steinberg::TUID _iid, void** obj) SMTG_OVERRIDE; \
	virtual ::Steinberg::uint32 PLUGIN_API addRef () SMTG_OVERRIDE;                                   \
	virtual ::Steinberg::uint32 PLUGIN_API release () SMTG_OVERRIDE;                                  \
protected :                                                                                           \
	::Steinberg::int32 __funknownRefCount;                                                            \
public:

//------------------------------------------------------------------------

#define DELEGATE_REFCOUNT(ClassName)											        \
public:																			        \
	virtual ::Steinberg::uint32 PLUGIN_API addRef () SMTG_OVERRIDE { return ClassName::addRef ();  } \
	virtual ::Steinberg::uint32 PLUGIN_API release () SMTG_OVERRIDE { return ClassName::release (); }

//------------------------------------------------------------------------
#define IMPLEMENT_REFCOUNT(ClassName)                                          \
::Steinberg::uint32 PLUGIN_API ClassName::addRef ()                            \
{                                                                              \
	return ::Steinberg::FUnknownPrivate::atomicAdd (__funknownRefCount, 1);    \
}                                                                              \
::Steinberg::uint32 PLUGIN_API ClassName::release ()                           \
{                                                                              \
	if (::Steinberg::FUnknownPrivate::atomicAdd (__funknownRefCount, -1) == 0) \
	{                                                                          \
		delete this;                                                           \
		return 0;                                                              \
	}                                                                          \
	return __funknownRefCount;                                                 \
}

//------------------------------------------------------------------------
#define FUNKNOWN_CTOR	{ __funknownRefCount = 1; }
#if SMTG_FUNKNOWN_DTOR_ASSERT
#include <cassert>
#define FUNKNOWN_DTOR { assert (__funknownRefCount == 0); }
#else
#define FUNKNOWN_DTOR
#endif

//------------------------------------------------------------------------
#define QUERY_INTERFACE(iid, obj, InterfaceIID, InterfaceName)  \
if (::Steinberg::FUnknownPrivate::iidEqual (iid, InterfaceIID)) \
{                                                               \
	addRef ();                                                  \
	*obj = static_cast< InterfaceName* >(this);                 \
	return ::Steinberg::kResultOk;                              \
}

//------------------------------------------------------------------------
#define IMPLEMENT_QUERYINTERFACE(ClassName, InterfaceName, ClassIID)                                \
::Steinberg::tresult PLUGIN_API ClassName::queryInterface (const ::Steinberg::TUID _iid, void** obj)\
{                                                                                                   \
	QUERY_INTERFACE (_iid, obj, ::Steinberg::FUnknown::iid, InterfaceName)                          \
	QUERY_INTERFACE (_iid, obj, ClassIID, InterfaceName)                                            \
	*obj = nullptr;                                                                                 \
	return ::Steinberg::kNoInterface;                                                               \
}

//------------------------------------------------------------------------
#define IMPLEMENT_FUNKNOWN_METHODS(ClassName,InterfaceName,ClassIID) \
	IMPLEMENT_REFCOUNT (ClassName)                                   \
	IMPLEMENT_QUERYINTERFACE (ClassName, InterfaceName, ClassIID)

//------------------------------------------------------------------------
//  Result Codes
//------------------------------------------------------------------------

namespace Steinberg {

//------------------------------------------------------------------------
#if COM_COMPATIBLE
#if SMTG_OS_WINDOWS
enum
{
	kNoInterface		= static_cast<tresult>(0x80004002L),	// E_NOINTERFACE
	kResultOk			= static_cast<tresult>(0x00000000L),	// S_OK
	kResultTrue			= kResultOk,
	kResultFalse		= static_cast<tresult>(0x00000001L),	// S_FALSE
	kInvalidArgument	= static_cast<tresult>(0x80070057L),	// E_INVALIDARG
	kNotImplemented		= static_cast<tresult>(0x80004001L),	// E_NOTIMPL
	kInternalError		= static_cast<tresult>(0x80004005L),	// E_FAIL
	kNotInitialized		= static_cast<tresult>(0x8000FFFFL),	// E_UNEXPECTED
	kOutOfMemory		= static_cast<tresult>(0x8007000EL)		// E_OUTOFMEMORY
};
#else
enum
{
	kNoInterface		= static_cast<tresult>(0x80000004L),	// E_NOINTERFACE
	kResultOk			= static_cast<tresult>(0x00000000L),	// S_OK
	kResultTrue			= kResultOk,
	kResultFalse		= static_cast<tresult>(0x00000001L),	// S_FALSE
	kInvalidArgument	= static_cast<tresult>(0x80000003L),	// E_INVALIDARG
	kNotImplemented		= static_cast<tresult>(0x80000001L),	// E_NOTIMPL
	kInternalError		= static_cast<tresult>(0x80000008L),	// E_FAIL
	kNotInitialized		= static_cast<tresult>(0x8000FFFFL),	// E_UNEXPECTED
	kOutOfMemory		= static_cast<tresult>(0x80000002L)		// E_OUTOFMEMORY
};
#endif
#else
enum
{
	kNoInterface = -1,
	kResultOk,
	kResultTrue = kResultOk,
	kResultFalse,
	kInvalidArgument,
	kNotImplemented,
	kInternalError,
	kNotInitialized,
	kOutOfMemory
};
#endif

//------------------------------------------------------------------------
typedef int64 LARGE_INT; // obsolete

//------------------------------------------------------------------------
//	FUID class declaration
//------------------------------------------------------------------------
typedef char TUID[16]; ///< plain UID type

#if SMTG_CPP14
//------------------------------------------------------------------------
inline SMTG_CONSTEXPR14 void copyTUID (char* dst, const char* src)
{
	for (auto i = 0; i < 16; ++i)
		dst[i] = src[i];
}
#endif

//------------------------------------------------------------------------
/* FUnknown private */
namespace FUnknownPrivate {
SMTG_ALWAYS_INLINE bool iidEqual (const void* iid1, const void* iid2)
{
	const uint64* p1 = reinterpret_cast<const uint64*> (iid1);
	const uint64* p2 = reinterpret_cast<const uint64*> (iid2);
	return p1[0] == p2[0] && p1[1] == p2[1];
}

int32 PLUGIN_API atomicAdd (int32& value, int32 amount);
}

//------------------------------------------------------------------------
/** Handling 16 Byte Globally Unique Identifiers.
\ingroup pluginBase

Each interface declares its identifier as static member inside the interface
namespace (e.g. FUnknown::iid).
*/
class FUID
{
public:
//------------------------------------------------------------------------
	FUID ();
	FUID (uint32 l1, uint32 l2, uint32 l3, uint32 l4);
	FUID (const FUID&);
	virtual ~FUID () {}

#if SMTG_CPP11_STDLIBSUPPORT
	FUID (FUID&& other);
	FUID& operator= (FUID&& other);
#endif

	/** Generates a new Unique Identifier (UID).
	    Will return true for success. If the return value is false, either no
	    UID is generated or the UID is not guaranteed to be unique worldwide. */
	bool generate ();

	/** Checks if the UID data is valid.
	    The default constructor initializes the memory with zeros. */
	bool isValid () const;

	FUID& operator = (const FUID& f);
	bool operator == (const FUID& f) const { return ::Steinberg::FUnknownPrivate::iidEqual (data, f.data); }
	bool operator < (const FUID& f) const { return memcmp (data, f.data, sizeof (TUID)) < 0; }
	bool operator != (const FUID& f) const   { return !::Steinberg::FUnknownPrivate::iidEqual (data, f.data); }

	uint32 getLong1 () const;
	uint32 getLong2 () const;
	uint32 getLong3 () const;
	uint32 getLong4 () const;

	void from4Int (uint32 d1, uint32 d2, uint32 d3, uint32 d4);
	void to4Int (uint32& d1, uint32& d2, uint32& d3, uint32& d4) const;

	typedef char8 String[33];

	/** Converts UID to a string.
		The string will be 32 characters long, representing the hexadecimal values
		of each data byte (e.g. "9127BE30160E4BB69966670AA6087880"). 
		
		Typical use-case is:
		\code{.cpp}
		char8[33] strUID = {0};
		FUID uid;
		if (uid.generate ())
			uid.toString (strUID);
		\endcode
		*/
	void toString (char8* string) const;

	/** Sets the UID data from a string.
	    The string has to be 32 characters long, where each character-pair is
	    the ASCII-encoded hexadecimal value of the corresponding data byte. */
	bool fromString (const char8* string);

	/** Converts UID to a string in Microsoft(R) OLE format.
	(e.g. "{c200e360-38c5-11ce-ae62-08002b2b79ef}") */
	void toRegistryString (char8* string) const;

	/** Sets the UID data from a string in Microsoft(R) OLE format. */
	bool fromRegistryString (const char8* string);

	enum UIDPrintStyle
	{
		kINLINE_UID,  ///< "INLINE_UID (0x00000000, 0x00000000, 0x00000000, 0x00000000)"
		kDECLARE_UID, ///< "DECLARE_UID (0x00000000, 0x00000000, 0x00000000, 0x00000000)"
		kFUID,        ///< "FUID (0x00000000, 0x00000000, 0x00000000, 0x00000000)"
		kCLASS_UID    ///< "DECLARE_CLASS_IID (Interface, 0x00000000, 0x00000000, 0x00000000, 0x00000000)"
	};
	/** Prints the UID to a string (or debug output if string is NULL).
	    \param style can be chosen from the FUID::UIDPrintStyle enumeration. 
	    \param string is the output string if not NULL. 
	    \param stringBufferSize is the size of the output string  */
	void print (int32 style, char8* string = nullptr, size_t stringBufferSize = 0) const;

#if SMTG_CPP17
	[[deprecated ("Use the print method with the buffer size")]]
#endif
	void print (char8* string = nullptr, int32 style = kINLINE_UID) const;

	template <size_t N>
	inline explicit FUID (const char (&uid)[N])
	{
#if SMTG_CPP11_STDLIBSUPPORT
		static_assert (N == sizeof (TUID), "only TUID allowed");
#endif
		memcpy (data, uid, sizeof (TUID));
	}
	inline void toTUID (TUID result) const { memcpy (result, data, sizeof (TUID)); }
	inline operator const TUID& () const { return data; }
	inline const TUID& toTUID () const { return data; }

	static FUID fromTUID (const TUID uid)
	{
		FUID res;
		if (uid)
			memcpy (res.data, uid, sizeof (TUID));
		return res;
	}

//------------------------------------------------------------------------
protected:
	TUID data;
};

#if SMTG_CPP11_STDLIBSUPPORT
template <typename T>
inline bool operator== (const FUID& f1, T f2)
{
	static_assert (
	    std::is_same<typename std::remove_cv<T>::type, FUID>::value,
	    "Do not compare a FUID with a TUID directly. Either convert the TUID to a FUID and compare them or use FUnknownPrivate::iidEqual");
	return f1.operator== (f2);
}
#endif

//------------------------------------------------------------------------
// FUnknown
//------------------------------------------------------------------------
/**	The basic interface of all interfaces.
\ingroup pluginBase

- The FUnknown::queryInterface method is used to retrieve pointers to other
  interfaces of the object.
- FUnknown::addRef and FUnknown::release manage the lifetime of the object.
  If no more references exist, the object is destroyed in memory.

Interfaces are identified by 16 byte Globally Unique Identifiers.
The SDK provides a class called FUID for this purpose.

\ref howtoClass
*/
class FUnknown
{
public:

//------------------------------------------------------------------------
	/** Query for a pointer to the specified interface.
	Returns kResultOk on success or kNoInterface if the object does not implement the interface.
	The object has to call addRef when returning an interface.
	\param _iid : (in) 16 Byte interface identifier (-> FUID)
	\param obj : (out) On return, *obj points to the requested interface */
	virtual tresult PLUGIN_API queryInterface (const TUID _iid, void** obj) = 0;

	/** Adds a reference and returns the new reference count.
	\par Remarks:
	    The initial reference count after creating an object is 1. */
	virtual uint32 PLUGIN_API addRef () = 0;

	/** Releases a reference and returns the new reference count.
	If the reference count reaches zero, the object will be destroyed in memory. */
	virtual uint32 PLUGIN_API release () = 0;

//------------------------------------------------------------------------
	static const FUID iid;
//------------------------------------------------------------------------
};


DECLARE_CLASS_IID (FUnknown, 0x00000000, 0x00000000, 0xC0000000, 0x00000046)

//------------------------------------------------------------------------
// FUnknownPtr
//------------------------------------------------------------------------
/** FUnknownPtr - automatic interface conversion and smart pointer in one.
    This template class can be used for interface conversion like this:
 \code{.cpp}
IPtr<IPath> path = owned (FHostCreate (IPath, hostClasses));
FUnknownPtr<IPath2> path2 (path); // does a query interface for IPath2
if (path2)
    ...
 \endcode
*/
template <class I>
class FUnknownPtr : public IPtr<I>
{
public:
//------------------------------------------------------------------------
	inline FUnknownPtr (FUnknown* unknown); // query interface
	inline FUnknownPtr (const FUnknownPtr& p) : IPtr<I> (p) {}
	inline FUnknownPtr () {}

	inline FUnknownPtr& operator= (const FUnknownPtr& p)
	{
		IPtr<I>::operator= (p);
		return *this;
	}
	inline I* operator= (FUnknown* unknown);
	inline I* getInterface () { return this->ptr; }

#if SMTG_CPP11_STDLIBSUPPORT
	inline FUnknownPtr (FUnknownPtr&& p) SMTG_NOEXCEPT : IPtr<I> (std::move (p)) {}
	inline FUnknownPtr& operator= (FUnknownPtr&& p) SMTG_NOEXCEPT
	{
		IPtr<I>::operator= (std::move (p));
		return *this;
	}
#endif
};

#if SMTG_CPP11_STDLIBSUPPORT

//------------------------------------------------------------------------
namespace FUnknownPrivate {

template <typename T>
struct Void : std::false_type
{
	using Type = void;
};

template <typename T>
using VoidT = typename Void<T>::Type;

//------------------------------------------------------------------------
/**
 *  This type trait detects if a class has an @c iid member variable. It is used to detect if
 *  the FUID and DECLARE_CLASS_IID method or the U::UID method is used.
 */
template <typename T, typename U = void>
struct HasIIDType : std::false_type
{
};

//------------------------------------------------------------------------
template <typename T>
struct HasIIDType<T, FUnknownPrivate::VoidT<typename T::IID>> : std::true_type
{
};

//------------------------------------------------------------------------
} // FUnknownPrivate

//------------------------------------------------------------------------
/** @return the TUID for an interface which uses the U::UID method. */
template <typename T,
          typename std::enable_if<FUnknownPrivate::HasIIDType<T>::value>::type* = nullptr>
const TUID& getTUID ()
{
	return T::IID::toTUID ();
}

//------------------------------------------------------------------------
/** @return the TUID for an interface which uses the FUID and DECLARE_CLASS_IID method. */
template <typename T,
          typename std::enable_if<!FUnknownPrivate::HasIIDType<T>::value>::type* = nullptr>
const TUID& getTUID ()
{
	return T::iid.toTUID ();
}

#else // SMTG_CPP11_STDLIBSUPPORT

template<typename T>
const TUID& getTUID ()
{
	return T::iid.toTUID ();
}

#endif // SMTG_CPP11_STDLIBSUPPORT

//------------------------------------------------------------------------
template <class I>
inline FUnknownPtr<I>::FUnknownPtr (FUnknown* unknown)
{
	if (unknown && unknown->queryInterface (getTUID<I> (), (void**)&this->ptr) != kResultOk)
		this->ptr = 0;
}

//------------------------------------------------------------------------
template <class I>
inline I* FUnknownPtr<I>::operator= (FUnknown* unknown)
{
	I* newPtr = 0;
	if (unknown && unknown->queryInterface (getTUID<I> (), (void**)&newPtr) == kResultOk)
	{
		OPtr<I> rel (newPtr);
		return IPtr<I>::operator= (newPtr);
	}

	return IPtr<I>::operator= (0);
}

//------------------------------------------------------------------------
// FReleaser (obsolete)
//------------------------------------------------------------------------
/** Release an interface using automatic object (obsolete).
This class is obsolete and is only kept for compatibility.
The replacement for FReleaser is OPtr.

Usage example with FReleaser:
\code{.cpp}
void someFunction ()
{
    IPath* path = pathCreateMethod ();
    FReleaser releaser (path);
    .... do something with path...
    .... path not used anymore, releaser will destroy it when leaving function scope
}
\endcode
Usage example with OPtr:
\code{.cpp}
void someFunction ()
{
    OPtr<IPath> path = pathCreateMethod ();
    .... do something with path...
    .... path not used anymore, OPtr will destroy it when leaving function scope
}
\endcode
*/
struct FReleaser
{
	FReleaser (FUnknown* u) : u (u) {}
	~FReleaser ()
	{
		if (u)
			u->release ();
	}
	FUnknown* u;
};

//------------------------------------------------------------------------
} // namespace Steinberg
//-----------------------------------------------------------------------------
// Flags       : clang-format SMTGSequencer
// Project     : SDK Core
//
// Category    : SDK Core Interfaces
// Filename    : pluginterfaces/base/funknownimpl.h
// Created by  : Steinberg, 10/2021
// Description : Steinberg Module Architecture Interface Implementation Helper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses. 
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#pragma once

//------------------------------------------------------------------------
#include "pluginterfaces/base/fplatform.h"
#include "pluginterfaces/base/funknown.h"
#include <atomic>
#include <type_traits>

//------------------------------------------------------------------------
#if !(SMTG_CPP11)
#error "C++11 is required for this header"
#endif

// clang-format off
/**
This header provides classes for working with FUnknown.

An interface which shall support Steinbergs Module Architecture should inherit from U::Unknown and provide a typedef
@c IID of type U::UID.

On OS X you can generate an U::UID and copy it to the clipboard with the following shell command:
@code
uuidgen | { read id; echo -n "using IID = U::UID<0x${id:0:8}, 0x${id:9:4}${id:14:4}, 0x${id:19:4}${id:24:4}, 0x${id:28:8}>;" ; } | pbcopy
@endcode

Example:
@code{.cpp}
struct IFoo : public U::Unknown
{
    // Use a generated random uid here.
    using IID = U::UID<0x01234567, 0x89012345, 0x67890123, 0x45678901>;

    virtual void bar () = 0;
};
@endcode

A class implementing the interface @c IFoo uses the U::Implements template to specify the
interfaces it implements. All interfaces which the class should derive from need to be listed in the
U::Directly template.

Example:
@code{.cpp}
struct FooImpl : public U::Implements<U::Directly<IFoo>>
{
    void bar () override {}
};
@endcode

To check if a class can provide a specific interface use the U::cast function.

Example:
@code{.cpp}
void test (U::Unknown* obj)
{
    if (auto foo = U::cast<IFoo> (obj))
    {
        // obj provided IFoo
    }
}
@endcode

The U::Implements class supports a second template parameter U::Indirectly for specifying
a list of interfaces which should be available via @c queryInterface but not inherited from.
This is useful if an interface extends another interface.

Example:
@code{.cpp}
struct IBar : public IFoo
{
    using IID = U::UID<0x11223344, 0x55667788, 0x99001122, 0x33445566>;

    virtual void baz () = 0;
};

struct BarImpl : public U::Implements<U::Directly<IBar>, U::Indirectly<IFoo>>
{
    void bar () override {}
    void baz () override {}
};
@endcode

In some cases a class shall be extended and an additional interface implemented.
This is possible with the U::Extends template which is a generalization of the U::Implements
template and allows specifying a base class from which should be inherited.

Example:
@code{.cpp}
struct ITest : public U::Unknown
{
    using IID = U::UID<0x99887766, 0x55443322, 0x11009988, 0x77665544>;

    virtual bool equal (int a, int b) const = 0;
};

struct TestImpl : public U::Extends<FooImpl, U::Directly<ITest>>
{
    bool equal (int a, int b) const override { return a == b; }
};
@endcode

To pass arbitrary arguments to the specified base class one can use the inherited @c Base
typedef. All arguments passed to @c Base are automatically perfectly forwarded to the base class.

In the following example the value 42 is passed to the @c AlternativeFooImpl base class:
@code{.cpp}
struct AlternativeFooImpl : public U::Implements<U::Directly<IFoo>>
{
    AlternativeFooImpl (int dummy = 0) : dummy {dummy} {}
    void bar () override {}

    int dummy;
};

struct AlternativeTestImpl : public U::Extends<AlternativeFooImpl, U::Directly<ITest>>
{
    AlternativeTestImpl () : Base {42} {}

    bool equal (int a, int b) const override { return a == b; }
};
@endcode
*/
// clang-format on

//------------------------------------------------------------------------
namespace Steinberg {
namespace FUnknownImpl {

/** Typedef to keep everything in this namespace. */
using Unknown = FUnknown;

/** A base class which hides the FUnknown::iid static var */
struct HideIIDBase : FUnknown
{
	using iid = void;
};

/** Common destroyer policy for ski object instances.*/
struct Destroyer
{
	template <typename UnknownT>
	static void destroy (UnknownT* ptr)
	{
		if (!!ptr)
			ptr->release ();
	}
};

template <typename Base, typename D, typename I>
class ImplementsImpl;

/**
 *  This class provides a compile-time uid and enables  interfaces to specify a UID as a simple
 *  typedef. This way the FUID, DECLARE_CLASS_IID and DEF_CLASS_IID code can be omitted.
 */
template <uint32 t1, uint32 t2, uint32 t3, uint32 t4>
struct UID
{
	enum : int8
	{
		l1_1 = static_cast<int8> ((t1 & 0xFF000000) >> 24),
		l1_2 = static_cast<int8> ((t1 & 0x00FF0000) >> 16),
		l1_3 = static_cast<int8> ((t1 & 0x0000FF00) >> 8),
		l1_4 = static_cast<int8> ((t1 & 0x000000FF)),
		l2_1 = static_cast<int8> ((t2 & 0xFF000000) >> 24),
		l2_2 = static_cast<int8> ((t2 & 0x00FF0000) >> 16),
		l2_3 = static_cast<int8> ((t2 & 0x0000FF00) >> 8),
		l2_4 = static_cast<int8> ((t2 & 0x000000FF)),
		l3_1 = static_cast<int8> ((t3 & 0xFF000000) >> 24),
		l3_2 = static_cast<int8> ((t3 & 0x00FF0000) >> 16),
		l3_3 = static_cast<int8> ((t3 & 0x0000FF00) >> 8),
		l3_4 = static_cast<int8> ((t3 & 0x000000FF)),
		l4_1 = static_cast<int8> ((t4 & 0xFF000000) >> 24),
		l4_2 = static_cast<int8> ((t4 & 0x00FF0000) >> 16),
		l4_3 = static_cast<int8> ((t4 & 0x0000FF00) >> 8),
		l4_4 = static_cast<int8> ((t4 & 0x000000FF))
	};

	UID () = delete;

	static const TUID& toTUID ()
	{
		// clang-format off
		static TUID uuid = {
#if COM_COMPATIBLE
			l1_4, l1_3, l1_2, l1_1,
			l2_2, l2_1, l2_4, l2_3,
#else
			l1_1, l1_2, l1_3, l1_4,
			l2_1, l2_2, l2_3, l2_4,
#endif
			l3_1, l3_2, l3_3, l3_4,
			l4_1, l4_2, l4_3, l4_4
		};
		// clang-format on
		return uuid;
	}
};

/** @return the TUID for an interface. */
template <typename T>
const TUID& getTUID ()
{
	return ::Steinberg::getTUID<T> ();
}

/**
 *  Checks if the given Unknown can provide the specified interface and returns it in an IPtr.
 *
 *  @return an IPtr pointing to an instance of the requested interface or nullptr in case the
 *          object does not provide the interface.
 */
template <typename I>
IPtr<I> cast (Unknown* u)
{
	I* out = nullptr;
	return u && u->queryInterface (getTUID<I> (), reinterpret_cast<void**> (&out)) == kResultOk ?
	           owned (out) :
	           nullptr;
}

/** Casts to Unknown* and then to the specified interface. */
template <typename I, typename S, typename T, typename U>
IPtr<I> cast (ImplementsImpl<S, T, U>* u)
{
	return cast<I> (u->unknownCast ());
}

/** Casts to Unknown* and then to the specified interface. */
template <typename I, typename T>
IPtr<I> cast (const IPtr<T>& u)
{
	return cast<I> (u.get ());
}

//------------------------------------------------------------------------
namespace Detail {

/**
 *  This struct implements reference counting for the @c U::Implements template.
 *  It also provides a @c queryInterface method stub to support @c queryInterface
 *  call made in the @c U::Implements template.
 */
struct RefCounted
{
//------------------------------------------------------------------------
	RefCounted () = default;
	RefCounted (const RefCounted&) {}
	RefCounted (RefCounted&& other) SMTG_NOEXCEPT : refCount {other.refCount.load ()} {}
	virtual ~RefCounted () = default;

	RefCounted& operator= (const RefCounted&) { return *this; }
	RefCounted& operator= (RefCounted&& other) SMTG_NOEXCEPT
	{
		refCount = other.refCount.load ();
		return *this;
	}

	uint32 PLUGIN_API addRef () { return ++refCount; }

	uint32 PLUGIN_API release ()
	{
		if (--refCount == 0)
		{
			destroyInstance ();
			refCount = -1000;
			delete this;
			return 0;
		}
		return refCount;
	}

//------------------------------------------------------------------------
private:
	virtual void destroyInstance () {}

	std::atomic<int32> refCount {1};
};

//------------------------------------------------------------------------
struct NonDestroyable
{
//------------------------------------------------------------------------
	NonDestroyable () = default;
	virtual ~NonDestroyable () = default;
	uint32 PLUGIN_API addRef () { return 1000; }
	uint32 PLUGIN_API release () { return 1000; }

private:
	virtual void destroyInstance () {}
};

//------------------------------------------------------------------------
template <typename T>
struct QueryInterfaceEnd : T
{
//------------------------------------------------------------------------
	tresult PLUGIN_API queryInterface (const TUID /*iid*/, void** obj)
	{
		*obj = nullptr;
		return kNoInterface;
	}
//------------------------------------------------------------------------
};

//------------------------------------------------------------------------
} // Detail

/**
 *  This struct is used to group a list of interfaces from which should be inherited and which
 *  should be available via the @c queryInterface method.
 */
template <typename... T>
struct Directly
{
};

/**
 *  This struct is used to group a list of interfaces from which should not be inherited but which
 *  should be available via the @c queryInterface method.
 */
template <typename... T>
struct Indirectly
{
};

template <typename Base, typename D, typename I>
class ImplementsImpl
{
	static_assert (sizeof (Base) == -1, "use U::Directly and U::Indirectly to specify interfaces");
};

template <typename Base, typename... DirectInterfaces, typename... IndirectInterfaces>
class ImplementsImpl<Base, Indirectly<IndirectInterfaces...>, Directly<DirectInterfaces...>>
{
	static_assert (sizeof (Base) == -1, "U::Indirectly only allowed after U::Directly");
};

/** This class implements the required virtual methods for the U::Unknown class. */
template <typename BaseClass, typename I, typename... DirectIFs, typename... IndirectIFs>
class ImplementsImpl<BaseClass, Directly<I, DirectIFs...>, Indirectly<IndirectIFs...>>
: public BaseClass, public I, public DirectIFs...
{
public:
//------------------------------------------------------------------------
	/**
	 *  This is a convenience typedef for the deriving class to pass arguments to the
	 *  constructor, which are in turn passed to the base class of this class.
	 */
	using Base = ImplementsImpl<BaseClass, Directly<I, DirectIFs...>, Indirectly<IndirectIFs...>>;

	template <typename... Args>
	ImplementsImpl (Args&&... args) : BaseClass {std::forward<Args> (args)...}
	{
	}

	tresult PLUGIN_API queryInterface (const TUID tuid, void** obj) override
	{
		if (!obj)
			return kInvalidArgument;

		if (queryInterfaceImpl<I, DirectIFs...> (tuid, *obj) ||
		    queryInterfaceImpl<IndirectIFs...> (tuid, *obj))
		{
			static_cast<Unknown*> (*obj)->addRef ();
			return kResultOk;
		}

		return BaseClass::queryInterface (tuid, obj);
	}

	uint32 PLUGIN_API addRef () override { return BaseClass::addRef (); }
	uint32 PLUGIN_API release () override { return BaseClass::release (); }

	Unknown* unknownCast () { return static_cast<Unknown*> (static_cast<I*> (this)); }

//------------------------------------------------------------------------
private:
	template <typename Interface>
	inline constexpr bool match (const TUID tuid) const noexcept
	{
		return reinterpret_cast<const uint64*> (tuid)[0] ==
		           reinterpret_cast<const uint64*> (getTUID<Interface> ())[0] &&
		       reinterpret_cast<const uint64*> (tuid)[1] ==
		           reinterpret_cast<const uint64*> (getTUID<Interface> ())[1];
	}

	template <int = 0>
	inline constexpr bool queryInterfaceImpl (const TUID, void*&) const noexcept
	{
		return false;
	}

	template <typename Interface, typename... RemainingInterfaces>
	inline bool queryInterfaceImpl (const TUID tuid, void*& obj) noexcept
	{
		if (match<Interface> (tuid) || match<Unknown> (tuid))
		{
			obj = static_cast<Interface*> (this->unknownCast ());
			return true;
		}

		obj = getInterface<RemainingInterfaces...> (tuid);
		return obj != nullptr;
	}

	template <int = 0>
	inline constexpr void* getInterface (const TUID) const noexcept
	{
		return nullptr;
	}

	template <typename Interface, typename... RemainingInterfaces>
	inline void* getInterface (const TUID tuid) noexcept
	{
		return match<Interface> (tuid) ? static_cast<Interface*> (this) :
		                                 getInterface<RemainingInterfaces...> (tuid);
	}
};

/** This typedef enables using a custom base class with the interface implementation. */
template <typename BaseClass, typename D, typename I = Indirectly<>>
using Extends = ImplementsImpl<BaseClass, D, I>;

/** This typedef provides the interface implementation. */
template <typename D, typename I = Indirectly<>>
using Implements = ImplementsImpl<Detail::QueryInterfaceEnd<Detail::RefCounted>, D, I>;

/** This typedef provides the interface implementation for objects which should not be destroyed via
 *	FUnknown::release (like singletons). */
template <typename D, typename I = Indirectly<>>
using ImplementsNonDestroyable =
    ImplementsImpl<Detail::QueryInterfaceEnd<Detail::NonDestroyable>, D, I>;

//------------------------------------------------------------------------
} // FUnknownImpl

//------------------------------------------------------------------------
/** Shortcut namespace for implementing FUnknown based objects. */
namespace U {

using Unknown = FUnknownImpl::HideIIDBase;
using FUnknownImpl::UID;
using FUnknownImpl::Extends;
using FUnknownImpl::Implements;
using FUnknownImpl::ImplementsNonDestroyable;
using FUnknownImpl::Directly;
using FUnknownImpl::Indirectly;
using FUnknownImpl::cast;
using FUnknownImpl::getTUID;

//------------------------------------------------------------------------
} // U
} // Steinberg
//-----------------------------------------------------------------------------
// Project     : SDK Core
//
// Category    : SDK Core Interfaces
// Filename    : pluginterfaces/base/futils.h
// Created by  : Steinberg, 01/2004
// Description : Basic utilities
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses. 
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#pragma once

#include "pluginterfaces/base/ftypes.h"

namespace Steinberg {
//----------------------------------------------------------------------------
// min/max/etc. template functions
template <class T>
inline const T& Min (const T& a, const T& b)
{
	return b < a ? b : a;
}

//----------------------------------------------------------------------------
template <class T>
inline const T& Max (const T& a, const T& b)
{
	return a < b ? b : a;
}

//----------------------------------------------------------------------------
template <class T>
inline T Abs (const T& value)
{
	return (value >= (T)0) ? value : -value;
}

//----------------------------------------------------------------------------
template <class T>
inline T Sign (const T& value)
{
	return (value == (T)0) ? 0 : ((value >= (T)0) ? 1 : -1);
}

//----------------------------------------------------------------------------
template <class T>
inline T Bound (T minval, T maxval, T x)
{
	if (x < minval)
		return minval;
	if (x > maxval)
		return maxval;
	return x;
}

//----------------------------------------------------------------------------
template <class T>
void Swap (T& t1, T& t2)
{
	T tmp = t1;
	t1 = t2;
	t2 = tmp;
}

//----------------------------------------------------------------------------
template <class T>
bool IsApproximateEqual (T t1, T t2, T epsilon)
{
	if (t1 == t2)
		return true;
	T diff = t1 - t2;
	if (diff < 0.0)
		diff = -diff;
	if (diff < epsilon)
		return true;
	return false;
}

//----------------------------------------------------------------------------
template <class T>
inline T ToNormalized (const T& value, const int32 numSteps)
{
	return value / T (numSteps);
}

//----------------------------------------------------------------------------
template <class T>
inline int32 FromNormalized (const T& norm, const int32 numSteps)
{
	return Min<int32> (numSteps, int32 (norm * (numSteps + 1)));
}

//----------------------------------------------------------------------------
// Four character constant
#ifndef CCONST
#define CCONST(a, b, c, d) \
	 ((((int32) (a)) << 24) | (((int32) (b)) << 16) | (((int32) (c)) << 8) | (((int32) (d)) << 0))
#endif

//------------------------------------------------------------------------
} // namespace Steinberg
//-----------------------------------------------------------------------------
// Project     : SDK Core
//
// Category    : SDK Core Interfaces
// Filename    : pluginterfaces/base/fvariant.h
// Created by  : Steinberg, 01/2004
// Description : Basic Interface
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses. 
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#pragma once

#include "pluginterfaces/base/fstrdefs.h"
#include "pluginterfaces/base/funknown.h"

//------------------------------------------------------------------------
namespace Steinberg {

class FUnknown;

//------------------------------------------------------------------------
//  FVariant struct declaration
//------------------------------------------------------------------------
/** A Value of variable type.
 \ingroup pluginBase
 */
class FVariant
{
//------------------------------------------------------------------------
public:
	enum
	{
		kEmpty = 0,
		kInteger = 1 << 0,
		kFloat = 1 << 1,
		kString8 = 1 << 2,
		kObject = 1 << 3,
		kOwner = 1 << 4,
		kString16 = 1 << 5
	};

//------------------------------------------------------------------------
	// ctors
	inline FVariant () { memset (this, 0, sizeof (FVariant)); }
	inline FVariant (const FVariant& variant);

	inline FVariant (bool b) : type (kInteger), intValue (b) {}
	inline FVariant (uint32 v) : type (kInteger), intValue (v) {}
	inline FVariant (int64 v) : type (kInteger), intValue (v) {}
	inline FVariant (double v) : type (kFloat), floatValue (v) {}
	inline FVariant (const char8* str) : type (kString8), string8 (str) {}
	inline FVariant (const char16* str) : type (kString16), string16 (str) {}
	inline FVariant (FUnknown* obj, bool owner = false) : type (kObject), object (obj)
	{
		setOwner (owner);
	}
	inline ~FVariant () { empty (); }

//------------------------------------------------------------------------
	inline FVariant& operator= (const FVariant& variant);

	inline void set (bool b)
	{
		setInt (b);
	}

	inline void set (uint32 v)
	{
		setInt (v);
	}

	inline void set (int64 v)
	{
		setInt (v);
	}

	inline void set (double v)
	{
		setFloat (v);
	}

	inline void set (const char8* c)
	{
		setString8 (c);
	}

	inline void set (const char16* c)
	{
		setString16 (c);
	}

	inline void setInt (int64 v)
	{
		empty ();
		type = kInteger;
		intValue = v;
	}

	inline void setFloat (double v)
	{
		empty ();
		type = kFloat;
		floatValue = v;
	}
	inline void setString8 (const char8* v)
	{
		empty ();
		type = kString8;
		string8 = v;
	}
	inline void setString16 (const char16* v)
	{
		empty ();
		type = kString16;
		string16 = v;
	}

	inline void setObject (FUnknown* obj)
	{
		empty ();
		type = kObject;
		object = obj;
	}

	template <typename T>
	inline T get () const;

	inline int64 getInt () const { return (type & kInteger) ? intValue : 0; }
	inline double getFloat () const { return (type & kFloat) ? floatValue : 0.; }
	inline double getNumber () const
	{
		return (type & kInteger) ? static_cast<double> (intValue) : (type & kFloat) ? floatValue :
																					  0.;
	}
	inline const char8* getString8 () const { return (type & kString8) ? string8 : nullptr; }
	inline const char16* getString16 () const { return (type & kString16) ? string16 : nullptr; }

	inline FUnknown* getObject () const { return (type & kObject) ? object : nullptr; }

	inline uint16 getType () const { return static_cast<uint16> (type & ~(kOwner)); }
	inline bool isEmpty () const { return getType () == kEmpty; }
	inline bool isOwner () const { return (type & kOwner) != 0; }
	inline bool isString () const { return (type & (kString8 | kString16)) != 0; }
	inline void setOwner (bool state)
	{
		if (state)
			type |= kOwner;
		else
			type &= ~kOwner;
	}

	void empty ();
//------------------------------------------------------------------------
	uint16 type;
	union
	{
		int64 intValue;
		double floatValue;
		const char8* string8;
		const char16* string16;
		FUnknown* object;
	};
};

//------------------------------------------------------------------------
inline bool operator== (const FVariant& v1, const FVariant& v2)
{
#if SMTG_PLATFORM_64
	return v1.type == v2.type && v1.intValue == v2.intValue;
#else
	if (v1.type != v2.type)
		return false;
	if (v1.type & (FVariant::kString8 | FVariant::kString16 | FVariant::kObject))
		return v1.string8 == v2.string8; // pointer type comparisons
	return v1.intValue == v2.intValue; // intValue & double comparison

#endif
}

template <>
inline bool FVariant::get<bool> () const
{
	return getInt () != 0;
}

template <>
inline uint32 FVariant::get<uint32> () const
{
	return static_cast<uint32> (getInt ());
}

template <>
inline int32 FVariant::get<int32> () const
{
	return static_cast<int32> (getInt ());
}

template <>
inline int64 FVariant::get<int64> () const
{
	return static_cast<int64> (getInt ());
}

template <>
inline float FVariant::get<float> () const
{
	return static_cast<float> (getFloat ());
}

template <>
inline double FVariant::get<double> () const
{
	return getFloat ();
}

template <>
inline const char8* FVariant::get<const char8*> () const
{
	return getString8 ();
}

template <>
inline const char16* FVariant::get<const char16*> () const
{
	return getString16 ();
}

template <>
inline FUnknown* FVariant::get<FUnknown*> () const
{
	return getObject ();
}

//------------------------------------------------------------------------
inline bool operator!= (const FVariant& v1, const FVariant& v2) { return !(v1 == v2); }

//------------------------------------------------------------------------
inline FVariant::FVariant (const FVariant& variant) : type (kEmpty) { *this = variant; }

//------------------------------------------------------------------------
inline void FVariant::empty ()
{
	if (type & kOwner)
	{
		if ((type & kString8) && string8)
			delete[] string8;
		else if ((type & kString16) && string16)
			delete[] string16;

		else if ((type & kObject) && object)
			object->release ();
	}
	memset (this, 0, sizeof (FVariant));
}

//------------------------------------------------------------------------
inline FVariant& FVariant::operator= (const FVariant& variant)
{
	empty ();

	type = variant.type;

	if ((type & kString8) && variant.string8)
	{
		string8 = new char8[strlen (variant.string8) + 1];
		strcpy (const_cast<char8*> (string8), variant.string8);
		type |= kOwner;
	}
	else if ((type & kString16) && variant.string16)
	{
		auto len = static_cast<size_t> (strlen16 (variant.string16));
		string16 = new char16[len + 1];
		char16* tmp = const_cast<char16*> (string16);
		memcpy (tmp, variant.string16, len * sizeof (char16));
		tmp[len] = 0;
		type |= kOwner;
	}
	else if ((type & kObject) && variant.object)
	{
		object = variant.object;
		object->addRef ();
		type |= kOwner;
	}
	else
		intValue = variant.intValue; // copy memory

	return *this;
}

//------------------------------------------------------------------------
} // namespace Steinberg
//-----------------------------------------------------------------------------
// Project     : SDK Core
//
// Category    : SDK Core Interfaces
// Filename    : pluginterfaces/base/geoconstants.h
// Created by  : Steinberg, 11/2014
// Description : Defines orientations and directions as also used by fpoint.h and frect.h
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses. 
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#pragma once

//------------------------------------------------------------------------
namespace Steinberg {

//------------------------------------------------------------------------
enum Direction 
{
	kNorth,
	kNorthEast,
	kEast,
	kSouthEast,
	kSouth,
	kSouthWest,
	kWest,
	kNorthWest,
	kNoDirection,  //same position or center point of a geometry

	kNumberOfDirections,
};

//------------------------------------------------------------------------
enum Orientation 
{
	kHorizontal,
	kVertical,

	kNumberOfOrientations
};

//------------------------------------------------------------------------
namespace GeoConstants {

//------------------------------------------------------------------------
inline Direction toOpposite (Direction dir) 
{
	switch (dir) 
	{
		case kNorth :		return kSouth;
		case kNorthEast :	return kSouthWest;
		case kEast :		return kWest;
		case kSouthEast :	return kNorthWest;
		case kSouth :		return kNorth;
		case kSouthWest :	return kNorthEast;
		case kWest :		return kEast;
		case kNorthWest :	return kSouthEast;
		case kNoDirection : return kNoDirection;
		default:
			return kNumberOfDirections;
	}
}

//------------------------------------------------------------------------
inline Orientation toOrientation (Direction dir) 
{
	switch (dir) 
	{
		case kNorth :		return kVertical;
		case kEast :		return kHorizontal;
		case kSouth :		return kVertical;
		case kWest :		return kHorizontal;
		default:
			return kNumberOfOrientations;
	}
}

//------------------------------------------------------------------------
inline Orientation toOrthogonalOrientation (Orientation dir) 
{
	switch (dir) 
	{
		case kVertical :	return kHorizontal;
		case kHorizontal :	return kVertical;
		default:
			return kNumberOfOrientations;
	}
}

//------------------------------------------------------------------------
} // namespace GeoConstants
} // namespace Steinberg
//-----------------------------------------------------------------------------
// Project     : SDK Core
//
// Category    : SDK Core Interfaces
// Filename    : pluginterfaces/base/ibstream.h
// Created by  : Steinberg, 01/2004
// Description : Interface for reading/writing streams
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses. 
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#pragma once

#include "funknown.h"

namespace Steinberg {

//------------------------------------------------------------------------
/** Base class for streams.
\ingroup pluginBase
- read/write binary data from/to stream
- get/set stream read-write position (read and write position is the same)
*/
class IBStream: public FUnknown
{
public:
	enum IStreamSeekMode
	{
		kIBSeekSet = 0, ///< set absolute seek position
		kIBSeekCur,     ///< set seek position relative to current position
		kIBSeekEnd      ///< set seek position relative to stream end
	};

//------------------------------------------------------------------------
	/** Reads binary data from stream.
	\param buffer : destination buffer
	\param numBytes : amount of bytes to be read
	\param numBytesRead : result - how many bytes have been read from stream (set to 0 if this is of no interest) */
	virtual tresult PLUGIN_API read (void* buffer, int32 numBytes, int32* numBytesRead = nullptr) = 0;
	
	/** Writes binary data to stream.
	\param buffer : source buffer
	\param numBytes : amount of bytes to write
	\param numBytesWritten : result - how many bytes have been written to stream (set to 0 if this is of no interest) */
	virtual tresult PLUGIN_API write (void* buffer, int32 numBytes, int32* numBytesWritten = nullptr) = 0;
	
	/** Sets stream read-write position. 
	\param pos : new stream position (dependent on mode)
	\param mode : value of enum IStreamSeekMode
	\param result : new seek position (set to 0 if this is of no interest) */
	virtual tresult PLUGIN_API seek (int64 pos, int32 mode, int64* result = nullptr) = 0;
	
	/** Gets current stream read-write position. 
	\param pos : is assigned the current position if function succeeds */
	virtual tresult PLUGIN_API tell (int64* pos) = 0;
//------------------------------------------------------------------------
	static const FUID iid;
};

DECLARE_CLASS_IID (IBStream, 0xC3BF6EA2, 0x30994752, 0x9B6BF990, 0x1EE33E9B)

//------------------------------------------------------------------------
/** Stream with a size. 
\ingroup pluginBase
[extends IBStream] when stream type supports it (like file and memory stream)
*/
class ISizeableStream: public FUnknown
{
public:
//------------------------------------------------------------------------
	/** Return the stream size */
	virtual tresult PLUGIN_API getStreamSize (int64& size) = 0;
	/** Set the steam size. File streams can only be resized if they are write enabled. */
	virtual tresult PLUGIN_API setStreamSize (int64 size) = 0;

//------------------------------------------------------------------------
	static const FUID iid;
};
DECLARE_CLASS_IID (ISizeableStream, 0x04F9549E, 0xE02F4E6E, 0x87E86A87, 0x47F4E17F)

//------------------------------------------------------------------------
} // namespace Steinberg
//-----------------------------------------------------------------------------
// Project     : SDK Core
//
// Category    : SDK Core Interfaces
// Filename    : pluginterfaces/base/icloneable.h
// Created by  : Steinberg, 11/2007
// Description : Interface for object copies
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses. 
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#pragma once

#include "funknown.h"

namespace Steinberg {

//------------------------------------------------------------------------
/**  Interface allowing an object to be copied.
- [plug & host imp] 
- [released: N4.12]
*/
class ICloneable : public FUnknown
{
public:
//------------------------------------------------------------------------
	/** Create exact copy of the object */
	virtual FUnknown* PLUGIN_API clone () = 0;
//------------------------------------------------------------------------
	static const FUID iid;
};

DECLARE_CLASS_IID (ICloneable, 0xD45406B9, 0x3A2D4443, 0x9DAD9BA9, 0x85A1454B)

//------------------------------------------------------------------------
} // namespace Steinberg
//-----------------------------------------------------------------------------
// Project     : SDK Core
//
// Category    : SDK Core Interfaces
// Filename    : pluginterfaces/base/ierrorcontext.h
// Created by  : Steinberg, 02/2008
// Description : Error Context Interface
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses. 
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#pragma once

#include "pluginterfaces/base/funknown.h"

namespace Steinberg {

class IString;

//------------------------------------------------------------------------
/** Interface for error handling. 
- [plug imp]
- [released: Sequel 2]
*/
class IErrorContext : public FUnknown
{
public:
//------------------------------------------------------------------------
	/** Tells the plug-in to not show any UI elements on errors. */
	virtual void PLUGIN_API disableErrorUI (bool state) = 0;    
	/** If an error happens and disableErrorUI was not set this should return kResultTrue if the plug-in already showed a message to the user what happened. */
	virtual tresult PLUGIN_API errorMessageShown () = 0;
	/** Fill message with error string. The host may show this to the user. */
	virtual tresult PLUGIN_API getErrorMessage (IString* message) = 0;

//------------------------------------------------------------------------
	static const FUID iid;
};
DECLARE_CLASS_IID (IErrorContext, 0x12BCD07B, 0x7C694336, 0xB7DA77C3, 0x444A0CD0)

//------------------------------------------------------------------------
} // namespace Steinberg
//-----------------------------------------------------------------------------
// Project     : SDK Core
//
// Category    : SDK Core Interfaces
// Filename    : pluginterfaces/base/ipersistent.h
// Created by  : Steinberg, 09/2004
// Description : Plug-In Storage Interfaces
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses. 
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#pragma once

#include "pluginterfaces/base/funknown.h"

namespace Steinberg {

class FVariant;
class IAttributes;
//------------------------------------------------------------------------
/**  Persistent Object Interface. 
[plug imp] \n
This interface is used to store/restore attributes of an object.
An IPlugController can implement this interface to handle presets.
The gui-xml for a preset control looks like this:
\code
....
<view name="PresetView" data="Preset"/>
....
<template name="PresetView">
	<view name="preset control" size="0, 0, 100, 20"/>
	<switch name="store preset" size="125,0,80,20" style="push|immediate" title="Store"  />
	<switch name="remove preset" size="220,0,80,20" style="push|immediate" title="Delete"  />
</template>
\endcode
The tag data="Preset" tells the host to create a preset controller that handles the 
3 values named "preset control",  "store preset", and "remove preset".
*/
class IPersistent: public FUnknown
{
public:
//------------------------------------------------------------------------
	/** The class ID must be a 16 bytes unique id that is used to create the object. 
	This ID is also used to identify the preset list when used with presets. */
	virtual tresult PLUGIN_API getClassID (char8* uid) = 0;
	/** Store all members/data in the passed IAttributes. */
	virtual tresult PLUGIN_API saveAttributes (IAttributes* ) = 0;
	/** Restore all members/data from the passed IAttributes. */
	virtual tresult PLUGIN_API loadAttributes (IAttributes* ) = 0;
//------------------------------------------------------------------------
	static const FUID iid;
};

DECLARE_CLASS_IID (IPersistent, 0xBA1A4637, 0x3C9F46D0, 0xA65DBA0E, 0xB85DA829)


typedef FIDString IAttrID;
//------------------------------------------------------------------------
/**  Object Data Archive Interface. 
- [host imp]

- store data/objects/binary/subattributes in the archive
- read stored data from the archive

All data stored to the archive are identified by a string (IAttrID), which must be unique on each
IAttribute level.

The basic set/get methods make use of the FVariant class defined in 'funknown.h'.
For a more convenient usage of this interface, you should use the functions defined
in namespace PAttributes (public.sdk/source/common/pattributes.h+cpp) !! 

\ingroup frameworkHostClasses
*/
class IAttributes: public FUnknown
{
public:
//------------------------------------------------------------------------
	/*! \name Methods to write attributes
	******************************************************************************************************** */
	//@{
	/** Store any data in the archive. It is even possible to store sub-attributes by creating
	    a new IAttributes instance via the IHostClasses interface and pass it to the parent in the
		FVariant. In this case the archive must take the ownership of the newly created object, which
		is true for all objects that have been created only for storing. You tell the archive to take
		ownership by adding the FVariant::kOwner flag to the FVariant::type member (data.type |= FVariant::kOwner).
		When using the PAttributes functions, this is done through a function parameter.*/
	virtual tresult PLUGIN_API set (IAttrID attrID, const FVariant& data) = 0;

	/** Store a list of data in the archive. Please note that the type of data is not mixable! So
	    you can only store a list of integers or a list of doubles/strings/etc. You can also store a list
		of subattributes or other objects that implement the IPersistent interface.*/	
	virtual tresult PLUGIN_API queue (IAttrID listID, const FVariant& data) = 0;  

	/** Store binary data in the archive. Parameter 'copyBytes' specifies if the passed data should be copied.
	    The archive cannot take the ownership of binary data. Either it just references a buffer in order
		to write it to a file (copyBytes = false) or it copies the data to its own buffers (copyBytes = true).
		When binary data should be stored in the default pool for example, you must always copy it!*/	
	virtual tresult PLUGIN_API setBinaryData (IAttrID attrID, void* data, uint32 bytes, bool copyBytes) = 0;
	//@}

	/*! \name Methods to read attributes 
	******************************************************************************************************** */
	//@{
	/** Get data previously stored to the archive. */
	virtual tresult PLUGIN_API get (IAttrID attrID, FVariant& data) = 0;

	/** Get list of data previously stored to the archive. As long as there are queue members the method
	    will return kResultTrue. When the queue is empty, the methods returns kResultFalse. All lists except from
		object lists can be reset which means that the items can be read once again. \see IAttributes::resetQueue */	
	virtual tresult PLUGIN_API unqueue (IAttrID listID, FVariant& data) = 0; 

	/** Get the amount of items in a queue. */
	virtual int32 PLUGIN_API getQueueItemCount (IAttrID) = 0;
	
	/** Reset a queue. If you need to restart reading a queue, you have to reset it. You can reset a queue at any time.*/
	virtual tresult PLUGIN_API resetQueue (IAttrID attrID) = 0;
	
	/** Reset all queues in the archive.*/
	virtual tresult PLUGIN_API resetAllQueues () = 0;

	/** Read binary data from the archive. The data is copied into the passed buffer. The size of that buffer
	    must fit the size of data stored in the archive which can be queried via IAttributes::getBinaryDataSize  */	
	virtual tresult PLUGIN_API getBinaryData (IAttrID attrID, void* data, uint32 bytes) = 0;
	/** Get the size in bytes of binary data in the archive. */	
	virtual uint32 PLUGIN_API getBinaryDataSize (IAttrID attrID) = 0;
	//@}

//------------------------------------------------------------------------
	static const FUID iid;
};

DECLARE_CLASS_IID (IAttributes, 0xFA1E32F9, 0xCA6D46F5, 0xA982F956, 0xB1191B58)

//------------------------------------------------------------------------
/**  Extended access to Attributes; supports Attribute retrieval via iteration. 
- [host imp]
- [released] C7/N6

\ingroup frameworkHostClasses
*/
class IAttributes2 : public IAttributes
{
public:
	/** Returns the number of existing attributes. */
	virtual int32 PLUGIN_API countAttributes () const = 0;
	/** Returns the attribute's ID for the given index. */
	virtual IAttrID PLUGIN_API getAttributeID (int32 index) const = 0;
//------------------------------------------------------------------------
	static const FUID iid;
};

DECLARE_CLASS_IID (IAttributes2, 0x1382126A, 0xFECA4871, 0x97D52A45, 0xB042AE99)

//------------------------------------------------------------------------
} // namespace Steinberg
//-----------------------------------------------------------------------------
// Project     : SDK Core
//
// Category    : SDK Core Interfaces
// Filename    : pluginterfaces/base/ipluginbase.h
// Created by  : Steinberg, 01/2004
// Description : Basic Plug-in Interfaces
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses. 
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#pragma once

#include "funknown.h"
#include "fstrdefs.h"

namespace Steinberg {

//------------------------------------------------------------------------
/**  Basic interface to a plug-in component: IPluginBase
\ingroup pluginBase
- [plug imp]
- initialize/terminate the plug-in component

The host uses this interface to initialize and to terminate the plug-in component.
The context that is passed to the initialize method contains any interface to the
host that the plug-in will need to work. These interfaces can vary from category to category.
A list of supported host context interfaces should be included in the documentation
of a specific category. 
*/
class IPluginBase: public FUnknown
{
public:
//------------------------------------------------------------------------
	/** The host passes a number of interfaces as context to initialize the plug-in class.
		\param context, passed by the host, is mandatory and should implement IHostApplication
		@note Extensive memory allocations etc. should be performed in this method rather than in
	   the class' constructor! If the method does NOT return kResultOk, the object is released
	   immediately. In this case terminate is not called! */
	virtual tresult PLUGIN_API initialize (FUnknown* context) = 0;

	/** This function is called before the plug-in is unloaded and can be used for
	    cleanups. You have to release all references to any host application interfaces. */
	virtual tresult PLUGIN_API terminate () = 0;

//------------------------------------------------------------------------
	static const FUID iid;
};

DECLARE_CLASS_IID (IPluginBase, 0x22888DDB, 0x156E45AE, 0x8358B348, 0x08190625)


//------------------------------------------------------------------------
/** Basic Information about the class factory of the plug-in.
\ingroup pluginBase
*/
struct PFactoryInfo
{
//------------------------------------------------------------------------
	enum FactoryFlags
	{
		/** Nothing */
		kNoFlags = 0,

		/** The number of exported classes can change each time the Module is loaded. If this flag
		   is set, the host does not cache class information. This leads to a longer startup time
		   because the host always has to load the Module to get the current class information. */
		kClassesDiscardable = 1 << 0,

		/** This flag is deprecated, do not use anymore, resp. it will get ignored from
		   Cubase/Nuendo 12 and later. */
		kLicenseCheck = 1 << 1,

		/** Component will not be unloaded until process exit */
		kComponentNonDiscardable = 1 << 3,

		/** Components have entirely unicode encoded strings (True for VST 3 plug-ins so far). */
		kUnicode = 1 << 4
	};

	enum
	{
		kURLSize = 256,
		kEmailSize = 128,
		kNameSize = 64
	};

//------------------------------------------------------------------------
	char8 vendor[kNameSize];	///< e.g. "Steinberg Media Technologies"
	char8 url[kURLSize];		///< e.g. "http://www.steinberg.de"
	char8 email[kEmailSize];	///< e.g. "info@steinberg.de"
	int32 flags;				///< (see FactoryFlags above)
//------------------------------------------------------------------------
	SMTG_CONSTEXPR14 PFactoryInfo (const char8* _vendor, const char8* _url, const char8* _email,
	                               int32 _flags)
#if SMTG_CPP14
	: vendor (), url (), email (), flags ()
#endif
	{
		strncpy8 (vendor, _vendor, kNameSize);
		strncpy8 (url, _url, kURLSize);
		strncpy8 (email, _email, kEmailSize);
		flags = _flags;
#ifdef UNICODE
		flags |= kUnicode;
#endif
	}
#if SMTG_CPP11
	constexpr PFactoryInfo () : vendor (), url (), email (), flags () {}
#else
	PFactoryInfo () { memset (this, 0, sizeof (PFactoryInfo)); }
#endif
};

//------------------------------------------------------------------------
/**  Basic Information about a class provided by the plug-in.
\ingroup pluginBase
*/
struct PClassInfo
{
//------------------------------------------------------------------------
	enum ClassCardinality
	{
		kManyInstances = 0x7FFFFFFF
	};

	enum
	{
		kCategorySize = 32,
		kNameSize = 64
	};
//------------------------------------------------------------------------
	/** Class ID 16 Byte class GUID */
	TUID cid;

	/** Cardinality of the class, set to kManyInstances (see \ref PClassInfo::ClassCardinality) */
	int32 cardinality;

	/** Class category, host uses this to categorize interfaces */
	char8 category[kCategorySize];

	/** Class name, visible to the user */
	char8 name[kNameSize];

//------------------------------------------------------------------------

	SMTG_CONSTEXPR14 PClassInfo (const TUID _cid, int32 _cardinality, const char8* _category,
	                             const char8* _name)
#if SMTG_CPP14
	: cid (), cardinality (), category (), name ()
#endif
	{
#if SMTG_CPP14
		copyTUID (cid, _cid);
#else
		memset (this, 0, sizeof (PClassInfo));
		memcpy (cid, _cid, sizeof (TUID));
#endif
		if (_category)
			strncpy8 (category, _category, kCategorySize);
		if (_name)
			strncpy8 (name, _name, kNameSize);
		cardinality = _cardinality;
	}
#if SMTG_CPP11
	constexpr PClassInfo () : cid (), cardinality (), category (), name () {}
#else
	PClassInfo () { memset (this, 0, sizeof (PClassInfo)); }
#endif
};

//------------------------------------------------------------------------
//  IPluginFactory interface declaration
//------------------------------------------------------------------------
/**	Class factory that any plug-in defines for creating class instances: IPluginFactory
\ingroup pluginBase
- [plug imp]

From the host's point of view a plug-in module is a factory which can create
a certain kind of object(s). The interface IPluginFactory provides methods
to get information about the classes exported by the plug-in and a
mechanism to create instances of these classes (that usually define the IPluginBase interface).

<b> An implementation is provided in public.sdk/source/common/pluginfactory.cpp </b>
\see GetPluginFactory
*/
class IPluginFactory : public FUnknown
{
public:
//------------------------------------------------------------------------
	/** Fill a PFactoryInfo structure with information about the plug-in vendor. */
	virtual tresult PLUGIN_API getFactoryInfo (PFactoryInfo* info) = 0;

	/** Returns the number of exported classes by this factory. If you are using the CPluginFactory
	 * implementation provided by the SDK, it returns the number of classes you registered with
	 * CPluginFactory::registerClass. */
	virtual int32 PLUGIN_API countClasses () = 0;

	/** Fill a PClassInfo structure with information about the class at the specified index. */
	virtual tresult PLUGIN_API getClassInfo (int32 index, PClassInfo* info) = 0;

	/** Create a new class instance. */
	virtual tresult PLUGIN_API createInstance (FIDString cid, FIDString _iid, void** obj) = 0;

//------------------------------------------------------------------------
	static const FUID iid;
};

DECLARE_CLASS_IID (IPluginFactory, 0x7A4D811C, 0x52114A1F, 0xAED9D2EE, 0x0B43BF9F)


//------------------------------------------------------------------------
/**  Version 2 of Basic Information about a class provided by the plug-in.
\ingroup pluginBase
*/
struct PClassInfo2
{
//------------------------------------------------------------------------
	/** Class ID 16 Byte class GUID */
	TUID cid;

	/** Cardinality of the class, set to kManyInstances (see \ref PClassInfo::ClassCardinality) */
	int32 cardinality;

	/** Class category, host uses this to categorize interfaces */
	char8 category[PClassInfo::kCategorySize];

	/** Class name, visible to the user */
	char8 name[PClassInfo::kNameSize];

	enum {
		kVendorSize = 64,
		kVersionSize = 64,
		kSubCategoriesSize = 128
	};

	/** flags used for a specific category, must be defined where category is defined */
	uint32 classFlags;

	/** module specific subcategories, can be more than one, logically added by the OR operator */
	char8 subCategories[kSubCategoriesSize];
	
	/** overwrite vendor information from factory info */
	char8 vendor[kVendorSize];

	/** Version string (e.g. "1.0.0.512" with Major.Minor.Subversion.Build) */
	char8 version[kVersionSize];

	/** SDK version used to build this class (e.g. "VST 3.0") */
	char8 sdkVersion[kVersionSize];
//------------------------------------------------------------------------

	SMTG_CONSTEXPR14 PClassInfo2 (const TUID _cid, int32 _cardinality, const char8* _category,
	                              const char8* _name, int32 _classFlags,
	                              const char8* _subCategories, const char8* _vendor,
	                              const char8* _version, const char8* _sdkVersion)
#if SMTG_CPP14
	: cid ()
	, cardinality ()
	, category ()
	, name ()
	, classFlags ()
	, subCategories ()
	, vendor ()
	, version ()
	, sdkVersion ()
#endif
	{
#if SMTG_CPP14
		copyTUID (cid, _cid);
#else
		memset (this, 0, sizeof (PClassInfo2));
		memcpy (cid, _cid, sizeof (TUID));
#endif
		cardinality = _cardinality;
		if (_category)
			strncpy8 (category, _category, PClassInfo::kCategorySize);
		if (_name)
			strncpy8 (name, _name, PClassInfo::kNameSize);
		classFlags = static_cast<uint32> (_classFlags);
		if (_subCategories)
			strncpy8 (subCategories, _subCategories, kSubCategoriesSize);
		if (_vendor)
			strncpy8 (vendor, _vendor, kVendorSize);
		if (_version)
			strncpy8 (version, _version, kVersionSize);
		if (_sdkVersion)
			strncpy8 (sdkVersion, _sdkVersion, kVersionSize);
	}
#if SMTG_CPP11
	constexpr PClassInfo2 ()
	: cid ()
	, cardinality ()
	, category ()
	, name ()
	, classFlags ()
	, subCategories ()
	, vendor ()
	, version ()
	, sdkVersion ()
	{
	}
#else
	PClassInfo2 () { memset (this, 0, sizeof (PClassInfo2)); }
#endif
};

//------------------------------------------------------------------------
//  IPluginFactory2 interface declaration
//------------------------------------------------------------------------
/**	Version 2 of class factory supporting PClassInfo2: IPluginFactory2
\ingroup pluginBase
\copydoc IPluginFactory
*/
class IPluginFactory2 : public IPluginFactory
{
public:
//------------------------------------------------------------------------
	/** Returns the class info (version 2) for a given index. */
	virtual tresult PLUGIN_API getClassInfo2 (int32 index, PClassInfo2* info) = 0;

//------------------------------------------------------------------------
	static const FUID iid;
};
DECLARE_CLASS_IID (IPluginFactory2, 0x0007B650, 0xF24B4C0B, 0xA464EDB9, 0xF00B2ABB)


//------------------------------------------------------------------------
/** Unicode Version of Basic Information about a class provided by the plug-in
*/
struct PClassInfoW
{
//------------------------------------------------------------------------
	TUID cid;							///< see \ref PClassInfo
	int32 cardinality;					///< see \ref PClassInfo
	char8 category[PClassInfo::kCategorySize];	///< see \ref PClassInfo
	char16 name[PClassInfo::kNameSize];	///< see \ref PClassInfo

	enum {
		kVendorSize = 64,
		kVersionSize = 64,
		kSubCategoriesSize = 128
	};

	/** flags used for a specific category, must be defined where category is defined */
	uint32 classFlags;

	/** module specific subcategories, can be more than one, logically added by the OR operator */
	char8 subCategories[kSubCategoriesSize];
	
	/** overwrite vendor information from factory info */
	char16 vendor[kVendorSize];
	
	/** Version string (e.g. "1.0.0.512" with Major.Minor.Subversion.Build) */
	char16 version[kVersionSize];
	
	/** SDK version used to build this class (e.g. "VST 3.0") */
	char16 sdkVersion[kVersionSize];

//------------------------------------------------------------------------
	SMTG_CONSTEXPR14 PClassInfoW (const TUID _cid, int32 _cardinality, const char8* _category,
	                              const char16* _name, int32 _classFlags,
	                              const char8* _subCategories, const char16* _vendor,
	                              const char16* _version, const char16* _sdkVersion)
#if SMTG_CPP14
	: cid ()
	, cardinality ()
	, category ()
	, name ()
	, classFlags ()
	, subCategories ()
	, vendor ()
	, version ()
	, sdkVersion ()
#endif
	{
#if SMTG_CPP14
		copyTUID (cid, _cid);
#else
		memset (this, 0, sizeof (PClassInfoW));
		memcpy (cid, _cid, sizeof (TUID));
#endif
		cardinality = _cardinality;
		if (_category)
			strncpy8 (category, _category, PClassInfo::kCategorySize);
		if (_name)
			strncpy16 (name, _name, PClassInfo::kNameSize);
		classFlags = static_cast<uint32> (_classFlags);
		if (_subCategories)
			strncpy8 (subCategories, _subCategories, kSubCategoriesSize);
		if (_vendor)
			strncpy16 (vendor, _vendor, kVendorSize);
		if (_version)
			strncpy16 (version, _version, kVersionSize);
		if (_sdkVersion)
			strncpy16 (sdkVersion, _sdkVersion, kVersionSize);
	}
#if SMTG_CPP11
	constexpr PClassInfoW ()
	: cid ()
	, cardinality ()
	, category ()
	, name ()
	, classFlags ()
	, subCategories ()
	, vendor ()
	, version ()
	, sdkVersion ()
	{
	}
#else
	PClassInfoW () { memset (this, 0, sizeof (PClassInfoW)); }
#endif

	SMTG_CONSTEXPR14 void fromAscii (const PClassInfo2& ci2)
	{
#if SMTG_CPP14
		copyTUID (cid, ci2.cid);
#else
		memcpy (cid, ci2.cid, sizeof (TUID));
#endif
		cardinality = ci2.cardinality;
		strncpy8 (category, ci2.category, PClassInfo::kCategorySize);
		str8ToStr16 (name, ci2.name, PClassInfo::kNameSize);
		classFlags = ci2.classFlags;
		strncpy8 (subCategories, ci2.subCategories, kSubCategoriesSize);

		str8ToStr16 (vendor, ci2.vendor, kVendorSize);
		str8ToStr16 (version, ci2.version, kVersionSize);
		str8ToStr16 (sdkVersion, ci2.sdkVersion, kVersionSize);
	}
};

//------------------------------------------------------------------------
//  IPluginFactory3 interface declaration
//------------------------------------------------------------------------
/**	Version 3 of class factory supporting PClassInfoW: IPluginFactory3
\ingroup pluginBase
\copydoc IPluginFactory
*/
class IPluginFactory3 : public IPluginFactory2
{
public:
//------------------------------------------------------------------------
	/** Returns the unicode class info for a given index. */
	virtual tresult PLUGIN_API getClassInfoUnicode (int32 index, PClassInfoW* info) = 0;

	/** Receives information about host*/
	virtual tresult PLUGIN_API setHostContext (FUnknown* context) = 0;

//------------------------------------------------------------------------
	static const FUID iid;
};
DECLARE_CLASS_IID (IPluginFactory3, 0x4555A2AB, 0xC1234E57, 0x9B122910, 0x36878931)
//------------------------------------------------------------------------
} // namespace Steinberg

//------------------------------------------------------------------------
#define LICENCE_UID(l1, l2, l3, l4) \
{ \
	(int8)((l1 & 0xFF000000) >> 24), (int8)((l1 & 0x00FF0000) >> 16), \
	(int8)((l1 & 0x0000FF00) >>  8), (int8)((l1 & 0x000000FF)      ), \
	(int8)((l2 & 0xFF000000) >> 24), (int8)((l2 & 0x00FF0000) >> 16), \
	(int8)((l2 & 0x0000FF00) >>  8), (int8)((l2 & 0x000000FF)      ), \
	(int8)((l3 & 0xFF000000) >> 24), (int8)((l3 & 0x00FF0000) >> 16), \
	(int8)((l3 & 0x0000FF00) >>  8), (int8)((l3 & 0x000000FF)      ), \
	(int8)((l4 & 0xFF000000) >> 24), (int8)((l4 & 0x00FF0000) >> 16), \
	(int8)((l4 & 0x0000FF00) >>  8), (int8)((l4 & 0x000000FF)      )  \
}

//------------------------------------------------------------------------
// GetPluginFactory
//------------------------------------------------------------------------
/**  Plug-in entry point.
\ingroup pluginBase
Any plug-in must define and export this function. \n
A typical implementation of GetPluginFactory looks like this
\code{.cpp}
SMTG_EXPORT_SYMBOL IPluginFactory* PLUGIN_API GetPluginFactory ()
{
	if (!gPluginFactory)
	{
		static PFactoryInfo factoryInfo =
		{
			"My Company Name",
			"http://www.mywebpage.com",
			"mailto:myemail@address.com",
			PFactoryInfo::kNoFlags
		};

		gPluginFactory = new CPluginFactory (factoryInfo);

		static PClassInfo componentClass =
		{
			INLINE_UID (0x00000000, 0x00000000, 0x00000000, 0x00000000), // replace by a valid uid
			1,
			"Service",    // category
			"Name"
		};

		gPluginFactory->registerClass (&componentClass, MyComponentClass::newInstance);
	}
	else
		gPluginFactory->addRef ();

	return gPluginFactory;
}
\endcode
\see \ref loadPlugin
*/
extern "C"
{
	SMTG_EXPORT_SYMBOL Steinberg::IPluginFactory* PLUGIN_API GetPluginFactory ();
	typedef Steinberg::IPluginFactory* (PLUGIN_API *GetFactoryProc) ();
}
//-----------------------------------------------------------------------------
// Project     : SDK Core
//
// Category    : SDK Core Interfaces
// Filename    : pluginterfaces/base/iplugincompatibility.h
// Created by  : Steinberg, 02/2022
// Description : Basic Plug-in Interfaces
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#pragma once

#include "ibstream.h"

//------------------------------------------------------------------------
namespace Steinberg {

//------------------------------------------------------------------------
/** moduleinfo.json

The moduleinfo.json describes the contents of the plug-in in a JSON5 compatible format (See https://json5.org/).
It contains the factory info (see PFactoryInfo), the contained classes (see PClassInfo), the
included snapshots and a list of compatibility of the included classes.

An example moduleinfo.json:

\code
{
  "Name": "",
  "Version": "1.0",
  "Factory Info": {
    "Vendor": "Steinberg Media Technologies",
    "URL": "http://www.steinberg.net",
    "E-Mail": "mailto:info@steinberg.de",
    "Flags": {
      "Unicode": true,
      "Classes Discardable": false,
      "Component Non Discardable": false,
    },
  },
  "Compatibility": [
    {
		"New": "B9F9ADE1CD9C4B6DA57E61E3123535FD",
		"Old": [
		  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", // just an example
		  "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", // another example
		],
	},
  ],
  "Classes": [
    {
      "CID": "B9F9ADE1CD9C4B6DA57E61E3123535FD",
      "Category": "Audio Module Class",
      "Name": "AGainSimple VST3",
      "Vendor": "Steinberg Media Technologies",
      "Version": "1.3.0.1",
      "SDKVersion": "VST 3.7.4",
      "Sub Categories": [
        "Fx",
      ],
      "Class Flags": 0,
      "Cardinality": 2147483647,
      "Snapshots": [
      ],
    },
  ],
}
\endcode

*/

#define kPluginCompatibilityClass "Plugin Compatibility Class"

//------------------------------------------------------------------------
/** optional interface to query the compatibility of the plug-ins classes
- [plug imp]

A plug-in can add a class with this interface to its class factory if it cannot provide a
moduleinfo.json file in its plug-in package/bundle where the compatibility is normally part of.

If the module contains a moduleinfo.json the host will ignore this class.

The class must write into the stream an UTF-8 encoded json description of the compatibility of
the other classes in the factory.

It is expected that the JSON5 written starts with an array:
\code
[
    {
		"New": "B9F9ADE1CD9C4B6DA57E61E3123535FD",
		"Old": [
		  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", // just an example
		  "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", // another example
		],
	},
]
\endcode
*/
class IPluginCompatibility : public FUnknown
{
public:
	/** get the compatibility stream
	 *	@param stream	the stream the plug-in must write the UTF8 encoded JSON5 compatibility
	 *					string.
	 *	@return kResultTrue on success
	 */
	virtual tresult PLUGIN_API getCompatibilityJSON (IBStream* stream) = 0;

//------------------------------------------------------------------------
	static const FUID iid;
};

DECLARE_CLASS_IID (IPluginCompatibility, 0x4AFD4B6A, 0x35D7C240, 0xA5C31414, 0xFB7D15E6)

//------------------------------------------------------------------------
} // Steinberg
//-----------------------------------------------------------------------------
// Project     : SDK Core
//
// Category    : SDK Core Interfaces
// Filename    : pluginterfaces/base/istringresult.h
// Created by  : Steinberg, 01/2005
// Description : Strings Interface
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses. 
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#pragma once

#include "pluginterfaces/base/funknown.h"

namespace Steinberg {

//------------------------------------------------------------------------
/** Interface to return an ascii string of variable size. 
    In order to manage memory allocation and deallocation properly, 
	this interface is used to transfer a string as result parameter of
	a method requires a string of unknown size. 
- [host imp] or [plug imp]
- [released: SX 4]
*/
class IStringResult : public FUnknown
{
public:
//------------------------------------------------------------------------
	virtual void PLUGIN_API setText (const char8* text) = 0;    

//------------------------------------------------------------------------
	static const FUID iid;
};

DECLARE_CLASS_IID (IStringResult, 0x550798BC, 0x872049DB, 0x84920A15, 0x3B50B7A8)


//------------------------------------------------------------------------
/** Interface to a string of variable size and encoding. 
- [host imp] or [plug imp]
- [released: ] 
*/
class IString : public FUnknown
{
public:
//------------------------------------------------------------------------
	/** Assign ASCII string */
	virtual void PLUGIN_API setText8 (const char8* text) = 0;    
	/** Assign unicode string */
	virtual void PLUGIN_API setText16 (const char16* text) = 0;

	/** Return ASCII string. If the string is unicode so far, it will be converted.
	    So you need to be careful, because the conversion can result in data loss. 
		It is save though to call getText8 if isWideString() returns false */
	virtual const char8* PLUGIN_API getText8 () = 0;   
	/** Return unicode string. If the string is ASCII so far, it will be converted. */
	virtual const char16* PLUGIN_API getText16 () = 0;    

	/** !Do not use this method! Early implementations take the given pointer as 
	     internal string and this will cause problems because 'free' will be used to delete the passed memory.
		 Later implementations will redirect 'take' to setText8 and setText16 */
	virtual void PLUGIN_API take (void* s, bool isWide) = 0;

	/** Returns true if the string is in unicode format, returns false if the string is ASCII */
	virtual bool PLUGIN_API isWideString () const = 0;

//------------------------------------------------------------------------
	static const FUID iid;
};

DECLARE_CLASS_IID (IString, 0xF99DB7A3, 0x0FC14821, 0x800B0CF9, 0x8E348EDF)

//------------------------------------------------------------------------
} // namespace Steinberg
//------------------------------------------------------------------------
// Project     : Steinberg Module Architecture SDK
//
// Category    : Basic Host Service Interfaces
// Filename    : pluginterfaces/base/iupdatehandler.h
// Created by  : Steinberg, 01/2004
// Description : Update handling
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses. 
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#pragma once

#include "pluginterfaces/base/funknown.h"

namespace Steinberg {

class IDependent;

//------------------------------------------------------------------------
/** Host implements dependency handling for plugins.
- [host imp]
- [get this interface from IHostClasses]
- [released N3.1]

- Install/Remove change notifications
- Trigger updates when an object has changed

Can be used between host-objects and the Plug-In or 
inside the Plug-In to handle internal updates!

\see IDependent
\ingroup frameworkHostClasses
*/
class IUpdateHandler: public FUnknown
{
public:
//------------------------------------------------------------------------
	/** Install update notification for given object. It is essential to
	    remove all dependencies again using 'removeDependent'! Dependencies
		are not removed automatically when the 'object' is released! 
	\param object : interface to object that sends change notifications 
	\param dependent : interface through which the update is passed */
	virtual tresult PLUGIN_API addDependent (FUnknown* object, IDependent* dependent) = 0;
	
	/** Remove a previously installed dependency.*/
	virtual tresult PLUGIN_API removeDependent (FUnknown* object, IDependent* dependent) = 0;

	/** Inform all dependents, that object has changed. 
	\param object is the object that has changed
	\param message is a value of enum IDependent::ChangeMessage, usually  IDependent::kChanged - can be
	                 a private message as well (only known to sender and dependent)*/
	virtual	tresult PLUGIN_API triggerUpdates (FUnknown* object, int32 message) = 0;

	/** Same as triggerUpdates, but delivered in idle (usefull to collect updates).*/
	virtual	tresult PLUGIN_API deferUpdates (FUnknown* object, int32 message) = 0;
	static const FUID iid;
};

DECLARE_CLASS_IID (IUpdateHandler, 0xF5246D56, 0x86544d60, 0xB026AFB5, 0x7B697B37)

//------------------------------------------------------------------------
/**  A dependent will get notified about changes of a model.
[plug imp]
- notify changes of a model

\see IUpdateHandler
\ingroup frameworkHostClasses
*/
class IDependent: public FUnknown
{
public:
//------------------------------------------------------------------------
	/** Inform the dependent, that the passed FUnknown has changed. */
	virtual void PLUGIN_API update (FUnknown* changedUnknown, int32 message) = 0;

	enum ChangeMessage 
	{
		kWillChange,
		kChanged,
		kDestroyed,
		kWillDestroy,

		kStdChangeMessageLast = kWillDestroy
	};
	//------------------------------------------------------------------------
	static const FUID iid;
};

DECLARE_CLASS_IID (IDependent, 0xF52B7AAE, 0xDE72416d, 0x8AF18ACE, 0x9DD7BD5E)

//------------------------------------------------------------------------
} // namespace Steinberg
//-----------------------------------------------------------------------------
// Project     : SDK Core
//
// Category    : SDK Core Interfaces
// Filename    : pluginterfaces/base/keycodes.h
// Created by  : Steinberg, 01/2004
// Description : Key Code Definitions
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses. 
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#pragma once

#include "pluginterfaces/base/ftypes.h"

namespace Steinberg {
//------------------------------------------------------------------------------
/** Virtual Key Codes.
OS-independent enumeration of virtual keycodes.
*/
enum VirtualKeyCodes
{
	KEY_BACK = 1,
	KEY_TAB,
	KEY_CLEAR,
	KEY_RETURN,
	KEY_PAUSE,
	KEY_ESCAPE,
	KEY_SPACE,
	KEY_NEXT,
	KEY_END,
	KEY_HOME,

	KEY_LEFT,
	KEY_UP,
	KEY_RIGHT,
	KEY_DOWN,
	KEY_PAGEUP,
	KEY_PAGEDOWN,

	KEY_SELECT,
	KEY_PRINT,
	KEY_ENTER,
	KEY_SNAPSHOT,
	KEY_INSERT,
	KEY_DELETE,
	KEY_HELP,
	KEY_NUMPAD0,
	KEY_NUMPAD1,
	KEY_NUMPAD2,
	KEY_NUMPAD3,
	KEY_NUMPAD4,
	KEY_NUMPAD5,
	KEY_NUMPAD6,
	KEY_NUMPAD7,
	KEY_NUMPAD8,
	KEY_NUMPAD9,
	KEY_MULTIPLY,
	KEY_ADD,
	KEY_SEPARATOR,
	KEY_SUBTRACT,
	KEY_DECIMAL,
	KEY_DIVIDE,
	KEY_F1,
	KEY_F2,
	KEY_F3,
	KEY_F4,
	KEY_F5,
	KEY_F6,
	KEY_F7,
	KEY_F8,
	KEY_F9,
	KEY_F10,
	KEY_F11,
	KEY_F12,
	KEY_NUMLOCK,
	KEY_SCROLL,

	KEY_SHIFT,
	KEY_CONTROL,
	KEY_ALT,

	KEY_EQUALS,				// only occurs on a Mac
	KEY_CONTEXTMENU,		// Windows only

	// multimedia keys
	KEY_MEDIA_PLAY,
	KEY_MEDIA_STOP,
	KEY_MEDIA_PREV,
	KEY_MEDIA_NEXT,
	KEY_VOLUME_UP,
	KEY_VOLUME_DOWN,

	KEY_F13,
	KEY_F14,
	KEY_F15,
	KEY_F16,
	KEY_F17,
	KEY_F18,
	KEY_F19,
	KEY_F20,
	KEY_F21,
	KEY_F22,
	KEY_F23,
	KEY_F24,

	KEY_SUPER,	// Win-Key on Windows, Ctrl-Key on macOS

	VKEY_FIRST_CODE = KEY_BACK,
	VKEY_LAST_CODE = KEY_SUPER,

	VKEY_FIRST_ASCII = 128
	/*
	 KEY_0 - KEY_9 are the same as ASCII '0' - '9' (0x30 - 0x39) + FIRST_ASCII
	 KEY_A - KEY_Z are the same as ASCII 'A' - 'Z' (0x41 - 0x5A) + FIRST_ASCII
	*/
};

//------------------------------------------------------------------------------
inline SMTG_CONSTEXPR14 tchar VirtualKeyCodeToChar (uint8 vKey)
{
	if (vKey >= VKEY_FIRST_ASCII)
		return (tchar)(vKey - VKEY_FIRST_ASCII + 0x30);
	else if (vKey == KEY_SPACE)
		return ' ';
	return 0;
}

//------------------------------------------------------------------------------
inline SMTG_CONSTEXPR14 uint8 CharToVirtualKeyCode (tchar character)
{
	if ((character >= 0x30 && character <= 0x39) || (character >= 0x41 && character <= 0x5A))
		return (uint8)(character - 0x30 + VKEY_FIRST_ASCII);
	if (character == ' ')
		return (uint8)KEY_SPACE;
	return 0;
}

/** OS-independent enumeration of virtual modifier-codes. */
enum KeyModifier
{
	kShiftKey     = 1 << 0, ///< same on Windows and macOS
	kAlternateKey = 1 << 1, ///< same on Windows and macOS
	kCommandKey   = 1 << 2, ///< Windows: ctrl key; macOS: cmd key
	kControlKey   = 1 << 3  ///< Windows: win key, macOS: ctrl key
};

/** Simple data-struct representing a key-stroke on the keyboard. */
struct KeyCode
{
	/** The associated character. */
	tchar character {0};
	/** The associated virtual key-code. */
	uint8 virt {0};
	/** The associated virtual modifier-code. */
	uint8 modifier {0};

	/** Constructs a new KeyCode. */
	SMTG_CONSTEXPR14 KeyCode (tchar character = 0, uint8 virt = 0, uint8 modifier = 0)
	: character (character), virt (virt), modifier (modifier)
	{
	}

	SMTG_CONSTEXPR14 KeyCode& operator= (const KeyCode& other) SMTG_NOEXCEPT
	{
		character = other.character;
		virt = other.virt;
		modifier = other.modifier;

		return *this;
	}
};

/** Utility functions to handle key-codes. 
* @see Steinberg::KeyCode 
*/
namespace KeyCodes {}

namespace KeyCodes {

/** Is only a modifier pressed on the keyboard? */
template <typename Key>
bool isModifierOnlyKey (const Key& key)
{
	return (key.character == 0 && (key.virt == KEY_SHIFT || key.virt == KEY_ALT ||
	                               key.virt == KEY_CONTROL || key.virt == KEY_SUPER));
}

//------------------------------------------------------------------------
} // namespace KeyCodes
} // namespace Steinberg
//------------------------------------------------------------------------
// Project     : SDK Base
//
// Category    : SDK Core Interfaces
// Filename    : pluginterfaces/base/pluginbasefwd.h
// Created by  : Steinberg, 10/2014
// Description : Forward declarations for pluginterfaces base module
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses. 
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#pragma once

namespace Steinberg {

class FUnknown;
class FUID;

template <typename T> class IPtr;

class ICloneable;
class IDependent;
class IUpdateHandler;

class IBStream;

struct KeyCode;

} // Steinberg
//-----------------------------------------------------------------------------
// Project     : SDK Core
//
// Category    : SDK Core Interfaces
// Filename    : pluginterfaces/base/smartpointer.h
// Created by  : Steinberg, 01/2004
// Description : Basic Interface
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses. 
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#pragma once

#include "pluginterfaces/base/fplatform.h"
#if SMTG_CPP11_STDLIBSUPPORT
#include <utility>
#endif

//------------------------------------------------------------------------
namespace Steinberg {

//------------------------------------------------------------------------
// IPtr
//------------------------------------------------------------------------
/**	IPtr - Smart pointer template class.
 \ingroup pluginBase

 - can be used as an I* pointer
 - handles refCount of the interface
 - Usage example:
 \code
IPtr<IPath> path (sharedPath);
if (path)
	path->ascend ();
 \endcode
 */
template <class I>
class IPtr
{
public:
//------------------------------------------------------------------------
	inline IPtr (I* ptr, bool addRef = true);
	inline IPtr (const IPtr&);

	template <class T>
	inline IPtr (const IPtr<T>& other) : ptr (other.get ())
	{
		if (ptr)
			ptr->addRef ();
	}

	inline IPtr ();
	inline ~IPtr ();

	inline I* operator= (I* ptr);

	inline IPtr& operator= (const IPtr& other);

	template <class T>
	inline IPtr& operator= (const IPtr<T>& other)
	{
		operator= (other.get ());
		return *this;
	}

	inline operator I* () const { return ptr; } // act as I*
	inline I* operator-> () const { return ptr; } // act as I*

	inline I* get () const { return ptr; }

#if SMTG_CPP11_STDLIBSUPPORT
	inline IPtr (IPtr<I>&& movePtr) SMTG_NOEXCEPT : ptr (movePtr.take ()) { }
	
	template <typename T>
	inline IPtr (IPtr<T>&& movePtr) SMTG_NOEXCEPT : ptr (movePtr.take ()) {  }

	inline IPtr& operator= (IPtr<I>&& movePtr) SMTG_NOEXCEPT
	{
		if (ptr)
			ptr->release ();

		ptr = movePtr.take ();
		return *this;
	}
	
	template <typename T>
	inline IPtr& operator= (IPtr<T>&& movePtr) 
	{
		if (ptr)
			ptr->release ();
		
		ptr = movePtr.take ();
		return *this;
	}
#endif

	inline void reset (I* obj = nullptr) 
	{
		if (ptr)
			ptr->release();
		ptr = obj;
	}

	I* take () SMTG_NOEXCEPT 
	{
		I* out = ptr; 
		ptr = nullptr; 
		return out;
	}

	template <typename T>
	static IPtr<T> adopt (T* obj) SMTG_NOEXCEPT { return IPtr<T> (obj, false); }

//------------------------------------------------------------------------
protected:
	I* ptr;
};

//------------------------------------------------------------------------
template <class I>
inline IPtr<I>::IPtr (I* _ptr, bool addRef) : ptr (_ptr)
{
	if (ptr && addRef)
		ptr->addRef ();
}

//------------------------------------------------------------------------
template <class I>
inline IPtr<I>::IPtr (const IPtr<I>& other) : ptr (other.ptr)
{
	if (ptr)
		ptr->addRef ();
}

//------------------------------------------------------------------------
template <class I>
inline IPtr<I>::IPtr () : ptr (0)
{
}

//------------------------------------------------------------------------
template <class I>
inline IPtr<I>::~IPtr ()
{
	if (ptr) 
	{
		ptr->release ();
		ptr = nullptr;  //TODO_CORE: how much does this cost? is this something hiding for us?
	}
}

//------------------------------------------------------------------------
template <class I>
inline I* IPtr<I>::operator= (I* _ptr)
{
	if (_ptr != ptr)
	{
		if (ptr)
			ptr->release ();
		ptr = _ptr;
		if (ptr)
			ptr->addRef ();
	}
	return ptr;
}

//------------------------------------------------------------------------
template <class I>
inline IPtr<I>& IPtr<I>::operator= (const IPtr<I>& _ptr)
{
	operator= (_ptr.ptr);
	return *this;
}

//------------------------------------------------------------------------
/** OPtr - "owning" smart pointer used for newly created FObjects.
 \ingroup pluginBase

 FUnknown implementations are supposed to have a refCount of 1 right after creation.
 So using an IPtr on newly created objects would lead to a leak.
 Instead the OPtr can be used in this case. \n
 Example:
 \code
 OPtr<IPath> path = FHostCreate (IPath, hostClasses);
 // no release is needed...
 \endcode
 The assignment operator takes ownership of a new object and releases the old.
 So its safe to write:
 \code
 OPtr<IPath> path = FHostCreate (IPath, hostClasses);
 path = FHostCreate (IPath, hostClasses);
 path = 0;
 \endcode
 This is the difference to using an IPtr with addRef=false.
 \code
 // DONT DO THIS:
 IPtr<IPath> path (FHostCreate (IPath, hostClasses), false);
 path = FHostCreate (IPath, hostClasses);
 path = 0;
 \endcode
 This will lead to a leak!
 */
template <class I>
class OPtr : public IPtr<I>
{
public:
//------------------------------------------------------------------------
	inline OPtr (I* p) : IPtr<I> (p, false) {}
	inline OPtr (const IPtr<I>& p) : IPtr<I> (p) {}
	inline OPtr (const OPtr<I>& p) : IPtr<I> (p) {}
	inline OPtr () {}
	inline I* operator= (I* _ptr)
	{
		if (_ptr != this->ptr)
		{
			if (this->ptr)
				this->ptr->release ();
			this->ptr = _ptr;
		}
		return this->ptr;
	}
};

//------------------------------------------------------------------------
/** Assigning newly created object to an IPtr.
 Example:
 \code
 IPtr<IPath> path = owned (FHostCreate (IPath, hostClasses));
 \endcode
 which is a slightly shorter form of writing:
 \code
 IPtr<IPath> path = OPtr<IPath> (FHostCreate (IPath, hostClasses));
 \endcode
 */
template <class I>
IPtr<I> owned (I* p)
{
	return IPtr<I> (p, false);
}

/** Assigning shared object to an IPtr.
 Example:
 \code
 IPtr<IPath> path = shared (iface.getXY ());
 \endcode
 */
template <class I>
IPtr<I> shared (I* p)
{
	return IPtr<I> (p, true);
}

#if SMTG_CPP11_STDLIBSUPPORT
//------------------------------------------------------------------------
// Ownership functionality
//------------------------------------------------------------------------
namespace SKI {
namespace Detail {
struct Adopt;
} // Detail

/** Strong typedef for shared reference counted objects. 
  *	Use SKI::adopt to unwrap the provided object.
  * @tparam T Referenced counted type.
  */
template <typename T>
class Shared
{
	friend struct Detail::Adopt;
	T* obj = nullptr;
};

/** Strong typedef for transferring the ownership of reference counted objects. 
  *	Use SKI::adopt to unwrap the provided object. 
  * After calling adopt the reference in this object is null.
  * @tparam T Referenced counted type.
  */
template <typename T>
class Owned
{
	friend struct Detail::Adopt;
	T* obj = nullptr;
};

/** Strong typedef for using reference counted objects. 
  *	Use SKI::adopt to unwrap the provided object. 
  * After calling adopt the reference in this object is null.
  * @tparam T Referenced counted type.
  */
template <typename T>
class Used
{
	friend struct Detail::Adopt;
	T* obj = nullptr;
};
	
namespace Detail {

struct Adopt 
{
	template <typename T>
	static IPtr<T> adopt (Shared<T>& ref) 
	{ 
		using Steinberg::shared;
		return shared (ref.obj); 
	}

	template <typename T>
	static IPtr<T> adopt (Owned<T>& ref) 
	{ 
		using Steinberg::owned;
		IPtr<T> out = owned (ref.obj);
		ref.obj = nullptr;
		return out;
	}

	template <typename T>
	static T* adopt (Used<T>& ref)
	{
		return ref.obj;
	}

	template <template <typename> class OwnerType, typename T>
	static OwnerType<T> toOwnerType (T* obj) 
	{ 
		OwnerType<T> out;
		out.obj = obj;
		return out; 
	}
};

} // Detail

/** Common function to adopt referenced counted object. 
  *	@tparam T			Referenced counted type.
  * @param ref			The reference to be adopted in a smart pointer.
  */
template <typename T>
IPtr<T> adopt (Shared<T>& ref) { return Detail::Adopt::adopt (ref); }

template <typename T>
IPtr<T> adopt (Shared<T>&& ref) { return Detail::Adopt::adopt (ref); }

/** Common function to adopt referenced counted object. 
  *	@tparam T			Referenced counted type.
  * @param ref			The reference to be adopted in a smart pointer.
  */
template <typename T>
IPtr<T> adopt (Owned<T>& ref) { return Detail::Adopt::adopt (ref); }

template <typename T>
IPtr<T> adopt (Owned<T>&& ref) { return Detail::Adopt::adopt (ref); }

/** Common function to adopt referenced counted object. 
  *	@tparam T			Referenced counted type.
  * @param ref			The reference to be adopted in a smart pointer.
  */
template <typename T>
T* adopt (Used<T>& ref) { return Detail::Adopt::adopt (ref); }

template <typename T>
T* adopt (Used<T>&& ref) { return Detail::Adopt::adopt (ref); }

/** Common function to wrap owned instances. */
template <typename T>
Owned<T> toOwned (T* obj) { return Detail::Adopt::toOwnerType<Owned> (obj); }

/** Common function to wrap shared instances. */
template <typename T>
Shared<T> toShared (T* obj) { return Detail::Adopt::toOwnerType<Shared> (obj); }

/** Common function to wrap used instances. */
template <typename T>
Used<T> toUsed (T* obj) { return Detail::Adopt::toOwnerType<Used> (obj); }

//------------------------------------------------------------------------
} // SKI
#endif
} // Steinberg
//-----------------------------------------------------------------------------
// Project     : SDK Core
//
// Category    : SDK Core Interfaces
// Filename    : pluginterfaces/base/typesizecheck.h
// Created by  : Steinberg, 08/2018
// Description : Compile time type size check macro
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#pragma once

#include "pluginterfaces/base/fplatform.h"

#if SMTG_CPP11
#define SMTG_TYPE_STATIC_CHECK(Operator, Type, Platform64Size, MacOS32Size, Win32Size,             \
                               Linux32Size)                                                        \
	namespace {                                                                                    \
	template <typename Type, size_t w, size_t x, size_t y, size_t z>                               \
	struct Operator##Check##Type                                                                   \
	{                                                                                              \
		constexpr Operator##Check##Type ()                                                         \
		{                                                                                          \
			static_assert (Operator (Type) ==                                                      \
			                   (SMTG_PLATFORM_64 ? w : SMTG_OS_MACOS ? x : SMTG_OS_LINUX ? z : y), \
			               "Struct " #Operator " error: " #Type);                                     \
		}                                                                                          \
	};                                                                                             \
	static constexpr Operator##Check##Type<Type, Platform64Size, MacOS32Size, Win32Size,           \
	                                       Linux32Size>                                            \
	    instance##Operator##Type;                                                                  \
	}

/** Check the size of a structure depending on compilation platform
 *	Used to check that structure sizes don't change between SDK releases.
 */
#define SMTG_TYPE_SIZE_CHECK(Type, Platform64Size, MacOS32Size, Win32Size, Linux32Size) \
	SMTG_TYPE_STATIC_CHECK (sizeof, Type, Platform64Size, MacOS32Size, Win32Size, Linux32Size)

/** Check the alignment of a structure depending on compilation platform
 *	Used to check that structure alignments don't change between SDK releases.
 */
#define SMTG_TYPE_ALIGN_CHECK(Type, Platform64Size, MacOS32Size, Win32Size, Linux32Size) \
	SMTG_TYPE_STATIC_CHECK (alignof, Type, Platform64Size, MacOS32Size, Win32Size, Linux32Size)

#else
// need static_assert
#define SMTG_TYPE_SIZE_CHECK(Type, Platform64Size, MacOS32Size, Win32Size, Linux32Size)
#endif
//-----------------------------------------------------------------------------
// Project     : SDK Core
//
// Category    : SDK Core Interfaces
// Filename    : pluginterfaces/base/ucolorspec.h
// Created by  : Steinberg, 02/2006	(Host: S4.1)
// Description : GUI data types
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses. 
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#pragma once

#include "pluginterfaces/base/ftypes.h"

namespace Steinberg {

//------------------------------------------------------------------------
// Colors		
typedef uint32			ColorSpec;
typedef uint8			ColorComponent;

typedef ColorSpec		UColorSpec;			// legacy support
typedef ColorComponent	UColorComponent;	// legacy support

/** Create color specifier with RGB values (alpha is opaque) */
inline SMTG_CONSTEXPR ColorSpec MakeColorSpec (ColorComponent r, ColorComponent g, ColorComponent b)
{ 
	return 0xFF000000 | ((ColorSpec)r) << 16 | ((ColorSpec)g) << 8 | (ColorSpec)b;
}
/** Create color specifier with RGBA values  */
inline SMTG_CONSTEXPR ColorSpec MakeColorSpec (ColorComponent r, ColorComponent g, ColorComponent b, ColorComponent a)
{ 
	return ((ColorSpec)a) << 24 | ((ColorSpec)r) << 16 | ((ColorSpec)g) << 8 | (uint32)b;
}

inline SMTG_CONSTEXPR ColorComponent GetBlue (ColorSpec cs)		         { return (ColorComponent)(cs & 0x000000FF); }
inline SMTG_CONSTEXPR ColorComponent GetGreen (ColorSpec cs)		     { return (ColorComponent)((cs >> 8) & 0x000000FF); }
inline SMTG_CONSTEXPR ColorComponent GetRed (ColorSpec cs)		         { return (ColorComponent)((cs >> 16) & 0x000000FF); }
inline SMTG_CONSTEXPR ColorComponent GetAlpha (ColorSpec cs)		     { return (ColorComponent)((cs >> 24) & 0x000000FF); }

inline void SetBlue (ColorSpec& argb, ColorComponent b)	 { argb = (argb & 0xFFFFFF00) | (ColorSpec)(b); }
inline void SetGreen (ColorSpec& argb, ColorComponent g)	 { argb = (argb & 0xFFFF00FF) | (((ColorSpec)g) << 8); }
inline void SetRed (ColorSpec& argb, ColorComponent r)	 { argb = (argb & 0xFF00FFFF) | (((ColorSpec)r) << 16); }
inline void SetAlpha (ColorSpec& argb, ColorComponent a)	 { argb = (argb & 0x00FFFFFF) | (((ColorSpec)a) << 24); }

/** Normalized color components*/
/** { */
inline double NormalizeColorComponent (ColorComponent c)    { return c / 255.0; }
inline ColorComponent DenormalizeColorComponent (double c)  { return static_cast<ColorComponent> (c * 255.0); }

inline void SetAlphaNorm (ColorSpec& argb, double a)        { SetAlpha (argb, DenormalizeColorComponent (a)); }
inline double GetAlphaNorm (ColorSpec cs)	                 { return (NormalizeColorComponent (GetAlpha (cs))); }
inline double NormalizeAlpha (uint8 alpha)                   {return NormalizeColorComponent (alpha);}
inline ColorComponent DenormalizeAlpha (double alphaNorm)   { return DenormalizeColorComponent (alphaNorm); }
/** } */
inline ColorSpec StripAlpha (ColorSpec argb)               { return (argb & 0x00FFFFFF); }

inline ColorSpec SMTG_CONSTEXPR BlendColor (ColorSpec color, double opacity)
{
	return MakeColorSpec (
		GetRed (color),
		GetGreen (color),
		GetBlue (color),
		static_cast<ColorComponent> (GetAlpha(color) * opacity)
	);
}

enum StandardColor	 //TODO_REFACTOR: change to enum class (c++11)
{
	kBlack = 0,
	kWhite,
	kGray5,
	kGray10,
	kGray20,
	kGray30,
	kGray40,
	kGray50,
	kGray60,
	kGray70,
	kGray80,
	kGray90,
	kRed,
	kLtRed,
	kDkRed,
	kGreen,
	kLtGreen,
	kDkGreen,
	kBlue,
	kLtBlue,
	kDkBlue,
	kMagenta,
	kLtMagenta,
	kDkMagenta,
	kYellow,
	kLtYellow,
	kDkYellow,
	kOrange,
	kLtOrange,
	kDkOrange,
	kGold,
	kBlack50,
	kBlack70,
	kNumStandardColors,
	kLtGray = kGray20,
	kGray = kGray50,
	kDkGray = kGray70
};

}
//-----------------------------------------------------------------------------
// Project     : SDK Core
//
// Category    : Helpers
// Filename    : pluginterfaces/base/ustring.h
// Created by  : Steinberg, 12/2005
// Description : UTF-16 String class
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses. 
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#pragma once

#include "ftypes.h"

//------------------------------------------------------------------------
namespace Steinberg {

//------------------------------------------------------------------------
/** UTF-16 string class without buffer management.
 Note: that some characters are encoded in 2 UTF16 code units (surrogate pair),
 this means that getLength returns the number of code unit, not the count of character! */
class UString
{
public:
//------------------------------------------------------------------------
	/** Construct from UTF-16 string, size is in code unit (count of char16) */
	UString (char16* buffer, int32 size) : thisBuffer (buffer), thisSize (size) {}

	/** returns buffer size */
	int32 getSize () const { return thisSize; }
	
	/** cast to char16* */
	operator const char16* () const { return thisBuffer; }

	/** Returns length of string (in code unit). Note this is not the count of character! */
	int32 getLength () const;

	/** Copy from UTF-16 buffer (srcSize is in code unit (count of char16)). */
	UString& assign (const char16* src, int32 srcSize = -1);

	/** Append UTF-16 buffer (srcSize is in code unit (count of char16)). */
	UString& append (const char16* src, int32 srcSize = -1);

	/** Copy to UTF-16 buffer (dstSize is in code unit (count of char16)). */
	const UString& copyTo (char16* dst, int32 dstSize) const;

	/** Copy from ASCII string (srcSize is in code unit (count of char16)). */
	UString& fromAscii (const char* src, int32 srcSize = -1);
	UString& assign (const char* src, int32 srcSize = -1) { return fromAscii (src, srcSize); }

	/** Copy to ASCII string. */
	const UString& toAscii (char* dst, int32 dstSize) const;

	/** Scan integer from string. */
	bool scanInt (int64& value) const;

	/** Print integer to string. */
	bool printInt (int64 value);

	/** Scan float from string. */
	bool scanFloat (double& value) const;

	/** Print float to string. */
	bool printFloat (double value, int32 precision = 4);
//------------------------------------------------------------------------
protected:
	char16* thisBuffer;
	int32 thisSize; ///< size in code unit (not in byte!)
};

//------------------------------------------------------------------------
/** UTF-16 string with fixed buffer size.
 */
template <int32 maxSize>
class UStringBuffer : public UString
{
public:
//------------------------------------------------------------------------
	UStringBuffer () : UString (data, maxSize) { data[0] = 0; }

	/** Construct from UTF-16 string. */
	UStringBuffer (const char16* src, int32 srcSize = -1) : UString (data, maxSize)
	{
		data[0] = 0;
		if (src)
			assign (src, srcSize);
	}

	/** Construct from ASCII string. */
	UStringBuffer (const char* src, int32 srcSize = -1) : UString (data, maxSize)
	{
		data[0] = 0;
		if (src)
			fromAscii (src, srcSize);
	}
//------------------------------------------------------------------------
protected:
	char16 data[maxSize];
};

//------------------------------------------------------------------------
typedef UStringBuffer<128> UString128; ///< 128 character UTF-16 string
typedef UStringBuffer<256> UString256; ///< 256 character UTF-16 string
} // namespace Steinberg

//------------------------------------------------------------------------
#define USTRING(asciiString) Steinberg::UString256 (asciiString)
#define USTRINGSIZE(var) (sizeof (var) / sizeof (Steinberg::char16))

//------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Project     : SDK Core
//
// Category    : SDK GUI Interfaces
// Filename    : pluginterfaces/gui/iplugview.h
// Created by  : Steinberg, 12/2007
// Description : Plug-in User Interface
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#pragma once

#include "pluginterfaces/base/funknown.h"
#include "pluginterfaces/base/typesizecheck.h"

namespace Steinberg {

class IPlugFrame;

//------------------------------------------------------------------------
/*! \defgroup pluginGUI Graphical User Interface
*/

//------------------------------------------------------------------------
/**  Graphical rectangle structure. Used with IPlugView.
\ingroup pluginGUI
*/
struct ViewRect
{
	ViewRect (int32 l = 0, int32 t = 0, int32 r = 0, int32 b = 0)
	: left (l), top (t), right (r), bottom (b)
	{
	}

	int32 left;
	int32 top;
	int32 right;
	int32 bottom;

	//--- ---------------------------------------------------------------------
	int32 getWidth () const { return right - left; }
	int32 getHeight () const { return bottom - top; }
};

SMTG_TYPE_SIZE_CHECK (ViewRect, 16, 16, 16, 16)

//------------------------------------------------------------------------
/**  \defgroup platformUIType Platform UI Types
\ingroup pluginGUI
List of Platform UI types for IPlugView. This list is used to match the GUI-System between
the host and a plug-in in case that an OS provides multiple GUI-APIs.
*/
/*@{*/
/** The parent parameter in IPlugView::attached() is a HWND handle.
 *	You should attach a child window to it. */
const FIDString kPlatformTypeHWND = "HWND"; ///< HWND handle. (Microsoft Windows)

/** The parent parameter in IPlugView::attached() is a WindowRef.
 *	You should attach a HIViewRef to the content view of the window. */
const FIDString kPlatformTypeHIView = "HIView"; ///< HIViewRef. (Mac OS X)

/** The parent parameter in IPlugView::attached() is a NSView pointer.
 * You should attach a NSView to it. */
const FIDString kPlatformTypeNSView = "NSView"; ///< NSView pointer. (Mac OS X)

/** The parent parameter in IPlugView::attached() is a UIView pointer.
 * You should attach an UIView to it. */
const FIDString kPlatformTypeUIView = "UIView"; ///< UIView pointer. (iOS)

/** The parent parameter in IPlugView::attached() is a X11 Window supporting XEmbed.
 * You should attach a Window to it that supports the XEmbed extension.
 * See https://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html */
const FIDString kPlatformTypeX11EmbedWindowID = "X11EmbedWindowID"; ///< X11 Window ID. (X11)

/*@}*/
//------------------------------------------------------------------------

//------------------------------------------------------------------------
/**  Plug-in definition of a view.
\ingroup pluginGUI vstIPlug vst300
- [plug imp]
- [released: 3.0.0]

\par Sizing of a view
Usually, the size of a plug-in view is fixed. But both the host and the plug-in can cause
a view to be resized:
\n
- \b Host: If IPlugView::canResize () returns kResultTrue the host will set up the window
  so that the user can resize it. While the user resizes the window,
  IPlugView::checkSizeConstraint () is called, allowing the plug-in to change the size to a valid
  a valid supported rectangle size. The host then resizes the window to this rect and has to call IPlugView::onSize ().
\n
\n
- \b Plug-in: The plug-in can call IPlugFrame::resizeView () and cause the host to resize the
  window.\n\n
  Afterwards, in the same callstack, the host has to call IPlugView::onSize () if a resize is needed (size was changed).
  Note that if the host calls IPlugView::getSize () before calling IPlugView::onSize () (if needed),
  it will get the current (old) size not the wanted one!!\n
  Here the calling sequence:\n
    - plug-in->host: IPlugFrame::resizeView (newSize)
	- host->plug-in (optional): IPlugView::getSize () returns the currentSize (not the newSize!)
	- host->plug-in: if newSize is different from the current size: IPlugView::onSize (newSize)
    - host->plug-in (optional): IPlugView::getSize () returns the newSize
\n
<b>Please only resize the platform representation of the view when IPlugView::onSize () is
called.</b>

\par Keyboard handling
The plug-in view receives keyboard events from the host. A view implementation must not handle
keyboard events by the means of platform callbacks, but let the host pass them to the view. The host
depends on a proper return value when IPlugView::onKeyDown is called, otherwise the plug-in view may
cause a malfunction of the host's key command handling.

\see IPlugFrame, \ref platformUIType
*/
class IPlugView : public FUnknown
{
public:
//------------------------------------------------------------------------
	/** Is Platform UI Type supported
	    \param type : IDString of \ref platformUIType */
	virtual tresult PLUGIN_API isPlatformTypeSupported (FIDString type) = 0;

	/** The parent window of the view has been created, the (platform) representation of the view
		should now be created as well.
	    Note that the parent is owned by the caller and you are not allowed to alter it in any way
		other than adding your own views.
	    Note that in this call the plug-in could call a IPlugFrame::resizeView ()!
	    \param parent : platform handle of the parent window or view
	    \param type : \ref platformUIType which should be created */
	virtual tresult PLUGIN_API attached (void* parent, FIDString type) = 0;

	/** The parent window of the view is about to be destroyed.
	    You have to remove all your own views from the parent window or view. */
	virtual tresult PLUGIN_API removed () = 0;

	/** Handling of mouse wheel. */
	virtual tresult PLUGIN_API onWheel (float distance) = 0;

	/** Handling of keyboard events : Key Down.
	    \param key : unicode code of key
	    \param keyCode : virtual keycode for non ascii keys - see \ref VirtualKeyCodes in keycodes.h
	    \param modifiers : any combination of modifiers - see \ref KeyModifier in keycodes.h
	    \return kResultTrue if the key is handled, otherwise kResultFalse. \n
	            <b> Please note that kResultTrue must only be returned if the key has really been
	   handled. </b> Otherwise key command handling of the host might be blocked! */
	virtual tresult PLUGIN_API onKeyDown (char16 key, int16 keyCode, int16 modifiers) = 0;

	/** Handling of keyboard events : Key Up.
	    \param key : unicode code of key
	    \param keyCode : virtual keycode for non ascii keys - see \ref VirtualKeyCodes in keycodes.h
	    \param modifiers : any combination of KeyModifier - see \ref KeyModifier in keycodes.h
	    \return kResultTrue if the key is handled, otherwise return kResultFalse. */
	virtual tresult PLUGIN_API onKeyUp (char16 key, int16 keyCode, int16 modifiers) = 0;

	/** Returns the size of the platform representation of the view. */
	virtual tresult PLUGIN_API getSize (ViewRect* size) = 0;

	/** Resizes the platform representation of the view to the given rect. Note that if the plug-in
	 *	requests a resize (IPlugFrame::resizeView ()) onSize has to be called afterward. */
	virtual tresult PLUGIN_API onSize (ViewRect* newSize) = 0;

	/** Focus changed message. */
	virtual tresult PLUGIN_API onFocus (TBool state) = 0;

	/** Sets IPlugFrame object to allow the plug-in to inform the host about resizing. */
	virtual tresult PLUGIN_API setFrame (IPlugFrame* frame) = 0;

	/** Is view sizable by user. */
	virtual tresult PLUGIN_API canResize () = 0;

	/** On live resize this is called to check if the view can be resized to the given rect, if not
	 *	adjust the rect to the allowed size. */
	virtual tresult PLUGIN_API checkSizeConstraint (ViewRect* rect) = 0;
//------------------------------------------------------------------------
	static const FUID iid;
};

DECLARE_CLASS_IID (IPlugView, 0x5BC32507, 0xD06049EA, 0xA6151B52, 0x2B755B29)

//------------------------------------------------------------------------
/** Callback interface passed to IPlugView.
\ingroup pluginGUI vstIHost vst300
- [host imp]
- [released: 3.0.0]
- [mandatory]

Enables a plug-in to resize the view and cause the host to resize the window.
*/
class IPlugFrame : public FUnknown
{
public:
//------------------------------------------------------------------------
	/** Called to inform the host about the resize of a given view.
	 *	Afterwards the host has to call IPlugView::onSize (). */
	virtual tresult PLUGIN_API resizeView (IPlugView* view, ViewRect* newSize) = 0;
//------------------------------------------------------------------------
	static const FUID iid;
};

DECLARE_CLASS_IID (IPlugFrame, 0x367FAF01, 0xAFA94693, 0x8D4DA2A0, 0xED0882A3)

#if SMTG_OS_LINUX
//------------------------------------------------------------------------
namespace Linux {

using TimerInterval = uint64;
using FileDescriptor = int;

//------------------------------------------------------------------------
/** Linux event handler interface
\ingroup pluginGUI vst368
- [plug imp]
- [released: 3.6.8]
\see IRunLoop
*/
class IEventHandler : public FUnknown
{
public:
	virtual void PLUGIN_API onFDIsSet (FileDescriptor fd) = 0;
//------------------------------------------------------------------------
	static const FUID iid;
};
DECLARE_CLASS_IID (IEventHandler, 0x561E65C9, 0x13A0496F, 0x813A2C35, 0x654D7983)

//------------------------------------------------------------------------
/** Linux timer handler interface
\ingroup pluginGUI vst368
- [plug imp]
- [released: 3.6.8]
\see IRunLoop
*/
class ITimerHandler : public FUnknown
{
public:
	virtual void PLUGIN_API onTimer () = 0;
//------------------------------------------------------------------------
	static const FUID iid;
};
DECLARE_CLASS_IID (ITimerHandler, 0x10BDD94F, 0x41424774, 0x821FAD8F, 0xECA72CA9)

//------------------------------------------------------------------------
/** Linux host run loop interface
\ingroup pluginGUI vst368
- [host imp]
- [extends IPlugFrame]
- [released: 3.6.8]

On Linux the host has to provide this interface to the plug-in as there's no global event run loop
defined as on other platforms. 

This can be done by IPlugFrame and the context which is passed to the plug-in as an argument 
in the method IPlugFactory3::setHostContext. This way the plug-in can get a runloop even if 
it does not have an editor.

A plug-in can register an event handler for a file descriptor. The host has to call the event
handler when the file descriptor is marked readable.

A plug-in also can register a timer which will be called repeatedly until it is unregistered.
*/
class IRunLoop : public FUnknown
{
public:
	virtual tresult PLUGIN_API registerEventHandler (IEventHandler* handler, FileDescriptor fd) = 0;
	virtual tresult PLUGIN_API unregisterEventHandler (IEventHandler* handler) = 0;

	virtual tresult PLUGIN_API registerTimer (ITimerHandler* handler,
											  TimerInterval milliseconds) = 0;
	virtual tresult PLUGIN_API unregisterTimer (ITimerHandler* handler) = 0;
//------------------------------------------------------------------------
	static const FUID iid;
};
DECLARE_CLASS_IID (IRunLoop, 0x18C35366, 0x97764F1A, 0x9C5B8385, 0x7A871389)

//------------------------------------------------------------------------
} // namespace Linux

#endif

//------------------------------------------------------------------------
} // namespace Steinberg
//-----------------------------------------------------------------------------
// Project     : SDK Core
//
// Category    : SDK GUI Interfaces
// Filename    : pluginterfaces/gui/iplugviewcontentscalesupport.h
// Created by  : Steinberg, 06/2016
// Description : Plug-in User Interface Scaling
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses. 
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#pragma once

#include "pluginterfaces/base/funknown.h"

//------------------------------------------------------------------------
#include "pluginterfaces/base/falignpush.h"
//------------------------------------------------------------------------

//------------------------------------------------------------------------
namespace Steinberg {

//------------------------------------------------------------------------
/** Plug-in view content scale support
\ingroup pluginGUI vstIPlug vst366
- [plug impl]
- [extends IPlugView]
- [released: 3.6.6]
- [optional]

This interface communicates the content scale factor from the host to the plug-in view on
systems where plug-ins cannot get this information directly like Microsoft Windows.

The host calls setContentScaleFactor directly before or after the plug-in view is attached and when
the scale factor changes while the view is attached (system change or window moved to another screen
with different scaling settings).

The host may call setContentScaleFactor in a different context, for example: scaling the plug-in
editor for better readability.

When a plug-in handles this (by returning kResultTrue), it needs to scale the width and height of
its view by the scale factor and inform the host via a IPlugFrame::resizeView(). The host will then
call IPlugView::onSize().

Note that the host is allowed to call setContentScaleFactor() at any time the IPlugView is valid.
If this happens before the IPlugFrame object is set on your view, make sure that when the host calls
IPlugView::getSize() afterwards you return the size of your view for that new scale factor.

It is recommended to implement this interface on Microsoft Windows to let the host know that the
plug-in is able to render in different scalings.
*/
class IPlugViewContentScaleSupport : public FUnknown
{
public:
//------------------------------------------------------------------------
	typedef float ScaleFactor;

	virtual tresult PLUGIN_API setContentScaleFactor (ScaleFactor factor) = 0;
//------------------------------------------------------------------------
	static const FUID iid;
};

DECLARE_CLASS_IID (IPlugViewContentScaleSupport, 0x65ED9690, 0x8AC44525, 0x8AADEF7A, 0x72EA703F)

//------------------------------------------------------------------------
} // namespace Steinberg

//------------------------------------------------------------------------
#include "pluginterfaces/base/falignpop.h"
//------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Project     : SDK Core
// Version     : 1.0
//
// Category    : SDK Core Interfaces
// Filename    : pluginterfaces/test/itest.h
// Created by  : Steinberg, 01/2005
// Description : Test Interface - Availability Depends on HOST  
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses. 
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#pragma once

#include "pluginterfaces/base/funknown.h"

#ifndef kTestClass
#define kTestClass "Test Class" ///< A class for automated tests
#endif

namespace Steinberg {

class ITestResult;

/** ------------------------------------------------------------------------
ITest interface declaration
*/
class ITest : public FUnknown
{
public:
	//--- ---------------------------------------------------------------------
	/** called immediately before the test is actually run.
	    Usually this will be used to setup the test environment.
	    \return true upon success	*/
	virtual bool PLUGIN_API setup () = 0;

	/** execute the test.
	    \param testResult : points to a test result where the test can
	                        (optionally) add an error message.
	    \return true upon success
	    \sa ITestResult */
	virtual bool PLUGIN_API run (ITestResult* testResult) = 0;

	/** called after the test has run. This method shall be used to
	    deconstruct a test environment that has been setup with ITest::setup ().
	\return true upon success */
	virtual bool PLUGIN_API teardown () = 0;

	/** This function is used to provide information about the performed
	    testcase. What is done, what is validated and what has to be prepared
	    before executing the test (in case of half-automated tests).
	\return null terminated string upon success, zero otherwise */
	virtual const tchar* PLUGIN_API getDescription () { return nullptr; }
	//--- ---------------------------------------------------------------------
	static const FUID iid;
};

#ifdef UNICODE
DECLARE_CLASS_IID (ITest, 0xFE64FC19, 0x95684F53, 0xAAA78DC8, 0x7228338E)
#else
DECLARE_CLASS_IID (ITest, 0x9E2E608B, 0x64C64CF8, 0x839059BD, 0xA194032D)
#endif

//------------------------------------------------------------------------
// ITestResult interface declaration
//------------------------------------------------------------------------
/** Test Result message logger
[host imp]
when a test is called, a pointer to an ITestResult is passed in, so the 
test class can output error messages
*/
class ITestResult : public FUnknown
{
public:
	//--- ---------------------------------------------------------------------
	/** add an error message */
	virtual void PLUGIN_API addErrorMessage (const tchar* msg) = 0;
	virtual void PLUGIN_API addMessage (const tchar* msg) = 0;
	//--- ---------------------------------------------------------------------
	static const FUID iid;
};

#ifdef UNICODE
DECLARE_CLASS_IID (ITestResult, 0x69796279, 0xF651418B, 0xB24D79B7, 0xD7C527F4)
#else
DECLARE_CLASS_IID (ITestResult, 0xCE13B461, 0x5334451D, 0xB3943E99, 0x7446885B)
#endif

//------------------------------------------------------------------------
// ITestSuite interface declaration
//------------------------------------------------------------------------
/** A collection of tests supporting a hierarchical ordering 
[host imp]
[create via hostclasses]
*/
class ITestSuite : public FUnknown
{
public:
	//--- ---------------------------------------------------------------------
	/** append a new test */
	virtual tresult PLUGIN_API addTest (FIDString name, ITest* test) = 0;

	/** append an entire test suite as a child suite */
	virtual tresult PLUGIN_API addTestSuite (FIDString name, ITestSuite* testSuite) = 0;

	virtual tresult PLUGIN_API setEnvironment (ITest* environment) = 0;

	//--- ---------------------------------------------------------------------
	static const FUID iid;
};

#ifdef UNICODE
DECLARE_CLASS_IID (ITestSuite, 0x5CA7106F, 0x98784AA5, 0xB4D30D71, 0x2F5F1498)
#else
DECLARE_CLASS_IID (ITestSuite, 0x81724C94, 0xE9F64F65, 0xACB104E9, 0xCC702253)
#endif

//------------------------------------------------------------------------
//  ITestFactory interface declaration
//------------------------------------------------------------------------
/**	Class factory that any testable module defines for creating tests
that will be executed from the host
- [plug imp]
*/
class ITestFactory : public FUnknown
{
public:
	//--- ---------------------------------------------------------------------
	/** create the tests that this module provides.
		\param context :
		\param parentSuite : the test suite that the newly created tests
							shall register with. */
	virtual tresult PLUGIN_API createTests (FUnknown* context, ITestSuite* parentSuite) = 0;
	//--- ---------------------------------------------------------------------
	static const FUID iid;
};

#ifdef UNICODE
DECLARE_CLASS_IID (ITestFactory, 0xAB483D3A, 0x15264650, 0xBF86EEF6, 0x9A327A93)
#else
DECLARE_CLASS_IID (ITestFactory, 0xE77EA913, 0x58AA4838, 0x986A4620, 0x53579080)
#endif

//------------------------------------------------------------------------
} // namespace Steinberg