Initial release
This commit is contained in:
+24
@@ -0,0 +1,24 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025, Steinberg Media Technologies GmbH
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
//---------------------------------------------------------------------------------
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
# Welcome to VST SDK 3 base
|
||||
|
||||
Here you can find some helper classes useful for developing **VST 3** plug-ins.
|
||||
|
||||
## License & Usage guidelines
|
||||
|
||||
More details are found at [www.steinberg.net/sdklicenses_vst3](http://www.steinberg.net/sdklicenses_vst3)
|
||||
|
||||
----
|
||||
Return to [VST 3 SDK](https://github.com/steinbergmedia/vst3sdk)
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
//------------------------------------------------------------------------
|
||||
// Project : SDK Base
|
||||
// Version : 1.0
|
||||
//
|
||||
// Category : Helpers
|
||||
// Filename : base/source/baseidds.cpp
|
||||
// Created by : Steinberg, 01/2008
|
||||
// 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 "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
|
||||
@@ -0,0 +1,67 @@
|
||||
//------------------------------------------------------------------------
|
||||
// Project : SDK Base
|
||||
// Version : 1.0
|
||||
//
|
||||
// Category : Helpers
|
||||
// Filename : base/source/classfactoryhelpers.h
|
||||
// Created by : Steinberg, 03/2017
|
||||
// Description : Class factory
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 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)
|
||||
+621
@@ -0,0 +1,621 @@
|
||||
//------------------------------------------------------------------------
|
||||
// Project : SDK Base
|
||||
// Version : 1.0
|
||||
//
|
||||
// Category : Helpers
|
||||
// Filename : base/source/fbuffer.cpp
|
||||
// Created by : Steinberg, 2008
|
||||
// Description :
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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 "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 = NEWSTR8 ((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
|
||||
+286
@@ -0,0 +1,286 @@
|
||||
//------------------------------------------------------------------------
|
||||
// Project : SDK Base
|
||||
// Version : 1.0
|
||||
//
|
||||
// Category : Helpers
|
||||
// Filename : base/source/fbuffer.h
|
||||
// Created by : Steinberg, 2008
|
||||
// Description :
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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"
|
||||
#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
|
||||
+302
@@ -0,0 +1,302 @@
|
||||
//------------------------------------------------------------------------
|
||||
// Project : SDK Base
|
||||
// Version : 1.0
|
||||
//
|
||||
// Category : Helpers
|
||||
// Filename : base/source/fcleanup.h
|
||||
// Created by : Steinberg, 2008
|
||||
// Description : Template classes for automatic resource cleanup
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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 <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
|
||||
+367
@@ -0,0 +1,367 @@
|
||||
//------------------------------------------------------------------------
|
||||
// Project : SDK Base
|
||||
// Version : 1.0
|
||||
//
|
||||
// Category : Helpers
|
||||
// Filename : base/source/fcommandline.h
|
||||
// Created by : Steinberg, 2007
|
||||
// Description : Very simple command-line parser. @see Steinberg::CommandLine
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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 <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.
|
||||
|
||||
/** Retrieve the value of option @c k.*/
|
||||
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;
|
||||
|
||||
/** Returns @c != @c 0 if command-line contains option @c k. */
|
||||
VariablesMapContainer::size_type count (const VariablesMapContainer::key_type k) const;
|
||||
};
|
||||
|
||||
//! 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:
|
||||
/** Construct a Description */
|
||||
Description (const std::string& name, const std::string& help,
|
||||
const std::string& valueType);
|
||||
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;
|
||||
}
|
||||
|
||||
/** Parse the command - line. */
|
||||
bool parse (int ac, char* av[], const Descriptions& desc, VariablesMap& result,
|
||||
FilesVector* files = nullptr);
|
||||
|
||||
/** Make Descriptions stream able. */
|
||||
std::ostream& operator<< (std::ostream& os, const Descriptions& desc);
|
||||
|
||||
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
|
||||
+320
@@ -0,0 +1,320 @@
|
||||
//------------------------------------------------------------------------
|
||||
// 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.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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 "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;
|
||||
}
|
||||
+226
@@ -0,0 +1,226 @@
|
||||
//------------------------------------------------------------------------
|
||||
// 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.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** @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
|
||||
+244
@@ -0,0 +1,244 @@
|
||||
//------------------------------------------------------------------------
|
||||
// Project : SDK Base
|
||||
// Version : 1.0
|
||||
//
|
||||
// Category : Helpers
|
||||
// Filename : base/source/fdynlib.cpp
|
||||
// Created by : Steinberg, 1998
|
||||
// Description : Dynamic library loading
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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 "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
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
//------------------------------------------------------------------------
|
||||
// Project : SDK Base
|
||||
// Version : 1.0
|
||||
//
|
||||
// Category : Helpers
|
||||
// Filename : base/source/fdynlib.h
|
||||
// Created by : Steinberg, 1998
|
||||
// Description : Dynamic library loading
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/** @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
|
||||
+274
@@ -0,0 +1,274 @@
|
||||
//------------------------------------------------------------------------
|
||||
// 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
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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 "base/source/fobject.h"
|
||||
#include "base/thread/include/flock.h"
|
||||
|
||||
#include <functional>
|
||||
#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 {
|
||||
|
||||
// Entry point for addRef/release tracking. See fobjecttracker.h
|
||||
//------------------------------------------------------------------------
|
||||
#if DEVELOPMENT
|
||||
using FObjectTrackerFn = std::function<void (FObject*, bool)>;
|
||||
FObjectTrackerFn gFObjectTracker = nullptr;
|
||||
#endif
|
||||
|
||||
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 ()
|
||||
{
|
||||
#if DEVELOPMENT
|
||||
if (gFObjectTracker)
|
||||
{
|
||||
gFObjectTracker (this, true);
|
||||
}
|
||||
#endif
|
||||
|
||||
return FUnknownPrivate::atomicAdd (refCount, 1);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
uint32 PLUGIN_API FObject::release ()
|
||||
{
|
||||
#if DEVELOPMENT
|
||||
if (gFObjectTracker)
|
||||
{
|
||||
gFObjectTracker (this, false);
|
||||
}
|
||||
#endif
|
||||
|
||||
auto rc = FUnknownPrivate::atomicAdd (refCount, -1);
|
||||
if (rc == 0)
|
||||
{
|
||||
refCount = -1000;
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
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
|
||||
+558
@@ -0,0 +1,558 @@
|
||||
//------------------------------------------------------------------------
|
||||
// Project : SDK Base
|
||||
// Version : 1.0
|
||||
//
|
||||
// Category : Helpers
|
||||
// Filename : base/source/fobject.h
|
||||
// Created by : Steinberg, 2008
|
||||
// Description : Basic Object implementing FUnknown
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/** @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
|
||||
/** convert from FUnknown to FObject */
|
||||
template <class Class>
|
||||
static inline IPtr<Class> fromUnknown (FUnknown* unknown);
|
||||
|
||||
/** 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 subclass
|
||||
//------------------------------------------------------------------------
|
||||
template <class C>
|
||||
inline IPtr<C> FObject::fromUnknown (FUnknown* unknown)
|
||||
{
|
||||
if (unknown)
|
||||
{
|
||||
FObject* object = nullptr;
|
||||
if (unknown->queryInterface (FObject::iid, (void**)&object) == kResultTrue && object)
|
||||
{
|
||||
if (object->isTypeOf (C::getFClassID (), true))
|
||||
return IPtr<C> (static_cast<C*> (object), false);
|
||||
object->release ();
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline FObject* FObject::unknownToObject (FUnknown* unknown)
|
||||
{
|
||||
FObject* object = nullptr;
|
||||
if (unknown)
|
||||
{
|
||||
unknown->queryInterface (FObject::iid, (void**)&object);
|
||||
if (object)
|
||||
{
|
||||
if (object->release () == 0)
|
||||
object = nullptr;
|
||||
}
|
||||
}
|
||||
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 nullptr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/** FCast overload 2 - FUnknown to FObject */
|
||||
//-----------------------------------------------------------------------
|
||||
template <class C>
|
||||
inline C* FCast (FUnknown* unknown)
|
||||
{
|
||||
FObject* object = FObject::unknownToObject (unknown);
|
||||
return FCast<C> (object);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/** ICast - casting from FObject to FUnknown Interface */
|
||||
//-----------------------------------------------------------------------
|
||||
template<class I>
|
||||
inline IPtr<I> ICast (FObject* object)
|
||||
{
|
||||
return FUnknownPtr<I> (object ? object->unknownCast () : nullptr);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/** ICast - casting from FUnknown to another FUnknown Interface */
|
||||
//-----------------------------------------------------------------------
|
||||
template<class I>
|
||||
inline IPtr<I> ICast (FUnknown* object)
|
||||
{
|
||||
return FUnknownPtr<I> (object);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
template <class C>
|
||||
inline C* FCastIsA (const FObject* object)
|
||||
{
|
||||
if (object && object->isA (C::getFClassID ()))
|
||||
return (C*)object;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifndef SMTG_HIDE_DEPRECATED_INLINE_FUNCTIONS
|
||||
//-----------------------------------------------------------------------
|
||||
/** \deprecated FUCast - casting from FUnknown to Interface */
|
||||
//-----------------------------------------------------------------------
|
||||
template <class C>
|
||||
SMTG_DEPRECATED_MSG("use ICast<>") inline C* FUCast (FObject* object)
|
||||
{
|
||||
return FUnknownPtr<C> (object ? object->unknownCast () : nullptr);
|
||||
}
|
||||
|
||||
template <class C>
|
||||
SMTG_DEPRECATED_MSG("use ICast<>") inline C* FUCast (FUnknown* object)
|
||||
{
|
||||
return FUnknownPtr<C> (object);
|
||||
}
|
||||
#endif // SMTG_HIDE_DEPRECATED_FUNCTIONS
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/** @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, Steinberg::getTUID<InterfaceName> (), 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
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
//------------------------------------------------------------------------
|
||||
// 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.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/** @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; }
|
||||
+736
@@ -0,0 +1,736 @@
|
||||
//------------------------------------------------------------------------
|
||||
// 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
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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 "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) ? NEWSTR8 (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
|
||||
+222
@@ -0,0 +1,222 @@
|
||||
//------------------------------------------------------------------------
|
||||
// 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
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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 {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
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
|
||||
+3997
File diff suppressed because it is too large
Load Diff
+758
@@ -0,0 +1,758 @@
|
||||
//------------------------------------------------------------------------
|
||||
// Project : SDK Base
|
||||
// Version : 1.0
|
||||
//
|
||||
// Category : Helpers
|
||||
// Filename : base/source/fstring.h
|
||||
// Created by : Steinberg, 2008
|
||||
// Description : 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 "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>
|
||||
#include <cstdlib>
|
||||
|
||||
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.
|
||||
|
||||
If allocated externally and passed in via take() or extracted via pass(), memory
|
||||
is expected to be allocated with C's malloc() (rather than new) and deallocated with free().
|
||||
Use the NEWSTR8/16 and DELETESTR8/16 macros below.
|
||||
|
||||
\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 memory allocation macros
|
||||
#define NEWSTR8(len) ((char8*)::malloc(len)) // len includes trailing zero
|
||||
#define NEWSTR16(len) ((char16*)::malloc(2*(len)))
|
||||
#define DELETESTR8(p) (::free((char*)(p))) // cast to strip const
|
||||
#define DELETESTR16(p) (::free((char16*)(p)))
|
||||
|
||||
// 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
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
//-------------------------------------------------------------------------------------
|
||||
// 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) 2024, 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
|
||||
+358
@@ -0,0 +1,358 @@
|
||||
//------------------------------------------------------------------------
|
||||
// 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
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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 "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 () override;
|
||||
|
||||
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
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
//------------------------------------------------------------------------
|
||||
// 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
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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 "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
|
||||
+720
@@ -0,0 +1,720 @@
|
||||
//------------------------------------------------------------------------
|
||||
// Project : SDK Base
|
||||
// Version : 1.0
|
||||
//
|
||||
// Category : Helpers
|
||||
// Filename : base/source/updatehandler.cpp
|
||||
// Created by : Steinberg, 2008
|
||||
// Description :
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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 "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)
|
||||
{
|
||||
if (unknown->queryInterface (FObject::iid, (void**)&result) != kResultTrue)
|
||||
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;
|
||||
}
|
||||
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
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
//------------------------------------------------------------------------
|
||||
// Project : SDK Base
|
||||
// Version : 1.0
|
||||
//
|
||||
// Category : Helpers
|
||||
// Filename : base/source/updatehandler.h
|
||||
// Created by : Steinberg, 2008
|
||||
// Description :
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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 "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
|
||||
@@ -0,0 +1,104 @@
|
||||
//------------------------------------------------------------------------
|
||||
// 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...
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
/** @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
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // namespace Thread
|
||||
} // namespace Base
|
||||
} // namespace Steinberg
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
//------------------------------------------------------------------------
|
||||
// Project : SDK Base
|
||||
// Version : 1.0
|
||||
//
|
||||
// Category : Helpers
|
||||
// Filename : base/thread/include/flock.h
|
||||
// Created by : Steinberg, 1995
|
||||
// Description : locks
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
/** @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
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // namespace Thread
|
||||
} // namespace Base
|
||||
} // namespace Steinberg
|
||||
@@ -0,0 +1,259 @@
|
||||
//------------------------------------------------------------------------
|
||||
// Project : SDK Base
|
||||
// Version : 1.0
|
||||
//
|
||||
// Category : Helpers
|
||||
// Filename : base/thread/source/fcondition.cpp
|
||||
// Created by : Steinberg, 1995
|
||||
// Description : signals...
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#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
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // namespace Thread
|
||||
} // namespace Base
|
||||
} // namespace Steinberg
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
//------------------------------------------------------------------------
|
||||
// Project : SDK Base
|
||||
// Version : 1.0
|
||||
//
|
||||
// Category : Helpers
|
||||
// Filename : base/thread/source/flock.cpp
|
||||
// Created by : Steinberg, 1995
|
||||
// Description : Locks
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#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)§ion);
|
||||
|
||||
#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)§ion);
|
||||
|
||||
#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)§ion);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool FLock::trylock ()
|
||||
{
|
||||
#if SMTG_PTHREADS
|
||||
return pthread_mutex_trylock (&mutex) == 0;
|
||||
|
||||
#elif SMTG_OS_WINDOWS
|
||||
return TryEnterCriticalSection ((LPCRITICAL_SECTION)§ion) != 0 ? true : false;
|
||||
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // namespace Thread
|
||||
} // namespace Base
|
||||
} // namespace Steinberg
|
||||
Reference in New Issue
Block a user