forked from boostorg/beast
General refactoring, using C++11
* Remove broken RecycledObjectPool * Fix beast::ServiceQueue using List instead of LockFreeStack * Add class semaphore, fixes broken Semaphore * Move crytpo module files to new beast directory * Use c++11 replacements for boost and beast types: - std::atomic instead of beast::Atomic - std::function instead of boost::function, beast::function - std::unique_ptr instead of beast::ScopedPointer - std::shared_ptr instead of boost::shared_ptr * Remove modules: - beast_db - beast_crypto - beast_extras * Remove unnecessary classes: - AbstractFifo - AddConst - AtomicCounter - AtomicFlag - AtomicPointer - AtomicState - CopyConst - Expression - ForwardList - IfCond - Interval - IntrusiveArray - KeyvaDB - PointerToOther - PointerTraits - RemoveConst - RemoveConstVolatile - RemoveReference - RemoveVolatile - SharedObjectArray - SingleThreadedSharedObject - SophiaDB factory - SortedSet - WeakReference - beast::unique_ptr
This commit is contained in:
@@ -24,7 +24,7 @@
|
||||
|
||||
This class is compatible with std::allocator and can be used in any
|
||||
boost interface which takes a template parameter of type Allocator.
|
||||
This includes boost::function and especially boost::asio::streambuf
|
||||
This includes std::function and especially boost::asio::streambuf
|
||||
and relatives. This is vastly more efficient in a variety of situations
|
||||
especially during an upcall and when using stackful coroutines.
|
||||
|
||||
|
||||
@@ -129,7 +129,6 @@
|
||||
namespace beast
|
||||
{
|
||||
|
||||
#include "containers/AbstractFifo.cpp"
|
||||
#include "containers/DynamicObject.cpp"
|
||||
#include "containers/NamedValueSet.cpp"
|
||||
#include "containers/PropertySet.cpp"
|
||||
@@ -155,7 +154,6 @@ namespace beast
|
||||
#include "logging/Logger.cpp"
|
||||
|
||||
#include "maths/BigInteger.cpp"
|
||||
#include "maths/Expression.cpp"
|
||||
#include "maths/Random.cpp"
|
||||
|
||||
#include "memory/MemoryBlock.cpp"
|
||||
@@ -188,7 +186,6 @@ namespace beast
|
||||
|
||||
#include "thread/impl/TrackedMutex.cpp"
|
||||
#include "thread/DeadlineTimer.cpp"
|
||||
#include "thread/Semaphore.cpp"
|
||||
#include "thread/Workers.cpp"
|
||||
|
||||
#include "threads/ChildProcess.cpp"
|
||||
|
||||
@@ -77,10 +77,6 @@ class FileOutputStream;
|
||||
|
||||
#include "diagnostic/Throw.h"
|
||||
#include "system/Functional.h"
|
||||
#include "memory/AtomicCounter.h"
|
||||
#include "memory/AtomicFlag.h"
|
||||
#include "memory/AtomicPointer.h"
|
||||
#include "memory/AtomicState.h"
|
||||
#include "threads/SpinDelay.h"
|
||||
|
||||
#include "time/AtExitHook.h"
|
||||
@@ -155,7 +151,6 @@ class FileOutputStream;
|
||||
#include "logging/Logger.h"
|
||||
#include "diagnostic/FPUFlags.h"
|
||||
#include "memory/SharedFunction.h"
|
||||
#include "containers/AbstractFifo.h"
|
||||
#include "text/Identifier.h"
|
||||
#include "containers/Variant.h"
|
||||
#include "containers/LinkedListPointer.h"
|
||||
@@ -163,13 +158,10 @@ class FileOutputStream;
|
||||
#include "containers/DynamicObject.h"
|
||||
#include "maths/BigInteger.h"
|
||||
#include "maths/Random.h"
|
||||
#include "containers/LockFreeQueue.h"
|
||||
#include "containers/OwnedArray.h"
|
||||
#include "text/StringPairArray.h"
|
||||
#include "containers/PropertySet.h"
|
||||
#include "containers/SharedObjectArray.h"
|
||||
#include "containers/ScopedValueSetter.h"
|
||||
#include "containers/SortedSet.h"
|
||||
#include "maths/Range.h"
|
||||
#include "containers/SparseSet.h"
|
||||
#include "files/DirectoryIterator.h"
|
||||
@@ -186,12 +178,8 @@ class FileOutputStream;
|
||||
#include "json/JSON.h"
|
||||
#include "logging/FileLogger.h"
|
||||
#include "logging/Logger.h"
|
||||
#include "maths/Expression.h"
|
||||
#include "maths/Interval.h"
|
||||
#include "memory/OptionalScopedPointer.h"
|
||||
#include "memory/SharedSingleton.h"
|
||||
#include "memory/WeakReference.h"
|
||||
#include "memory/RecycledObjectPool.h"
|
||||
#include "misc/Main.h"
|
||||
#include "misc/Uuid.h"
|
||||
#include "misc/WindowsRegistry.h"
|
||||
@@ -228,7 +216,6 @@ class FileOutputStream;
|
||||
|
||||
#include "thread/DeadlineTimer.h"
|
||||
|
||||
#include "thread/Semaphore.h"
|
||||
#include "thread/Workers.h"
|
||||
|
||||
}
|
||||
|
||||
@@ -1,228 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
AbstractFifo::AbstractFifo (const int capacity) noexcept
|
||||
: bufferSize (capacity)
|
||||
{
|
||||
bassert (bufferSize > 0);
|
||||
}
|
||||
|
||||
AbstractFifo::~AbstractFifo() {}
|
||||
|
||||
int AbstractFifo::getTotalSize() const noexcept { return bufferSize; }
|
||||
int AbstractFifo::getFreeSpace() const noexcept { return bufferSize - getNumReady(); }
|
||||
|
||||
int AbstractFifo::getNumReady() const noexcept
|
||||
{
|
||||
const int vs = validStart.get();
|
||||
const int ve = validEnd.get();
|
||||
return ve >= vs ? (ve - vs) : (bufferSize - (vs - ve));
|
||||
}
|
||||
|
||||
void AbstractFifo::reset() noexcept
|
||||
{
|
||||
validEnd = 0;
|
||||
validStart = 0;
|
||||
}
|
||||
|
||||
void AbstractFifo::setTotalSize (int newSize) noexcept
|
||||
{
|
||||
bassert (newSize > 0);
|
||||
reset();
|
||||
bufferSize = newSize;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AbstractFifo::prepareToWrite (int numToWrite, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept
|
||||
{
|
||||
const int vs = validStart.get();
|
||||
const int ve = validEnd.value;
|
||||
|
||||
const int freeSpace = ve >= vs ? (bufferSize - (ve - vs)) : (vs - ve);
|
||||
numToWrite = bmin (numToWrite, freeSpace - 1);
|
||||
|
||||
if (numToWrite <= 0)
|
||||
{
|
||||
startIndex1 = 0;
|
||||
startIndex2 = 0;
|
||||
blockSize1 = 0;
|
||||
blockSize2 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
startIndex1 = ve;
|
||||
startIndex2 = 0;
|
||||
blockSize1 = bmin (bufferSize - ve, numToWrite);
|
||||
numToWrite -= blockSize1;
|
||||
blockSize2 = numToWrite <= 0 ? 0 : bmin (numToWrite, vs);
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractFifo::finishedWrite (int numWritten) noexcept
|
||||
{
|
||||
bassert (numWritten >= 0 && numWritten < bufferSize);
|
||||
int newEnd = validEnd.value + numWritten;
|
||||
if (newEnd >= bufferSize)
|
||||
newEnd -= bufferSize;
|
||||
|
||||
validEnd = newEnd;
|
||||
}
|
||||
|
||||
void AbstractFifo::prepareToRead (int numWanted, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept
|
||||
{
|
||||
const int vs = validStart.value;
|
||||
const int ve = validEnd.get();
|
||||
|
||||
const int numReady = ve >= vs ? (ve - vs) : (bufferSize - (vs - ve));
|
||||
numWanted = bmin (numWanted, numReady);
|
||||
|
||||
if (numWanted <= 0)
|
||||
{
|
||||
startIndex1 = 0;
|
||||
startIndex2 = 0;
|
||||
blockSize1 = 0;
|
||||
blockSize2 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
startIndex1 = vs;
|
||||
startIndex2 = 0;
|
||||
blockSize1 = bmin (bufferSize - vs, numWanted);
|
||||
numWanted -= blockSize1;
|
||||
blockSize2 = numWanted <= 0 ? 0 : bmin (numWanted, ve);
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractFifo::finishedRead (int numRead) noexcept
|
||||
{
|
||||
bassert (numRead >= 0 && numRead <= bufferSize);
|
||||
|
||||
int newStart = validStart.value + numRead;
|
||||
if (newStart >= bufferSize)
|
||||
newStart -= bufferSize;
|
||||
|
||||
validStart = newStart;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
class AbstractFifoTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
AbstractFifoTests() : UnitTest ("Abstract Fifo", "beast")
|
||||
{
|
||||
}
|
||||
|
||||
class WriteThread : public Thread
|
||||
{
|
||||
public:
|
||||
WriteThread (AbstractFifo& fifo_, int* buffer_)
|
||||
: Thread ("fifo writer"), fifo (fifo_), buffer (buffer_)
|
||||
{
|
||||
startThread();
|
||||
}
|
||||
|
||||
~WriteThread()
|
||||
{
|
||||
stopThread (5000);
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
int n = 0;
|
||||
Random r;
|
||||
|
||||
while (! threadShouldExit())
|
||||
{
|
||||
int num = r.nextInt (2000) + 1;
|
||||
|
||||
int start1, size1, start2, size2;
|
||||
fifo.prepareToWrite (num, start1, size1, start2, size2);
|
||||
|
||||
bassert (size1 >= 0 && size2 >= 0);
|
||||
bassert (size1 == 0 || (start1 >= 0 && start1 < fifo.getTotalSize()));
|
||||
bassert (size2 == 0 || (start2 >= 0 && start2 < fifo.getTotalSize()));
|
||||
|
||||
for (int i = 0; i < size1; ++i)
|
||||
buffer [start1 + i] = n++;
|
||||
|
||||
for (int i = 0; i < size2; ++i)
|
||||
buffer [start2 + i] = n++;
|
||||
|
||||
fifo.finishedWrite (size1 + size2);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
AbstractFifo& fifo;
|
||||
int* buffer;
|
||||
};
|
||||
|
||||
void runTest()
|
||||
{
|
||||
beginTestCase ("AbstractFifo");
|
||||
|
||||
int buffer [5000];
|
||||
AbstractFifo fifo (numElementsInArray (buffer));
|
||||
|
||||
WriteThread writer (fifo, buffer);
|
||||
|
||||
int n = 0;
|
||||
Random r;
|
||||
|
||||
bool failed = false;
|
||||
|
||||
for (int count = 100000; --count >= 0;)
|
||||
{
|
||||
int num = r.nextInt (6000) + 1;
|
||||
|
||||
int start1, size1, start2, size2;
|
||||
fifo.prepareToRead (num, start1, size1, start2, size2);
|
||||
|
||||
if (! (size1 >= 0 && size2 >= 0)
|
||||
&& (size1 == 0 || (start1 >= 0 && start1 < fifo.getTotalSize()))
|
||||
&& (size2 == 0 || (start2 >= 0 && start2 < fifo.getTotalSize())))
|
||||
{
|
||||
expect (false, "prepareToRead returned negative values");
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < size1; ++i)
|
||||
failed = (buffer [start1 + i] != n++) || failed;
|
||||
|
||||
for (int i = 0; i < size2; ++i)
|
||||
failed = (buffer [start2 + i] != n++) || failed;
|
||||
|
||||
if (failed)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
fifo.finishedRead (size1 + size2);
|
||||
}
|
||||
|
||||
expect (! failed, "read values were incorrect");
|
||||
}
|
||||
};
|
||||
|
||||
static AbstractFifoTests abstractFifoTests;
|
||||
@@ -1,212 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_ABSTRACTFIFO_H_INCLUDED
|
||||
#define BEAST_ABSTRACTFIFO_H_INCLUDED
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Encapsulates the logic required to implement a lock-free FIFO.
|
||||
|
||||
This class handles the logic needed when building a single-reader,
|
||||
single-writer FIFO.
|
||||
|
||||
It doesn't actually hold any data itself, but your FIFO class can use one of
|
||||
these to manage its position and status when reading or writing to it.
|
||||
|
||||
To use it, you can call prepareToWrite() to determine the position within
|
||||
your own buffer that an incoming block of data should be stored, and
|
||||
prepareToRead() to find out when the next outgoing block should be read from.
|
||||
|
||||
e.g.
|
||||
@code
|
||||
class MyFifo
|
||||
{
|
||||
public:
|
||||
MyFifo() : abstractFifo (1024)
|
||||
{
|
||||
}
|
||||
|
||||
void addToFifo (const int* someData, int numItems)
|
||||
{
|
||||
int start1, size1, start2, size2;
|
||||
abstractFifo.prepareToWrite (numItems, start1, size1, start2, size2);
|
||||
|
||||
if (size1 > 0)
|
||||
copySomeData (myBuffer + start1, someData, size1);
|
||||
|
||||
if (size2 > 0)
|
||||
copySomeData (myBuffer + start2, someData + size1, size2);
|
||||
|
||||
abstractFifo.finishedWrite (size1 + size2);
|
||||
}
|
||||
|
||||
void readFromFifo (int* someData, int numItems)
|
||||
{
|
||||
int start1, size1, start2, size2;
|
||||
abstractFifo.prepareToRead (numSamples, start1, size1, start2, size2);
|
||||
|
||||
if (size1 > 0)
|
||||
copySomeData (someData, myBuffer + start1, size1);
|
||||
|
||||
if (size2 > 0)
|
||||
copySomeData (someData + size1, myBuffer + start2, size2);
|
||||
|
||||
abstractFifo.finishedRead (size1 + size2);
|
||||
}
|
||||
|
||||
private:
|
||||
AbstractFifo abstractFifo;
|
||||
int myBuffer [1024];
|
||||
};
|
||||
@endcode
|
||||
*/
|
||||
class BEAST_API AbstractFifo : LeakChecked <AbstractFifo>, public Uncopyable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a FIFO to manage a buffer with the specified capacity. */
|
||||
AbstractFifo (int capacity) noexcept;
|
||||
|
||||
/** Destructor */
|
||||
~AbstractFifo();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the total size of the buffer being managed. */
|
||||
int getTotalSize() const noexcept;
|
||||
|
||||
/** Returns the number of items that can currently be added to the buffer without it overflowing. */
|
||||
int getFreeSpace() const noexcept;
|
||||
|
||||
/** Returns the number of items that can currently be read from the buffer. */
|
||||
int getNumReady() const noexcept;
|
||||
|
||||
/** Clears the buffer positions, so that it appears empty. */
|
||||
void reset() noexcept;
|
||||
|
||||
/** Changes the buffer's total size.
|
||||
Note that this isn't thread-safe, so don't call it if there's any danger that it
|
||||
might overlap with a call to any other method in this class!
|
||||
*/
|
||||
void setTotalSize (int newSize) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the location within the buffer at which an incoming block of data should be written.
|
||||
|
||||
Because the section of data that you want to add to the buffer may overlap the end
|
||||
and wrap around to the start, two blocks within your buffer are returned, and you
|
||||
should copy your data into the first one, with any remaining data spilling over into
|
||||
the second.
|
||||
|
||||
If the number of items you ask for is too large to fit within the buffer's free space, then
|
||||
blockSize1 + blockSize2 may add up to a lower value than numToWrite. If this happens, you
|
||||
may decide to keep waiting and re-trying the method until there's enough space available.
|
||||
|
||||
After calling this method, if you choose to write your data into the blocks returned, you
|
||||
must call finishedWrite() to tell the FIFO how much data you actually added.
|
||||
|
||||
e.g.
|
||||
@code
|
||||
void addToFifo (const int* someData, int numItems)
|
||||
{
|
||||
int start1, size1, start2, size2;
|
||||
prepareToWrite (numItems, start1, size1, start2, size2);
|
||||
|
||||
if (size1 > 0)
|
||||
copySomeData (myBuffer + start1, someData, size1);
|
||||
|
||||
if (size2 > 0)
|
||||
copySomeData (myBuffer + start2, someData + size1, size2);
|
||||
|
||||
finishedWrite (size1 + size2);
|
||||
}
|
||||
@endcode
|
||||
|
||||
@param numToWrite indicates how many items you'd like to add to the buffer
|
||||
@param startIndex1 on exit, this will contain the start index in your buffer at which your data should be written
|
||||
@param blockSize1 on exit, this indicates how many items can be written to the block starting at startIndex1
|
||||
@param startIndex2 on exit, this will contain the start index in your buffer at which any data that didn't fit into
|
||||
the first block should be written
|
||||
@param blockSize2 on exit, this indicates how many items can be written to the block starting at startIndex2
|
||||
@see finishedWrite
|
||||
*/
|
||||
void prepareToWrite (int numToWrite, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept;
|
||||
|
||||
/** Called after writing from the FIFO, to indicate that this many items have been added.
|
||||
@see prepareToWrite
|
||||
*/
|
||||
void finishedWrite (int numWritten) noexcept;
|
||||
|
||||
/** Returns the location within the buffer from which the next block of data should be read.
|
||||
|
||||
Because the section of data that you want to read from the buffer may overlap the end
|
||||
and wrap around to the start, two blocks within your buffer are returned, and you
|
||||
should read from both of them.
|
||||
|
||||
If the number of items you ask for is greater than the amount of data available, then
|
||||
blockSize1 + blockSize2 may add up to a lower value than numWanted. If this happens, you
|
||||
may decide to keep waiting and re-trying the method until there's enough data available.
|
||||
|
||||
After calling this method, if you choose to read the data, you must call finishedRead() to
|
||||
tell the FIFO how much data you have consumed.
|
||||
|
||||
e.g.
|
||||
@code
|
||||
void readFromFifo (int* someData, int numItems)
|
||||
{
|
||||
int start1, size1, start2, size2;
|
||||
prepareToRead (numSamples, start1, size1, start2, size2);
|
||||
|
||||
if (size1 > 0)
|
||||
copySomeData (someData, myBuffer + start1, size1);
|
||||
|
||||
if (size2 > 0)
|
||||
copySomeData (someData + size1, myBuffer + start2, size2);
|
||||
|
||||
finishedRead (size1 + size2);
|
||||
}
|
||||
@endcode
|
||||
|
||||
@param numWanted indicates how many items you'd like to add to the buffer
|
||||
@param startIndex1 on exit, this will contain the start index in your buffer at which your data should be written
|
||||
@param blockSize1 on exit, this indicates how many items can be written to the block starting at startIndex1
|
||||
@param startIndex2 on exit, this will contain the start index in your buffer at which any data that didn't fit into
|
||||
the first block should be written
|
||||
@param blockSize2 on exit, this indicates how many items can be written to the block starting at startIndex2
|
||||
@see finishedRead
|
||||
*/
|
||||
void prepareToRead (int numWanted, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept;
|
||||
|
||||
/** Called after reading from the FIFO, to indicate that this many items have now been consumed.
|
||||
@see prepareToRead
|
||||
*/
|
||||
void finishedRead (int numRead) noexcept;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
int bufferSize;
|
||||
Atomic <int> validStart, validEnd;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,856 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_SHAREDOBJECTARRAY_H_INCLUDED
|
||||
#define BEAST_SHAREDOBJECTARRAY_H_INCLUDED
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Holds a list of objects derived from SharedObject.
|
||||
|
||||
A SharedObjectArray holds objects derived from SharedObject,
|
||||
and takes care of incrementing and decrementing their ref counts when they
|
||||
are added and removed from the array.
|
||||
|
||||
To make all the array's methods thread-safe, pass in "CriticalSection" as the templated
|
||||
TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection.
|
||||
|
||||
@see Array, OwnedArray, StringArray
|
||||
*/
|
||||
template <class ObjectClass, class TypeOfCriticalSectionToUse = DummyCriticalSection>
|
||||
class SharedObjectArray
|
||||
{
|
||||
public:
|
||||
typedef SharedPtr<ObjectClass> ObjectClassPtr;
|
||||
|
||||
//==============================================================================
|
||||
/** Creates an empty array.
|
||||
@see SharedObject, Array, OwnedArray
|
||||
*/
|
||||
SharedObjectArray() noexcept
|
||||
: numUsed (0)
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates a copy of another array */
|
||||
SharedObjectArray (const SharedObjectArray& other) noexcept
|
||||
{
|
||||
const ScopedLockType lock (other.getLock());
|
||||
numUsed = other.size();
|
||||
data.setAllocatedSize (numUsed);
|
||||
memcpy (data.elements, other.getRawDataPointer(), numUsed * sizeof (ObjectClass*));
|
||||
|
||||
for (int i = numUsed; --i >= 0;)
|
||||
if (ObjectClass* o = data.elements[i])
|
||||
o->incReferenceCount();
|
||||
}
|
||||
|
||||
/** Creates a copy of another array */
|
||||
template <class OtherObjectClass, class OtherCriticalSection>
|
||||
SharedObjectArray (const SharedObjectArray<OtherObjectClass, OtherCriticalSection>& other) noexcept
|
||||
{
|
||||
const typename SharedObjectArray<OtherObjectClass, OtherCriticalSection>::ScopedLockType lock (other.getLock());
|
||||
numUsed = other.size();
|
||||
data.setAllocatedSize (numUsed);
|
||||
memcpy (data.elements, other.getRawDataPointer(), numUsed * sizeof (ObjectClass*));
|
||||
|
||||
for (int i = numUsed; --i >= 0;)
|
||||
if (ObjectClass* o = data.elements[i])
|
||||
o->incReferenceCount();
|
||||
}
|
||||
|
||||
/** Copies another array into this one.
|
||||
Any existing objects in this array will first be released.
|
||||
*/
|
||||
SharedObjectArray& operator= (const SharedObjectArray& other) noexcept
|
||||
{
|
||||
SharedObjectArray otherCopy (other);
|
||||
swapWith (otherCopy);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Copies another array into this one.
|
||||
Any existing objects in this array will first be released.
|
||||
*/
|
||||
template <class OtherObjectClass>
|
||||
SharedObjectArray<ObjectClass, TypeOfCriticalSectionToUse>& operator= (const SharedObjectArray<OtherObjectClass, TypeOfCriticalSectionToUse>& other) noexcept
|
||||
{
|
||||
SharedObjectArray<ObjectClass, TypeOfCriticalSectionToUse> otherCopy (other);
|
||||
swapWith (otherCopy);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Destructor.
|
||||
Any objects in the array will be released, and may be deleted if not referenced from elsewhere.
|
||||
*/
|
||||
~SharedObjectArray()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Removes all objects from the array.
|
||||
|
||||
Any objects in the array that are not referenced from elsewhere will be deleted.
|
||||
*/
|
||||
void clear()
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
while (numUsed > 0)
|
||||
if (ObjectClass* o = data.elements [--numUsed])
|
||||
o->decReferenceCount();
|
||||
|
||||
bassert (numUsed == 0);
|
||||
data.setAllocatedSize (0);
|
||||
}
|
||||
|
||||
/** Returns the current number of objects in the array. */
|
||||
inline int size() const noexcept
|
||||
{
|
||||
return numUsed;
|
||||
}
|
||||
|
||||
/** Returns a pointer to the object at this index in the array.
|
||||
|
||||
If the index is out-of-range, this will return a null pointer, (and
|
||||
it could be null anyway, because it's ok for the array to hold null
|
||||
pointers as well as objects).
|
||||
|
||||
@see getUnchecked
|
||||
*/
|
||||
inline ObjectClassPtr operator[] (const int index) const noexcept
|
||||
{
|
||||
return getObjectPointer (index);
|
||||
}
|
||||
|
||||
/** Returns a pointer to the object at this index in the array, without checking
|
||||
whether the index is in-range.
|
||||
|
||||
This is a faster and less safe version of operator[] which doesn't check the index passed in, so
|
||||
it can be used when you're sure the index is always going to be legal.
|
||||
*/
|
||||
inline ObjectClassPtr getUnchecked (const int index) const noexcept
|
||||
{
|
||||
return getObjectPointerUnchecked (index);
|
||||
}
|
||||
|
||||
/** Returns a raw pointer to the object at this index in the array.
|
||||
|
||||
If the index is out-of-range, this will return a null pointer, (and
|
||||
it could be null anyway, because it's ok for the array to hold null
|
||||
pointers as well as objects).
|
||||
|
||||
@see getUnchecked
|
||||
*/
|
||||
inline ObjectClass* getObjectPointer (const int index) const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
return isPositiveAndBelow (index, numUsed) ? data.elements [index]
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
/** Returns a raw pointer to the object at this index in the array, without checking
|
||||
whether the index is in-range.
|
||||
*/
|
||||
inline ObjectClass* getObjectPointerUnchecked (const int index) const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
bassert (isPositiveAndBelow (index, numUsed));
|
||||
return data.elements [index];
|
||||
}
|
||||
|
||||
/** Returns a pointer to the first object in the array.
|
||||
|
||||
This will return a null pointer if the array's empty.
|
||||
@see getLast
|
||||
*/
|
||||
inline ObjectClassPtr getFirst() const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
return numUsed > 0 ? data.elements [0]
|
||||
: static_cast <ObjectClass*> (nullptr);
|
||||
}
|
||||
|
||||
/** Returns a pointer to the last object in the array.
|
||||
|
||||
This will return a null pointer if the array's empty.
|
||||
@see getFirst
|
||||
*/
|
||||
inline ObjectClassPtr getLast() const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
return numUsed > 0 ? data.elements [numUsed - 1]
|
||||
: static_cast <ObjectClass*> (nullptr);
|
||||
}
|
||||
|
||||
/** Returns a pointer to the actual array data.
|
||||
This pointer will only be valid until the next time a non-const method
|
||||
is called on the array.
|
||||
*/
|
||||
inline ObjectClass** getRawDataPointer() const noexcept
|
||||
{
|
||||
return data.elements;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a pointer to the first element in the array.
|
||||
This method is provided for compatibility with standard C++ iteration mechanisms.
|
||||
*/
|
||||
inline ObjectClass** begin() const noexcept
|
||||
{
|
||||
return data.elements;
|
||||
}
|
||||
|
||||
/** Returns a pointer to the element which follows the last element in the array.
|
||||
This method is provided for compatibility with standard C++ iteration mechanisms.
|
||||
*/
|
||||
inline ObjectClass** end() const noexcept
|
||||
{
|
||||
return data.elements + numUsed;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Finds the index of the first occurrence of an object in the array.
|
||||
|
||||
@param objectToLookFor the object to look for
|
||||
@returns the index at which the object was found, or -1 if it's not found
|
||||
*/
|
||||
int indexOf (const ObjectClass* const objectToLookFor) const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
ObjectClass** e = data.elements.getData();
|
||||
ObjectClass** const endPointer = e + numUsed;
|
||||
|
||||
while (e != endPointer)
|
||||
{
|
||||
if (objectToLookFor == *e)
|
||||
return static_cast <int> (e - data.elements.getData());
|
||||
|
||||
++e;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Returns true if the array contains a specified object.
|
||||
|
||||
@param objectToLookFor the object to look for
|
||||
@returns true if the object is in the array
|
||||
*/
|
||||
bool contains (const ObjectClass* const objectToLookFor) const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
ObjectClass** e = data.elements.getData();
|
||||
ObjectClass** const endPointer = e + numUsed;
|
||||
|
||||
while (e != endPointer)
|
||||
{
|
||||
if (objectToLookFor == *e)
|
||||
return true;
|
||||
|
||||
++e;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Appends a new object to the end of the array.
|
||||
|
||||
This will increase the new object's reference count.
|
||||
|
||||
@param newObject the new object to add to the array
|
||||
@see set, insert, addIfNotAlreadyThere, addSorted, addArray
|
||||
*/
|
||||
ObjectClass* add (ObjectClass* const newObject) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.ensureAllocatedSize (numUsed + 1);
|
||||
bassert (data.elements != nullptr);
|
||||
data.elements [numUsed++] = newObject;
|
||||
|
||||
if (newObject != nullptr)
|
||||
newObject->incReferenceCount();
|
||||
|
||||
return newObject;
|
||||
}
|
||||
|
||||
/** Inserts a new object into the array at the given index.
|
||||
|
||||
If the index is less than 0 or greater than the size of the array, the
|
||||
element will be added to the end of the array.
|
||||
Otherwise, it will be inserted into the array, moving all the later elements
|
||||
along to make room.
|
||||
|
||||
This will increase the new object's reference count.
|
||||
|
||||
@param indexToInsertAt the index at which the new element should be inserted
|
||||
@param newObject the new object to add to the array
|
||||
@see add, addSorted, addIfNotAlreadyThere, set
|
||||
*/
|
||||
ObjectClass* insert (int indexToInsertAt,
|
||||
ObjectClass* const newObject) noexcept
|
||||
{
|
||||
if (indexToInsertAt >= 0)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (indexToInsertAt > numUsed)
|
||||
indexToInsertAt = numUsed;
|
||||
|
||||
data.ensureAllocatedSize (numUsed + 1);
|
||||
bassert (data.elements != nullptr);
|
||||
|
||||
ObjectClass** const e = data.elements + indexToInsertAt;
|
||||
const int numToMove = numUsed - indexToInsertAt;
|
||||
|
||||
if (numToMove > 0)
|
||||
memmove (e + 1, e, sizeof (ObjectClass*) * (size_t) numToMove);
|
||||
|
||||
*e = newObject;
|
||||
|
||||
if (newObject != nullptr)
|
||||
newObject->incReferenceCount();
|
||||
|
||||
++numUsed;
|
||||
|
||||
return newObject;
|
||||
}
|
||||
else
|
||||
{
|
||||
return add (newObject);
|
||||
}
|
||||
}
|
||||
|
||||
/** Appends a new object at the end of the array as long as the array doesn't
|
||||
already contain it.
|
||||
|
||||
If the array already contains a matching object, nothing will be done.
|
||||
|
||||
@param newObject the new object to add to the array
|
||||
*/
|
||||
void addIfNotAlreadyThere (ObjectClass* const newObject) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
if (! contains (newObject))
|
||||
add (newObject);
|
||||
}
|
||||
|
||||
/** Replaces an object in the array with a different one.
|
||||
|
||||
If the index is less than zero, this method does nothing.
|
||||
If the index is beyond the end of the array, the new object is added to the end of the array.
|
||||
|
||||
The object being added has its reference count increased, and if it's replacing
|
||||
another object, then that one has its reference count decreased, and may be deleted.
|
||||
|
||||
@param indexToChange the index whose value you want to change
|
||||
@param newObject the new value to set for this index.
|
||||
@see add, insert, remove
|
||||
*/
|
||||
void set (const int indexToChange,
|
||||
ObjectClass* const newObject)
|
||||
{
|
||||
if (indexToChange >= 0)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (newObject != nullptr)
|
||||
newObject->incReferenceCount();
|
||||
|
||||
if (indexToChange < numUsed)
|
||||
{
|
||||
if (ObjectClass* o = data.elements [indexToChange])
|
||||
o->decReferenceCount();
|
||||
|
||||
data.elements [indexToChange] = newObject;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.ensureAllocatedSize (numUsed + 1);
|
||||
bassert (data.elements != nullptr);
|
||||
data.elements [numUsed++] = newObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Adds elements from another array to the end of this array.
|
||||
|
||||
@param arrayToAddFrom the array from which to copy the elements
|
||||
@param startIndex the first element of the other array to start copying from
|
||||
@param numElementsToAdd how many elements to add from the other array. If this
|
||||
value is negative or greater than the number of available elements,
|
||||
all available elements will be copied.
|
||||
@see add
|
||||
*/
|
||||
void addArray (const SharedObjectArray<ObjectClass, TypeOfCriticalSectionToUse>& arrayToAddFrom,
|
||||
int startIndex = 0,
|
||||
int numElementsToAdd = -1) noexcept
|
||||
{
|
||||
const ScopedLockType lock1 (arrayToAddFrom.getLock());
|
||||
|
||||
{
|
||||
const ScopedLockType lock2 (getLock());
|
||||
|
||||
if (startIndex < 0)
|
||||
{
|
||||
bassertfalse;
|
||||
startIndex = 0;
|
||||
}
|
||||
|
||||
if (numElementsToAdd < 0 || startIndex + numElementsToAdd > arrayToAddFrom.size())
|
||||
numElementsToAdd = arrayToAddFrom.size() - startIndex;
|
||||
|
||||
if (numElementsToAdd > 0)
|
||||
{
|
||||
data.ensureAllocatedSize (numUsed + numElementsToAdd);
|
||||
|
||||
while (--numElementsToAdd >= 0)
|
||||
add (arrayToAddFrom.getUnchecked (startIndex++));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Inserts a new object into the array assuming that the array is sorted.
|
||||
|
||||
This will use a comparator to find the position at which the new object
|
||||
should go. If the array isn't sorted, the behaviour of this
|
||||
method will be unpredictable.
|
||||
|
||||
@param comparator the comparator object to use to compare the elements - see the
|
||||
sort() method for details about this object's form
|
||||
@param newObject the new object to insert to the array
|
||||
@returns the index at which the new object was added
|
||||
@see add, sort
|
||||
*/
|
||||
template <class ElementComparator>
|
||||
int addSorted (ElementComparator& comparator, ObjectClass* newObject) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
const int index = findInsertIndexInSortedArray (comparator, data.elements.getData(), newObject, 0, numUsed);
|
||||
insert (index, newObject);
|
||||
return index;
|
||||
}
|
||||
|
||||
/** Inserts or replaces an object in the array, assuming it is sorted.
|
||||
|
||||
This is similar to addSorted, but if a matching element already exists, then it will be
|
||||
replaced by the new one, rather than the new one being added as well.
|
||||
*/
|
||||
template <class ElementComparator>
|
||||
void addOrReplaceSorted (ElementComparator& comparator,
|
||||
ObjectClass* newObject) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
const int index = findInsertIndexInSortedArray (comparator, data.elements.getData(), newObject, 0, numUsed);
|
||||
|
||||
if (index > 0 && comparator.compareElements (newObject, data.elements [index - 1]) == 0)
|
||||
set (index - 1, newObject); // replace an existing object that matches
|
||||
else
|
||||
insert (index, newObject); // no match, so insert the new one
|
||||
}
|
||||
|
||||
/** Finds the index of an object in the array, assuming that the array is sorted.
|
||||
|
||||
This will use a comparator to do a binary-chop to find the index of the given
|
||||
element, if it exists. If the array isn't sorted, the behaviour of this
|
||||
method will be unpredictable.
|
||||
|
||||
@param comparator the comparator to use to compare the elements - see the sort()
|
||||
method for details about the form this object should take
|
||||
@param objectToLookFor the object to search for
|
||||
@returns the index of the element, or -1 if it's not found
|
||||
@see addSorted, sort
|
||||
*/
|
||||
template <class ElementComparator>
|
||||
int indexOfSorted (ElementComparator& comparator,
|
||||
const ObjectClass* const objectToLookFor) const noexcept
|
||||
{
|
||||
(void) comparator;
|
||||
const ScopedLockType lock (getLock());
|
||||
int s = 0, e = numUsed;
|
||||
|
||||
while (s < e)
|
||||
{
|
||||
if (comparator.compareElements (objectToLookFor, data.elements [s]) == 0)
|
||||
return s;
|
||||
|
||||
const int halfway = (s + e) / 2;
|
||||
if (halfway == s)
|
||||
break;
|
||||
|
||||
if (comparator.compareElements (objectToLookFor, data.elements [halfway]) >= 0)
|
||||
s = halfway;
|
||||
else
|
||||
e = halfway;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Removes an object from the array.
|
||||
|
||||
This will remove the object at a given index and move back all the
|
||||
subsequent objects to close the gap.
|
||||
|
||||
If the index passed in is out-of-range, nothing will happen.
|
||||
|
||||
The object that is removed will have its reference count decreased,
|
||||
and may be deleted if not referenced from elsewhere.
|
||||
|
||||
@param indexToRemove the index of the element to remove
|
||||
@see removeObject, removeRange
|
||||
*/
|
||||
void remove (const int indexToRemove)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (indexToRemove, numUsed))
|
||||
{
|
||||
ObjectClass** const e = data.elements + indexToRemove;
|
||||
|
||||
if (ObjectClass* o = *e)
|
||||
o->decReferenceCount();
|
||||
|
||||
--numUsed;
|
||||
const int numberToShift = numUsed - indexToRemove;
|
||||
|
||||
if (numberToShift > 0)
|
||||
memmove (e, e + 1, sizeof (ObjectClass*) * (size_t) numberToShift);
|
||||
|
||||
if ((numUsed << 1) < data.numAllocated)
|
||||
minimiseStorageOverheads();
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes and returns an object from the array.
|
||||
|
||||
This will remove the object at a given index and return it, moving back all
|
||||
the subsequent objects to close the gap. If the index passed in is out-of-range,
|
||||
nothing will happen and a null pointer will be returned.
|
||||
|
||||
@param indexToRemove the index of the element to remove
|
||||
@see remove, removeObject, removeRange
|
||||
*/
|
||||
ObjectClassPtr removeAndReturn (const int indexToRemove)
|
||||
{
|
||||
ObjectClassPtr removedItem;
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (indexToRemove, numUsed))
|
||||
{
|
||||
ObjectClass** const e = data.elements + indexToRemove;
|
||||
|
||||
if (ObjectClass* o = *e)
|
||||
{
|
||||
removedItem = o;
|
||||
o->decReferenceCount();
|
||||
}
|
||||
|
||||
--numUsed;
|
||||
const int numberToShift = numUsed - indexToRemove;
|
||||
|
||||
if (numberToShift > 0)
|
||||
memmove (e, e + 1, sizeof (ObjectClass*) * (size_t) numberToShift);
|
||||
|
||||
if ((numUsed << 1) < data.numAllocated)
|
||||
minimiseStorageOverheads();
|
||||
}
|
||||
|
||||
return removedItem;
|
||||
}
|
||||
|
||||
/** Removes the first occurrence of a specified object from the array.
|
||||
|
||||
If the item isn't found, no action is taken. If it is found, it is
|
||||
removed and has its reference count decreased.
|
||||
|
||||
@param objectToRemove the object to try to remove
|
||||
@see remove, removeRange
|
||||
*/
|
||||
void removeObject (ObjectClass* const objectToRemove)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
remove (indexOf (objectToRemove));
|
||||
}
|
||||
|
||||
/** Removes a range of objects from the array.
|
||||
|
||||
This will remove a set of objects, starting from the given index,
|
||||
and move any subsequent elements down to close the gap.
|
||||
|
||||
If the range extends beyond the bounds of the array, it will
|
||||
be safely clipped to the size of the array.
|
||||
|
||||
The objects that are removed will have their reference counts decreased,
|
||||
and may be deleted if not referenced from elsewhere.
|
||||
|
||||
@param startIndex the index of the first object to remove
|
||||
@param numberToRemove how many objects should be removed
|
||||
@see remove, removeObject
|
||||
*/
|
||||
void removeRange (const int startIndex,
|
||||
const int numberToRemove)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
const int start = blimit (0, numUsed, startIndex);
|
||||
const int endIndex = blimit (0, numUsed, startIndex + numberToRemove);
|
||||
|
||||
if (endIndex > start)
|
||||
{
|
||||
int i;
|
||||
for (i = start; i < endIndex; ++i)
|
||||
{
|
||||
if (ObjectClass* o = data.elements[i])
|
||||
{
|
||||
o->decReferenceCount();
|
||||
data.elements[i] = nullptr; // (in case one of the destructors accesses this array and hits a dangling pointer)
|
||||
}
|
||||
}
|
||||
|
||||
const int rangeSize = endIndex - start;
|
||||
ObjectClass** e = data.elements + start;
|
||||
i = numUsed - endIndex;
|
||||
numUsed -= rangeSize;
|
||||
|
||||
while (--i >= 0)
|
||||
{
|
||||
*e = e [rangeSize];
|
||||
++e;
|
||||
}
|
||||
|
||||
if ((numUsed << 1) < data.numAllocated)
|
||||
minimiseStorageOverheads();
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes the last n objects from the array.
|
||||
|
||||
The objects that are removed will have their reference counts decreased,
|
||||
and may be deleted if not referenced from elsewhere.
|
||||
|
||||
@param howManyToRemove how many objects to remove from the end of the array
|
||||
@see remove, removeObject, removeRange
|
||||
*/
|
||||
void removeLast (int howManyToRemove = 1)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (howManyToRemove > numUsed)
|
||||
howManyToRemove = numUsed;
|
||||
|
||||
while (--howManyToRemove >= 0)
|
||||
remove (numUsed - 1);
|
||||
}
|
||||
|
||||
/** Swaps a pair of objects in the array.
|
||||
|
||||
If either of the indexes passed in is out-of-range, nothing will happen,
|
||||
otherwise the two objects at these positions will be exchanged.
|
||||
*/
|
||||
void swap (const int index1,
|
||||
const int index2) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (index1, numUsed)
|
||||
&& isPositiveAndBelow (index2, numUsed))
|
||||
{
|
||||
std::swap (data.elements [index1],
|
||||
data.elements [index2]);
|
||||
}
|
||||
}
|
||||
|
||||
/** Moves one of the objects to a different position.
|
||||
|
||||
This will move the object to a specified index, shuffling along
|
||||
any intervening elements as required.
|
||||
|
||||
So for example, if you have the array { 0, 1, 2, 3, 4, 5 } then calling
|
||||
move (2, 4) would result in { 0, 1, 3, 4, 2, 5 }.
|
||||
|
||||
@param currentIndex the index of the object to be moved. If this isn't a
|
||||
valid index, then nothing will be done
|
||||
@param newIndex the index at which you'd like this object to end up. If this
|
||||
is less than zero, it will be moved to the end of the array
|
||||
*/
|
||||
void move (const int currentIndex,
|
||||
int newIndex) noexcept
|
||||
{
|
||||
if (currentIndex != newIndex)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (currentIndex, numUsed))
|
||||
{
|
||||
if (! isPositiveAndBelow (newIndex, numUsed))
|
||||
newIndex = numUsed - 1;
|
||||
|
||||
ObjectClass* const value = data.elements [currentIndex];
|
||||
|
||||
if (newIndex > currentIndex)
|
||||
{
|
||||
memmove (data.elements + currentIndex,
|
||||
data.elements + currentIndex + 1,
|
||||
sizeof (ObjectClass*) * (size_t) (newIndex - currentIndex));
|
||||
}
|
||||
else
|
||||
{
|
||||
memmove (data.elements + newIndex + 1,
|
||||
data.elements + newIndex,
|
||||
sizeof (ObjectClass*) * (size_t) (currentIndex - newIndex));
|
||||
}
|
||||
|
||||
data.elements [newIndex] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** This swaps the contents of this array with those of another array.
|
||||
|
||||
If you need to exchange two arrays, this is vastly quicker than using copy-by-value
|
||||
because it just swaps their internal pointers.
|
||||
*/
|
||||
template <class OtherArrayType>
|
||||
void swapWith (OtherArrayType& otherArray) noexcept
|
||||
{
|
||||
const ScopedLockType lock1 (getLock());
|
||||
const typename OtherArrayType::ScopedLockType lock2 (otherArray.getLock());
|
||||
|
||||
data.swapWith (otherArray.data);
|
||||
std::swap (numUsed, otherArray.numUsed);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Compares this array to another one.
|
||||
|
||||
@returns true only if the other array contains the same objects in the same order
|
||||
*/
|
||||
bool operator== (const SharedObjectArray& other) const noexcept
|
||||
{
|
||||
const ScopedLockType lock2 (other.getLock());
|
||||
const ScopedLockType lock1 (getLock());
|
||||
|
||||
if (numUsed != other.numUsed)
|
||||
return false;
|
||||
|
||||
for (int i = numUsed; --i >= 0;)
|
||||
if (data.elements [i] != other.data.elements [i])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Compares this array to another one.
|
||||
|
||||
@see operator==
|
||||
*/
|
||||
bool operator!= (const SharedObjectArray<ObjectClass, TypeOfCriticalSectionToUse>& other) const noexcept
|
||||
{
|
||||
return ! operator== (other);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Sorts the elements in the array.
|
||||
|
||||
This will use a comparator object to sort the elements into order. The object
|
||||
passed must have a method of the form:
|
||||
@code
|
||||
int compareElements (ElementType first, ElementType second);
|
||||
@endcode
|
||||
|
||||
..and this method must return:
|
||||
- a value of < 0 if the first comes before the second
|
||||
- a value of 0 if the two objects are equivalent
|
||||
- a value of > 0 if the second comes before the first
|
||||
|
||||
To improve performance, the compareElements() method can be declared as static or const.
|
||||
|
||||
@param comparator the comparator to use for comparing elements.
|
||||
@param retainOrderOfEquivalentItems if this is true, then items
|
||||
which the comparator says are equivalent will be
|
||||
kept in the order in which they currently appear
|
||||
in the array. This is slower to perform, but may
|
||||
be important in some cases. If it's false, a faster
|
||||
algorithm is used, but equivalent elements may be
|
||||
rearranged.
|
||||
|
||||
@see sortArray
|
||||
*/
|
||||
template <class ElementComparator>
|
||||
void sort (ElementComparator& comparator,
|
||||
const bool retainOrderOfEquivalentItems = false) const noexcept
|
||||
{
|
||||
(void) comparator; // if you pass in an object with a static compareElements() method, this
|
||||
// avoids getting warning messages about the parameter being unused
|
||||
|
||||
const ScopedLockType lock (getLock());
|
||||
sortArray (comparator, data.elements.getData(), 0, size() - 1, retainOrderOfEquivalentItems);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Reduces the amount of storage being used by the array.
|
||||
|
||||
Arrays typically allocate slightly more storage than they need, and after
|
||||
removing elements, they may have quite a lot of unused space allocated.
|
||||
This method will reduce the amount of allocated storage to a minimum.
|
||||
*/
|
||||
void minimiseStorageOverheads() noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.shrinkToNoMoreThan (numUsed);
|
||||
}
|
||||
|
||||
/** Increases the array's internal storage to hold a minimum number of elements.
|
||||
|
||||
Calling this before adding a large known number of elements means that
|
||||
the array won't have to keep dynamically resizing itself as the elements
|
||||
are added, and it'll therefore be more efficient.
|
||||
*/
|
||||
void ensureStorageAllocated (const int minNumElements)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.ensureAllocatedSize (minNumElements);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the CriticalSection that locks this array.
|
||||
To lock, you can call getLock().enter() and getLock().exit(), or preferably use
|
||||
an object of ScopedLockType as an RAII lock for it.
|
||||
*/
|
||||
inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return data; }
|
||||
|
||||
/** Returns the type of scoped lock to use for locking this array */
|
||||
typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
ArrayAllocationBase <ObjectClass*, TypeOfCriticalSectionToUse> data;
|
||||
int numUsed;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,492 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_SORTEDSET_H_INCLUDED
|
||||
#define BEAST_SORTEDSET_H_INCLUDED
|
||||
|
||||
#if BEAST_MSVC
|
||||
#pragma warning (push)
|
||||
#pragma warning (disable: 4512)
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Holds a set of unique primitive objects, such as ints or doubles.
|
||||
|
||||
A set can only hold one item with a given value, so if for example it's a
|
||||
set of integers, attempting to add the same integer twice will do nothing
|
||||
the second time.
|
||||
|
||||
Internally, the list of items is kept sorted (which means that whatever
|
||||
kind of primitive type is used must support the ==, <, >, <= and >= operators
|
||||
to determine the order), and searching the set for known values is very fast
|
||||
because it uses a binary-chop method.
|
||||
|
||||
Note that if you're using a class or struct as the element type, it must be
|
||||
capable of being copied or moved with a straightforward memcpy, rather than
|
||||
needing construction and destruction code.
|
||||
|
||||
To make all the set's methods thread-safe, pass in "CriticalSection" as the templated
|
||||
TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection.
|
||||
|
||||
@see Array, OwnedArray, SharedObjectArray, StringArray, CriticalSection
|
||||
*/
|
||||
template <class ElementType, class TypeOfCriticalSectionToUse = DummyCriticalSection>
|
||||
class SortedSet
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty set. */
|
||||
SortedSet() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates a copy of another set.
|
||||
@param other the set to copy
|
||||
*/
|
||||
SortedSet (const SortedSet& other)
|
||||
: data (other.data)
|
||||
{
|
||||
}
|
||||
|
||||
/** Destructor. */
|
||||
~SortedSet() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
/** Copies another set over this one.
|
||||
@param other the set to copy
|
||||
*/
|
||||
SortedSet& operator= (const SortedSet& other) noexcept
|
||||
{
|
||||
data = other.data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Compares this set to another one.
|
||||
Two sets are considered equal if they both contain the same set of elements.
|
||||
@param other the other set to compare with
|
||||
*/
|
||||
bool operator== (const SortedSet<ElementType>& other) const noexcept
|
||||
{
|
||||
return data == other.data;
|
||||
}
|
||||
|
||||
/** Compares this set to another one.
|
||||
Two sets are considered equal if they both contain the same set of elements.
|
||||
@param other the other set to compare with
|
||||
*/
|
||||
bool operator!= (const SortedSet<ElementType>& other) const noexcept
|
||||
{
|
||||
return ! operator== (other);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Removes all elements from the set.
|
||||
|
||||
This will remove all the elements, and free any storage that the set is
|
||||
using. To clear it without freeing the storage, use the clearQuick()
|
||||
method instead.
|
||||
|
||||
@see clearQuick
|
||||
*/
|
||||
void clear() noexcept
|
||||
{
|
||||
data.clear();
|
||||
}
|
||||
|
||||
/** Removes all elements from the set without freeing the array's allocated storage.
|
||||
|
||||
@see clear
|
||||
*/
|
||||
void clearQuick() noexcept
|
||||
{
|
||||
data.clearQuick();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the current number of elements in the set.
|
||||
*/
|
||||
inline int size() const noexcept
|
||||
{
|
||||
return data.size();
|
||||
}
|
||||
|
||||
/** Returns one of the elements in the set.
|
||||
|
||||
If the index passed in is beyond the range of valid elements, this
|
||||
will return zero.
|
||||
|
||||
If you're certain that the index will always be a valid element, you
|
||||
can call getUnchecked() instead, which is faster.
|
||||
|
||||
@param index the index of the element being requested (0 is the first element in the set)
|
||||
@see getUnchecked, getFirst, getLast
|
||||
*/
|
||||
inline ElementType operator[] (const int index) const noexcept
|
||||
{
|
||||
return data [index];
|
||||
}
|
||||
|
||||
/** Returns one of the elements in the set, without checking the index passed in.
|
||||
Unlike the operator[] method, this will try to return an element without
|
||||
checking that the index is within the bounds of the set, so should only
|
||||
be used when you're confident that it will always be a valid index.
|
||||
|
||||
@param index the index of the element being requested (0 is the first element in the set)
|
||||
@see operator[], getFirst, getLast
|
||||
*/
|
||||
inline ElementType getUnchecked (const int index) const noexcept
|
||||
{
|
||||
return data.getUnchecked (index);
|
||||
}
|
||||
|
||||
/** Returns a direct reference to one of the elements in the set, without checking the index passed in.
|
||||
|
||||
This is like getUnchecked, but returns a direct reference to the element, so that
|
||||
you can alter it directly. Obviously this can be dangerous, so only use it when
|
||||
absolutely necessary.
|
||||
|
||||
@param index the index of the element being requested (0 is the first element in the array)
|
||||
*/
|
||||
inline ElementType& getReference (const int index) const noexcept
|
||||
{
|
||||
return data.getReference (index);
|
||||
}
|
||||
|
||||
/** Returns the first element in the set, or 0 if the set is empty.
|
||||
|
||||
@see operator[], getUnchecked, getLast
|
||||
*/
|
||||
inline ElementType getFirst() const noexcept
|
||||
{
|
||||
return data.getFirst();
|
||||
}
|
||||
|
||||
/** Returns the last element in the set, or 0 if the set is empty.
|
||||
|
||||
@see operator[], getUnchecked, getFirst
|
||||
*/
|
||||
inline ElementType getLast() const noexcept
|
||||
{
|
||||
return data.getLast();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a pointer to the first element in the set.
|
||||
This method is provided for compatibility with standard C++ iteration mechanisms.
|
||||
*/
|
||||
inline ElementType* begin() const noexcept
|
||||
{
|
||||
return data.begin();
|
||||
}
|
||||
|
||||
/** Returns a pointer to the element which follows the last element in the set.
|
||||
This method is provided for compatibility with standard C++ iteration mechanisms.
|
||||
*/
|
||||
inline ElementType* end() const noexcept
|
||||
{
|
||||
return data.end();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Finds the index of the first element which matches the value passed in.
|
||||
|
||||
This will search the set for the given object, and return the index
|
||||
of its first occurrence. If the object isn't found, the method will return -1.
|
||||
|
||||
@param elementToLookFor the value or object to look for
|
||||
@returns the index of the object, or -1 if it's not found
|
||||
*/
|
||||
int indexOf (const ElementType& elementToLookFor) const noexcept
|
||||
{
|
||||
const ScopedLockType lock (data.getLock());
|
||||
|
||||
int s = 0;
|
||||
int e = data.size();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (s >= e)
|
||||
return -1;
|
||||
|
||||
if (elementToLookFor == data.getReference (s))
|
||||
return s;
|
||||
|
||||
const int halfway = (s + e) / 2;
|
||||
|
||||
if (halfway == s)
|
||||
return -1;
|
||||
else if (elementToLookFor < data.getReference (halfway))
|
||||
e = halfway;
|
||||
else
|
||||
s = halfway;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns true if the set contains at least one occurrence of an object.
|
||||
|
||||
@param elementToLookFor the value or object to look for
|
||||
@returns true if the item is found
|
||||
*/
|
||||
bool contains (const ElementType& elementToLookFor) const noexcept
|
||||
{
|
||||
return indexOf (elementToLookFor) >= 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a new element to the set, (as long as it's not already in there).
|
||||
|
||||
Note that if a matching element already exists, the new value will be assigned
|
||||
to the existing one using operator=, so that if there are any differences between
|
||||
the objects which were not recognised by the object's operator==, then the
|
||||
set will always contain a copy of the most recently added one.
|
||||
|
||||
@param newElement the new object to add to the set
|
||||
@returns true if the value was added, or false if it already existed
|
||||
@see set, insert, addIfNotAlreadyThere, addSorted, addSet, addArray
|
||||
*/
|
||||
bool add (const ElementType& newElement) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
int s = 0;
|
||||
int e = data.size();
|
||||
|
||||
while (s < e)
|
||||
{
|
||||
ElementType& elem = data.getReference (s);
|
||||
if (newElement == elem)
|
||||
{
|
||||
elem = newElement; // force an update in case operator== permits differences.
|
||||
return false;
|
||||
}
|
||||
|
||||
const int halfway = (s + e) / 2;
|
||||
const bool isBeforeHalfway = (newElement < data.getReference (halfway));
|
||||
|
||||
if (halfway == s)
|
||||
{
|
||||
if (! isBeforeHalfway)
|
||||
++s;
|
||||
|
||||
break;
|
||||
}
|
||||
else if (isBeforeHalfway)
|
||||
e = halfway;
|
||||
else
|
||||
s = halfway;
|
||||
}
|
||||
|
||||
data.insert (s, newElement);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Adds elements from an array to this set.
|
||||
|
||||
@param elementsToAdd the array of elements to add
|
||||
@param numElementsToAdd how many elements are in this other array
|
||||
@see add
|
||||
*/
|
||||
void addArray (const ElementType* elementsToAdd,
|
||||
int numElementsToAdd) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
while (--numElementsToAdd >= 0)
|
||||
add (*elementsToAdd++);
|
||||
}
|
||||
|
||||
/** Adds elements from another set to this one.
|
||||
|
||||
@param setToAddFrom the set from which to copy the elements
|
||||
@param startIndex the first element of the other set to start copying from
|
||||
@param numElementsToAdd how many elements to add from the other set. If this
|
||||
value is negative or greater than the number of available elements,
|
||||
all available elements will be copied.
|
||||
@see add
|
||||
*/
|
||||
template <class OtherSetType>
|
||||
void addSet (const OtherSetType& setToAddFrom,
|
||||
int startIndex = 0,
|
||||
int numElementsToAdd = -1) noexcept
|
||||
{
|
||||
const typename OtherSetType::ScopedLockType lock1 (setToAddFrom.getLock());
|
||||
|
||||
{
|
||||
const ScopedLockType lock2 (getLock());
|
||||
bassert (this != &setToAddFrom);
|
||||
|
||||
if (this != &setToAddFrom)
|
||||
{
|
||||
if (startIndex < 0)
|
||||
{
|
||||
bassertfalse;
|
||||
startIndex = 0;
|
||||
}
|
||||
|
||||
if (numElementsToAdd < 0 || startIndex + numElementsToAdd > setToAddFrom.size())
|
||||
numElementsToAdd = setToAddFrom.size() - startIndex;
|
||||
|
||||
if (numElementsToAdd > 0)
|
||||
addArray (&setToAddFrom.data.getReference (startIndex), numElementsToAdd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Removes an element from the set.
|
||||
|
||||
This will remove the element at a given index.
|
||||
If the index passed in is out-of-range, nothing will happen.
|
||||
|
||||
@param indexToRemove the index of the element to remove
|
||||
@returns the element that has been removed
|
||||
@see removeValue, removeRange
|
||||
*/
|
||||
ElementType remove (const int indexToRemove) noexcept
|
||||
{
|
||||
return data.remove (indexToRemove);
|
||||
}
|
||||
|
||||
/** Removes an item from the set.
|
||||
|
||||
This will remove the given element from the set, if it's there.
|
||||
|
||||
@param valueToRemove the object to try to remove
|
||||
@see remove, removeRange
|
||||
*/
|
||||
void removeValue (const ElementType valueToRemove) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.remove (indexOf (valueToRemove));
|
||||
}
|
||||
|
||||
/** Removes any elements which are also in another set.
|
||||
|
||||
@param otherSet the other set in which to look for elements to remove
|
||||
@see removeValuesNotIn, remove, removeValue, removeRange
|
||||
*/
|
||||
template <class OtherSetType>
|
||||
void removeValuesIn (const OtherSetType& otherSet) noexcept
|
||||
{
|
||||
const typename OtherSetType::ScopedLockType lock1 (otherSet.getLock());
|
||||
const ScopedLockType lock2 (getLock());
|
||||
|
||||
if (this == &otherSet)
|
||||
{
|
||||
clear();
|
||||
}
|
||||
else if (otherSet.size() > 0)
|
||||
{
|
||||
for (int i = data.size(); --i >= 0;)
|
||||
if (otherSet.contains (data.getReference (i)))
|
||||
remove (i);
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes any elements which are not found in another set.
|
||||
|
||||
Only elements which occur in this other set will be retained.
|
||||
|
||||
@param otherSet the set in which to look for elements NOT to remove
|
||||
@see removeValuesIn, remove, removeValue, removeRange
|
||||
*/
|
||||
template <class OtherSetType>
|
||||
void removeValuesNotIn (const OtherSetType& otherSet) noexcept
|
||||
{
|
||||
const typename OtherSetType::ScopedLockType lock1 (otherSet.getLock());
|
||||
const ScopedLockType lock2 (getLock());
|
||||
|
||||
if (this != &otherSet)
|
||||
{
|
||||
if (otherSet.size() <= 0)
|
||||
{
|
||||
clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = data.size(); --i >= 0;)
|
||||
if (! otherSet.contains (data.getReference (i)))
|
||||
remove (i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** This swaps the contents of this array with those of another array.
|
||||
|
||||
If you need to exchange two arrays, this is vastly quicker than using copy-by-value
|
||||
because it just swaps their internal pointers.
|
||||
*/
|
||||
template <class OtherSortedSetType>
|
||||
void swapWith (OtherSortedSetType& otherSet) noexcept
|
||||
{
|
||||
data.swapWith (otherSet.data);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Reduces the amount of storage being used by the set.
|
||||
|
||||
Sets typically allocate slightly more storage than they need, and after
|
||||
removing elements, they may have quite a lot of unused space allocated.
|
||||
This method will reduce the amount of allocated storage to a minimum.
|
||||
*/
|
||||
void minimiseStorageOverheads() noexcept
|
||||
{
|
||||
data.minimiseStorageOverheads();
|
||||
}
|
||||
|
||||
/** Increases the set's internal storage to hold a minimum number of elements.
|
||||
|
||||
Calling this before adding a large known number of elements means that
|
||||
the set won't have to keep dynamically resizing itself as the elements
|
||||
are added, and it'll therefore be more efficient.
|
||||
*/
|
||||
void ensureStorageAllocated (const int minNumElements)
|
||||
{
|
||||
data.ensureStorageAllocated (minNumElements);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the CriticalSection that locks this array.
|
||||
To lock, you can call getLock().enter() and getLock().exit(), or preferably use
|
||||
an object of ScopedLockType as an RAII lock for it.
|
||||
*/
|
||||
inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return data.getLock(); }
|
||||
|
||||
/** Returns the type of scoped lock to use for locking this array */
|
||||
typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType;
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Array <ElementType, TypeOfCriticalSectionToUse> data;
|
||||
};
|
||||
|
||||
#if BEAST_MSVC
|
||||
#pragma warning (pop)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,264 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_EXPRESSION_H_INCLUDED
|
||||
#define BEAST_EXPRESSION_H_INCLUDED
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A class for dynamically evaluating simple numeric expressions.
|
||||
|
||||
This class can parse a simple C-style string expression involving floating point
|
||||
numbers, named symbols and functions. The basic arithmetic operations of +, -, *, /
|
||||
are supported, as well as parentheses, and any alphanumeric identifiers are
|
||||
assumed to be named symbols which will be resolved when the expression is
|
||||
evaluated.
|
||||
|
||||
Expressions which use identifiers and functions require a subclass of
|
||||
Expression::Scope to be supplied when evaluating them, and this object
|
||||
is expected to be able to resolve the symbol names and perform the functions that
|
||||
are used.
|
||||
*/
|
||||
class BEAST_API Expression
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a simple expression with a value of 0. */
|
||||
Expression();
|
||||
|
||||
/** Destructor. */
|
||||
~Expression();
|
||||
|
||||
/** Creates a simple expression with a specified constant value. */
|
||||
explicit Expression (double constant);
|
||||
|
||||
/** Creates a copy of an expression. */
|
||||
Expression (const Expression& other);
|
||||
|
||||
/** Copies another expression. */
|
||||
Expression& operator= (const Expression& other);
|
||||
|
||||
#if BEAST_COMPILER_SUPPORTS_MOVE_SEMANTICS
|
||||
Expression (Expression&& other) noexcept;
|
||||
Expression& operator= (Expression&& other) noexcept;
|
||||
#endif
|
||||
|
||||
/** Creates an expression by parsing a string.
|
||||
If there's a syntax error in the string, this will throw a ParseError exception.
|
||||
@throws ParseError
|
||||
*/
|
||||
explicit Expression (const String& stringToParse);
|
||||
|
||||
/** Returns a string version of the expression. */
|
||||
String toString() const;
|
||||
|
||||
/** Returns an expression which is an addtion operation of two existing expressions. */
|
||||
Expression operator+ (const Expression& other) const;
|
||||
/** Returns an expression which is a subtraction operation of two existing expressions. */
|
||||
Expression operator- (const Expression& other) const;
|
||||
/** Returns an expression which is a multiplication operation of two existing expressions. */
|
||||
Expression operator* (const Expression& other) const;
|
||||
/** Returns an expression which is a division operation of two existing expressions. */
|
||||
Expression operator/ (const Expression& other) const;
|
||||
/** Returns an expression which performs a negation operation on an existing expression. */
|
||||
Expression operator-() const;
|
||||
|
||||
/** Returns an Expression which is an identifier reference. */
|
||||
static Expression symbol (const String& symbol);
|
||||
|
||||
/** Returns an Expression which is a function call. */
|
||||
static Expression function (const String& functionName, const Array<Expression>& parameters);
|
||||
|
||||
/** Returns an Expression which parses a string from a character pointer, and updates the pointer
|
||||
to indicate where it finished.
|
||||
|
||||
The pointer is incremented so that on return, it indicates the character that follows
|
||||
the end of the expression that was parsed.
|
||||
|
||||
If there's a syntax error in the string, this will throw a ParseError exception.
|
||||
@throws ParseError
|
||||
*/
|
||||
static Expression parse (String::CharPointerType& stringToParse);
|
||||
|
||||
//==============================================================================
|
||||
/** When evaluating an Expression object, this class is used to resolve symbols and
|
||||
perform functions that the expression uses.
|
||||
*/
|
||||
class BEAST_API Scope
|
||||
{
|
||||
public:
|
||||
Scope();
|
||||
virtual ~Scope();
|
||||
|
||||
/** Returns some kind of globally unique ID that identifies this scope. */
|
||||
virtual String getScopeUID() const;
|
||||
|
||||
/** Returns the value of a symbol.
|
||||
If the symbol is unknown, this can throw an Expression::EvaluationError exception.
|
||||
The member value is set to the part of the symbol that followed the dot, if there is
|
||||
one, e.g. for "foo.bar", symbol = "foo" and member = "bar".
|
||||
@throws Expression::EvaluationError
|
||||
*/
|
||||
virtual Expression getSymbolValue (const String& symbol) const;
|
||||
|
||||
/** Executes a named function.
|
||||
If the function name is unknown, this can throw an Expression::EvaluationError exception.
|
||||
@throws Expression::EvaluationError
|
||||
*/
|
||||
virtual double evaluateFunction (const String& functionName,
|
||||
const double* parameters, int numParameters) const;
|
||||
|
||||
/** Used as a callback by the Scope::visitRelativeScope() method.
|
||||
You should never create an instance of this class yourself, it's used by the
|
||||
expression evaluation code.
|
||||
*/
|
||||
class Visitor
|
||||
{
|
||||
public:
|
||||
virtual ~Visitor() {}
|
||||
virtual void visit (const Scope&) = 0;
|
||||
};
|
||||
|
||||
/** Creates a Scope object for a named scope, and then calls a visitor
|
||||
to do some kind of processing with this new scope.
|
||||
|
||||
If the name is valid, this method must create a suitable (temporary) Scope
|
||||
object to represent it, and must call the Visitor::visit() method with this
|
||||
new scope.
|
||||
*/
|
||||
virtual void visitRelativeScope (const String& scopeName, Visitor& visitor) const;
|
||||
};
|
||||
|
||||
/** Evaluates this expression, without using a Scope.
|
||||
Without a Scope, no symbols can be used, and only basic functions such as sin, cos, tan,
|
||||
min, max are available.
|
||||
To find out about any errors during evaluation, use the other version of this method which
|
||||
takes a String parameter.
|
||||
*/
|
||||
double evaluate() const;
|
||||
|
||||
/** Evaluates this expression, providing a scope that should be able to evaluate any symbols
|
||||
or functions that it uses.
|
||||
To find out about any errors during evaluation, use the other version of this method which
|
||||
takes a String parameter.
|
||||
*/
|
||||
double evaluate (const Scope& scope) const;
|
||||
|
||||
/** Evaluates this expression, providing a scope that should be able to evaluate any symbols
|
||||
or functions that it uses.
|
||||
*/
|
||||
double evaluate (const Scope& scope, String& evaluationError) const;
|
||||
|
||||
/** Attempts to return an expression which is a copy of this one, but with a constant adjusted
|
||||
to make the expression resolve to a target value.
|
||||
|
||||
E.g. if the expression is "x + 10" and x is 5, then asking for a target value of 8 will return
|
||||
the expression "x + 3". Obviously some expressions can't be reversed in this way, in which
|
||||
case they might just be adjusted by adding a constant to the original expression.
|
||||
|
||||
@throws Expression::EvaluationError
|
||||
*/
|
||||
Expression adjustedToGiveNewResult (double targetValue, const Scope& scope) const;
|
||||
|
||||
/** Represents a symbol that is used in an Expression. */
|
||||
struct Symbol
|
||||
{
|
||||
Symbol (const String& scopeUID, const String& symbolName);
|
||||
bool operator== (const Symbol&) const noexcept;
|
||||
bool operator!= (const Symbol&) const noexcept;
|
||||
|
||||
String scopeUID; /**< The unique ID of the Scope that contains this symbol. */
|
||||
String symbolName; /**< The name of the symbol. */
|
||||
};
|
||||
|
||||
/** Returns a copy of this expression in which all instances of a given symbol have been renamed. */
|
||||
Expression withRenamedSymbol (const Symbol& oldSymbol, const String& newName, const Scope& scope) const;
|
||||
|
||||
/** Returns true if this expression makes use of the specified symbol.
|
||||
If a suitable scope is supplied, the search will dereference and recursively check
|
||||
all symbols, so that it can be determined whether this expression relies on the given
|
||||
symbol at any level in its evaluation. If the scope parameter is null, this just checks
|
||||
whether the expression contains any direct references to the symbol.
|
||||
|
||||
@throws Expression::EvaluationError
|
||||
*/
|
||||
bool referencesSymbol (const Symbol& symbol, const Scope& scope) const;
|
||||
|
||||
/** Returns true if this expression contains any symbols. */
|
||||
bool usesAnySymbols() const;
|
||||
|
||||
/** Returns a list of all symbols that may be needed to resolve this expression in the given scope. */
|
||||
void findReferencedSymbols (Array<Symbol>& results, const Scope& scope) const;
|
||||
|
||||
//==============================================================================
|
||||
/** An exception that can be thrown by Expression::parse(). */
|
||||
class ParseError : public std::exception
|
||||
{
|
||||
public:
|
||||
ParseError (const String& message);
|
||||
|
||||
String description;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Expression type.
|
||||
@see Expression::getType()
|
||||
*/
|
||||
enum Type
|
||||
{
|
||||
constantType,
|
||||
functionType,
|
||||
operatorType,
|
||||
symbolType
|
||||
};
|
||||
|
||||
/** Returns the type of this expression. */
|
||||
Type getType() const noexcept;
|
||||
|
||||
/** If this expression is a symbol, function or operator, this returns its identifier. */
|
||||
String getSymbolOrFunction() const;
|
||||
|
||||
/** Returns the number of inputs to this expression.
|
||||
@see getInput
|
||||
*/
|
||||
int getNumInputs() const;
|
||||
|
||||
/** Retrieves one of the inputs to this expression.
|
||||
@see getNumInputs
|
||||
*/
|
||||
Expression getInput (int index) const;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class Term;
|
||||
struct Helpers;
|
||||
friend class Term;
|
||||
friend struct Helpers;
|
||||
friend class ScopedPointer<Term>;
|
||||
friend class SharedPtr<Term>;
|
||||
SharedPtr<Term> term;
|
||||
|
||||
explicit Expression (Term*);
|
||||
};
|
||||
|
||||
#endif // BEAST_EXPRESSION_H_INCLUDED
|
||||
@@ -1,387 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_INTERVAL_H_INCLUDED
|
||||
#define BEAST_INTERVAL_H_INCLUDED
|
||||
|
||||
/** A half-open interval.
|
||||
|
||||
This represents the half-open interval [begin, end) over the scalar
|
||||
type of template parameter `Ty`. It may also be considered as the
|
||||
specification of a subset of a 1-dimensional Euclidean space.
|
||||
|
||||
@tparam Ty A scalar numerical type.
|
||||
*/
|
||||
template <class Ty>
|
||||
class Interval
|
||||
{
|
||||
public:
|
||||
typedef Ty value_type;
|
||||
|
||||
/** The empty interval.
|
||||
*/
|
||||
static const Interval none;
|
||||
|
||||
/** Create an uninitialized interval.
|
||||
*/
|
||||
Interval ()
|
||||
{
|
||||
}
|
||||
|
||||
/** Create an interval with the specified values.
|
||||
*/
|
||||
Interval (Ty begin, Ty end)
|
||||
: m_begin (begin)
|
||||
, m_end (end)
|
||||
{
|
||||
}
|
||||
|
||||
/** Create an interval from another interval.
|
||||
*/
|
||||
Interval (Interval const& other)
|
||||
: m_begin (other.m_begin)
|
||||
, m_end (other.m_end)
|
||||
{
|
||||
}
|
||||
|
||||
/** Assign from another interval.
|
||||
|
||||
@param other The interval to assign from.
|
||||
|
||||
@return A reference to this interval.
|
||||
*/
|
||||
Interval& operator= (const Interval& other)
|
||||
{
|
||||
m_begin = other.m_begin;
|
||||
m_end = other.m_end;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Compare an interval for equality.
|
||||
|
||||
Empty intervals are always equal to other empty intervals.
|
||||
|
||||
@param rhs The other interval to compare.
|
||||
|
||||
@return `true` if this interval is equal to the specified interval.
|
||||
*/
|
||||
bool operator== (Interval const& rhs) const
|
||||
{
|
||||
return (empty () && rhs.empty ()) ||
|
||||
(m_begin == rhs.m_begin && m_end == rhs.m_end);
|
||||
}
|
||||
|
||||
/** Compare an interval for inequality.
|
||||
|
||||
@param rhs The other interval to compare.
|
||||
|
||||
@return `true` if this interval is not equal to the specified interval.
|
||||
*/
|
||||
bool operator!= (Interval const& rhs) const
|
||||
{
|
||||
return !this->operator== (rhs);
|
||||
}
|
||||
|
||||
/** Get the starting value of the interval.
|
||||
|
||||
@return The starting point of the interval.
|
||||
*/
|
||||
Ty begin () const
|
||||
{
|
||||
return m_begin;
|
||||
}
|
||||
|
||||
/** Get the ending value of the interval.
|
||||
|
||||
@return The ending point of the interval.
|
||||
*/
|
||||
Ty end () const
|
||||
{
|
||||
return m_end;
|
||||
}
|
||||
|
||||
/** Get the Lebesque measure.
|
||||
|
||||
@return The Lebesque measure.
|
||||
*/
|
||||
Ty length () const
|
||||
{
|
||||
return empty () ? Ty () : (end () - begin ());
|
||||
}
|
||||
|
||||
//Ty count () const { return length (); } // sugar
|
||||
//Ty distance () const { return length (); } // sugar
|
||||
|
||||
/** Determine if the interval is empty.
|
||||
|
||||
@return `true` if the interval is empty.
|
||||
*/
|
||||
bool empty () const
|
||||
{
|
||||
return m_begin >= m_end;
|
||||
}
|
||||
|
||||
/** Determine if the interval is non-empty.
|
||||
|
||||
@return `true` if the interval is not empty.
|
||||
*/
|
||||
bool notEmpty () const
|
||||
{
|
||||
return m_begin < m_end;
|
||||
}
|
||||
|
||||
/** Set the starting point of the interval.
|
||||
|
||||
@param v The starting point.
|
||||
*/
|
||||
void setBegin (Ty v)
|
||||
{
|
||||
m_begin = v;
|
||||
}
|
||||
|
||||
/** Set the ending point of the interval.
|
||||
|
||||
@param v The ending point.
|
||||
*/
|
||||
void setEnd (Ty v)
|
||||
{
|
||||
m_end = v;
|
||||
}
|
||||
|
||||
/** Set the ending point relative to the starting point.
|
||||
|
||||
@param v The length of the resulting interval.
|
||||
*/
|
||||
void setLength (Ty v)
|
||||
{
|
||||
m_end = m_begin + v;
|
||||
}
|
||||
|
||||
/** Determine if a value is contained in the interval.
|
||||
|
||||
@param v The value to check.
|
||||
|
||||
@return `true` if this interval contains `v`.
|
||||
*/
|
||||
bool contains (Ty v) const
|
||||
{
|
||||
return notEmpty () && v >= m_begin && v < m_end;
|
||||
}
|
||||
|
||||
/** Determine if this interval intersects another interval.
|
||||
|
||||
@param other The other interval.
|
||||
|
||||
@return `true` if the intervals intersect.
|
||||
*/
|
||||
template <class To>
|
||||
bool intersects (Interval <To> const& other) const
|
||||
{
|
||||
return notEmpty () && other.notEmpty () &&
|
||||
end () > other.begin () && begin () < other.end ();
|
||||
}
|
||||
|
||||
/** Determine if this interval adjoins another interval.
|
||||
|
||||
An interval is adjoint to another interval if and only if the union of the
|
||||
intervals is a single non-empty half-open subset.
|
||||
|
||||
@param other The other interval.
|
||||
|
||||
@return `true` if the intervals are adjoint.
|
||||
*/
|
||||
template <class To>
|
||||
bool adjoins (Interval <To> const& other) const
|
||||
{
|
||||
return (empty () != other.empty ()) ||
|
||||
(notEmpty () && end () >= other.begin ()
|
||||
&& begin () <= other.end ());
|
||||
}
|
||||
|
||||
/** Determine if this interval is disjoint from another interval.
|
||||
|
||||
@param other The other interval.
|
||||
|
||||
@return `true` if the intervals are disjoint.
|
||||
*/
|
||||
bool disjoint (Interval const& other) const
|
||||
{
|
||||
return !intersects (other);
|
||||
}
|
||||
|
||||
/** Determine if this interval is a superset of another interval.
|
||||
|
||||
An interval A is a superset of interval B if B is empty or if A fully
|
||||
contains B.
|
||||
|
||||
@param other The other interval.
|
||||
|
||||
@return `true` if this is a superset of `other`.
|
||||
*/
|
||||
template <class To>
|
||||
bool superset_of (Interval <To> const& other) const
|
||||
{
|
||||
return other.empty () ||
|
||||
(notEmpty () && begin () <= other.begin ()
|
||||
&& end () >= other.end ());
|
||||
}
|
||||
|
||||
/** Determine if this interval is a proper superset of another interval.
|
||||
|
||||
An interval A is a proper superset of interval B if A is a superset of
|
||||
B and A is not equal to B.
|
||||
|
||||
@param other The other interval.
|
||||
|
||||
@return `true` if this interval is a proper superset of `other`.
|
||||
*/
|
||||
template <class To>
|
||||
bool proper_superset_of (Interval <To> const& other) const
|
||||
{
|
||||
return this->superset_of (other) && this->operator != (other);
|
||||
}
|
||||
|
||||
/** Determine if this interval is a subset of another interval.
|
||||
|
||||
@param other The other interval.
|
||||
|
||||
@return `true` if this interval is a subset of `other`.
|
||||
*/
|
||||
template <class To>
|
||||
bool subset_of (Interval <To> const& other) const
|
||||
{
|
||||
return other.superset_of (*this);
|
||||
}
|
||||
|
||||
/** Determine if this interval is a proper subset of another interval.
|
||||
|
||||
@param other The other interval.
|
||||
|
||||
@return `true` if this interval is a proper subset of `other`.
|
||||
*/
|
||||
template <class To>
|
||||
bool proper_subset_of (Interval <To> const& other) const
|
||||
{
|
||||
return other.proper_superset_of (*this);
|
||||
}
|
||||
|
||||
/** Return the intersection of this interval with another interval.
|
||||
|
||||
@param other The other interval.
|
||||
|
||||
@return The intersection of the intervals.
|
||||
*/
|
||||
template <class To>
|
||||
Interval intersection (Interval <To> const& other) const
|
||||
{
|
||||
return Interval (std::max (begin (), other.begin ()),
|
||||
std::min (end (), other.end ()));
|
||||
}
|
||||
|
||||
/** Determine the smallest interval that contains both intervals.
|
||||
|
||||
@param other The other interval.
|
||||
|
||||
@return The simple union of the intervals.
|
||||
*/
|
||||
template <class To>
|
||||
Interval simple_union (Interval <To> const& other) const
|
||||
{
|
||||
return Interval (
|
||||
std::min (other.normalized ().begin (), normalized ().begin ()),
|
||||
std::max (other.normalized ().end (), normalized ().end ()));
|
||||
}
|
||||
|
||||
/** Calculate the single-interval union.
|
||||
|
||||
The result is empty if the union cannot be represented as a
|
||||
single half-open interval.
|
||||
|
||||
@param other The other interval.
|
||||
|
||||
@return The simple union of the intervals.
|
||||
*/
|
||||
template <class To>
|
||||
Interval single_union (Interval <To> const& other) const
|
||||
{
|
||||
if (empty ())
|
||||
return other;
|
||||
|
||||
else if (other.empty ())
|
||||
return *this;
|
||||
|
||||
else if (end () < other.begin () || begin () > other.end ())
|
||||
return none;
|
||||
|
||||
else
|
||||
return Interval (std::min (begin (), other.begin ()),
|
||||
std::max (end (), other.end ()));
|
||||
}
|
||||
|
||||
/** Determine if the interval is correctly ordered.
|
||||
|
||||
@return `true` if the interval is correctly ordered.
|
||||
*/
|
||||
bool normal () const
|
||||
{
|
||||
return end () >= begin ();
|
||||
}
|
||||
|
||||
/** Return a normalized interval.
|
||||
|
||||
@return The normalized interval.
|
||||
*/
|
||||
Interval normalized () const
|
||||
{
|
||||
if (normal ())
|
||||
return *this;
|
||||
else
|
||||
return Interval (end (), begin ());
|
||||
}
|
||||
|
||||
/** Clamp a value to the interval.
|
||||
|
||||
@param v The value to clamp.
|
||||
|
||||
@return The clamped result.
|
||||
*/
|
||||
template <typename Tv>
|
||||
Ty clamp (Tv v) const
|
||||
{
|
||||
// These conditionals are carefully ordered so
|
||||
// that if m_begin == m_end, value is assigned m_begin.
|
||||
if (v > end ())
|
||||
v = end () - (std::numeric_limits <Tv>::is_integer ? 1 :
|
||||
std::numeric_limits <Tv>::epsilon ());
|
||||
|
||||
if (v < begin ())
|
||||
v = begin ();
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
private:
|
||||
Ty m_begin;
|
||||
Ty m_end;
|
||||
};
|
||||
|
||||
template <typename Ty>
|
||||
const Interval<Ty> Interval<Ty>::none = Interval<Ty> (Ty (), Ty ());
|
||||
|
||||
#endif
|
||||
@@ -1,83 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_ATOMICCOUNTER_H_INCLUDED
|
||||
#define BEAST_ATOMICCOUNTER_H_INCLUDED
|
||||
|
||||
/*============================================================================*/
|
||||
/**
|
||||
A thread safe usage counter.
|
||||
|
||||
This provides a simplified interface to an atomic integer suitable for
|
||||
measuring reference or usage counts. The counter is signaled when the
|
||||
count is non zero.
|
||||
|
||||
@ingroup beast_core
|
||||
*/
|
||||
class BEAST_API AtomicCounter
|
||||
{
|
||||
public:
|
||||
/** Create a new counter.
|
||||
|
||||
@param initialValue An optional starting usage count (default is 0).
|
||||
*/
|
||||
AtomicCounter (int initialValue = 0) noexcept
|
||||
:
|
||||
m_value (initialValue)
|
||||
{
|
||||
}
|
||||
|
||||
/** Increment the usage count.
|
||||
|
||||
@return `true` if the counter became signaled.
|
||||
*/
|
||||
inline bool addref () noexcept
|
||||
{
|
||||
return (++m_value) == 1;
|
||||
}
|
||||
|
||||
/** Decrements the usage count.
|
||||
|
||||
@return `true` if the counter became non-signaled.
|
||||
*/
|
||||
inline bool release () noexcept
|
||||
{
|
||||
// Unfortunately, AllocatorWithoutTLS breaks this assert
|
||||
//bassert (isSignaled ());
|
||||
|
||||
return (--m_value) == 0;
|
||||
}
|
||||
|
||||
/** Determine if the counter is signaled.
|
||||
|
||||
Note that another thread can cause the counter to become reset after
|
||||
this function returns true.
|
||||
|
||||
@return `true` if the counter was signaled.
|
||||
*/
|
||||
inline bool isSignaled () const noexcept
|
||||
{
|
||||
return m_value.get () > 0;
|
||||
}
|
||||
|
||||
private:
|
||||
Atomic <int> m_value;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,102 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_ATOMICFLAG_H_INCLUDED
|
||||
#define BEAST_ATOMICFLAG_H_INCLUDED
|
||||
|
||||
/*============================================================================*/
|
||||
/**
|
||||
A thread safe flag.
|
||||
|
||||
This provides a simplified interface to an atomic integer suitable for
|
||||
representing a flag. The flag is signaled when on, else it is considered
|
||||
reset.
|
||||
|
||||
@ingroup beast_core
|
||||
*/
|
||||
class BEAST_API AtomicFlag
|
||||
{
|
||||
public:
|
||||
/** Create an AtomicFlag in the reset state. */
|
||||
AtomicFlag () noexcept
|
||||
:
|
||||
m_value (0)
|
||||
{
|
||||
}
|
||||
|
||||
/** Signal the flag.
|
||||
|
||||
If two or more threads simultaneously attempt to signal the flag,
|
||||
only one will receive a true return value.
|
||||
|
||||
@return true if the flag was previously reset.
|
||||
*/
|
||||
inline bool trySignal () noexcept
|
||||
{
|
||||
return m_value.compareAndSetBool (1, 0);
|
||||
}
|
||||
|
||||
/** Signal the flag.
|
||||
|
||||
The flag must be in the reset state. Only one thread may
|
||||
call this at a time.
|
||||
*/
|
||||
inline void signal () noexcept
|
||||
{
|
||||
#if BEAST_DEBUG
|
||||
const bool success = m_value.compareAndSetBool (1, 0);
|
||||
bassert (success);
|
||||
#else
|
||||
m_value.set (1);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Reset the flag.
|
||||
|
||||
The flag must be in the signaled state. Only one thread may
|
||||
call this at a time. Usually it is the thread that was successful
|
||||
in a previous call to trySignal().
|
||||
*/
|
||||
inline void reset () noexcept
|
||||
{
|
||||
#if BEAST_DEBUG
|
||||
const bool success = m_value.compareAndSetBool (0, 1);
|
||||
bassert (success);
|
||||
#else
|
||||
m_value.set (0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Check if the AtomicFlag is signaled
|
||||
|
||||
The signaled status may change immediately after this call
|
||||
returns. The caller must synchronize.
|
||||
|
||||
@return true if the flag was signaled.
|
||||
*/
|
||||
inline bool isSignaled () const noexcept
|
||||
{
|
||||
return m_value.get () == 1;
|
||||
}
|
||||
|
||||
private:
|
||||
Atomic <int> m_value;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,133 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_ATOMICPOINTER_H_INCLUDED
|
||||
#define BEAST_ATOMICPOINTER_H_INCLUDED
|
||||
|
||||
/*============================================================================*/
|
||||
/**
|
||||
A thread safe pointer.
|
||||
|
||||
This provides a simplified interface to an atomic pointer suitable
|
||||
for building containers or composite classes. Operator overloads
|
||||
allow access to the underlying pointer using natural C++ syntax.
|
||||
|
||||
@ingroup beast_core
|
||||
*/
|
||||
template <class P>
|
||||
class AtomicPointer
|
||||
{
|
||||
public:
|
||||
/** Create a pointer.
|
||||
|
||||
@param initialValue An optional starting value (default is null).
|
||||
*/
|
||||
explicit AtomicPointer (P* const initialValue = nullptr) noexcept
|
||||
:
|
||||
m_value (initialValue)
|
||||
{
|
||||
}
|
||||
|
||||
/** Retrieve the pointer value */
|
||||
inline P* get () const noexcept
|
||||
{
|
||||
return m_value.get ();
|
||||
}
|
||||
|
||||
/** Obtain a pointer to P through type conversion.
|
||||
|
||||
The caller must synchronize access to P.
|
||||
|
||||
@return A pointer to P.
|
||||
*/
|
||||
inline operator P* () const noexcept
|
||||
{
|
||||
return get ();
|
||||
}
|
||||
|
||||
/** Dereference operator
|
||||
|
||||
The caller must synchronize access to P.
|
||||
|
||||
@return A reference to P.
|
||||
*/
|
||||
inline P& operator* () const noexcept
|
||||
{
|
||||
return &get ();
|
||||
}
|
||||
|
||||
/** Member selection
|
||||
|
||||
The caller must synchronize access to P.
|
||||
|
||||
@return A pointer to P.
|
||||
*/
|
||||
inline P* operator-> () const noexcept
|
||||
{
|
||||
return get ();
|
||||
}
|
||||
|
||||
inline void set (P* p)
|
||||
{
|
||||
m_value.set (p);
|
||||
}
|
||||
|
||||
/** Atomically assign a new pointer
|
||||
|
||||
@param newValue The new value to assign.
|
||||
*/
|
||||
inline void operator= (P* newValue) noexcept
|
||||
{
|
||||
set (newValue);
|
||||
}
|
||||
|
||||
/** Atomically assign a new pointer and return the old value.
|
||||
|
||||
@param newValue The new value to assign.
|
||||
|
||||
@return The previous value.
|
||||
*/
|
||||
inline P* exchange (P* newValue)
|
||||
{
|
||||
return m_value.exchange (newValue);
|
||||
}
|
||||
|
||||
/** Conditionally perform an atomic assignment.
|
||||
|
||||
The current value is compared with oldValue and atomically
|
||||
set to newValue if the comparison is equal.
|
||||
|
||||
The caller is responsible for handling the ABA problem.
|
||||
|
||||
@param newValue The new value to assign.
|
||||
|
||||
@param oldValue The matching old value.
|
||||
|
||||
@return true if the assignment was performed.
|
||||
*/
|
||||
inline bool compareAndSet (P* newValue, P* oldValue)
|
||||
{
|
||||
return m_value.compareAndSetBool (newValue, oldValue);
|
||||
}
|
||||
|
||||
private:
|
||||
Atomic <P*> m_value;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,101 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_ATOMICSTATE_H_INCLUDED
|
||||
#define BEAST_ATOMICSTATE_H_INCLUDED
|
||||
|
||||
/*============================================================================*/
|
||||
/**
|
||||
A thread safe state variable.
|
||||
|
||||
This provides a simplified interface to an integer used to control atomic
|
||||
state transitions. A state is distinguished by a single integer value.
|
||||
|
||||
@ingroup beast_core
|
||||
*/
|
||||
class BEAST_API AtomicState
|
||||
{
|
||||
public:
|
||||
/** Create a new state with an optional starting value.
|
||||
|
||||
@param initialState The initial state.
|
||||
*/
|
||||
|
||||
|
||||
explicit AtomicState (const int initialState = 0) noexcept
|
||||
:
|
||||
m_value (initialState)
|
||||
{
|
||||
}
|
||||
|
||||
/** Retrieve the current state.
|
||||
|
||||
This converts the object to an integer reflecting the current state.
|
||||
|
||||
Note that other threads may change the value immediately after this
|
||||
function returns. The caller is responsible for synchronizing.
|
||||
|
||||
@return The state at the time of the call.
|
||||
*/
|
||||
inline operator int () const
|
||||
{
|
||||
return m_value.get ();
|
||||
}
|
||||
|
||||
/** Attempt a state transition.
|
||||
|
||||
The current state is compared to `from`, and if the comparison is
|
||||
successful the state becomes `to`. The entire operation is atomic.
|
||||
|
||||
@param from The current state, for comparison.
|
||||
|
||||
@param to The desired new state.
|
||||
|
||||
@return true if the state transition succeeded.
|
||||
*/
|
||||
inline bool tryChangeState (const int from, const int to) noexcept
|
||||
{
|
||||
return m_value.compareAndSetBool (to, from);
|
||||
}
|
||||
|
||||
/** Perform a state transition.
|
||||
|
||||
This attempts to change the state and generates a diagnostic on
|
||||
failure. This routine can be used instead of tryChangeState()
|
||||
when program logic requires that the state change must succeed.
|
||||
|
||||
@param from The required current state.
|
||||
|
||||
@param to The new state.
|
||||
*/
|
||||
inline void changeState (const int from, const int to) noexcept
|
||||
{
|
||||
#if BEAST_DEBUG
|
||||
const bool success = tryChangeState (from, to);
|
||||
bassert (success);
|
||||
#else
|
||||
tryChangeState (from, to);
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
Atomic <int> m_value;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,126 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_RECYCLEDOBJECTPOOL_H_INCLUDED
|
||||
#define BEAST_RECYCLEDOBJECTPOOL_H_INCLUDED
|
||||
|
||||
/** A pool of objects which may be recycled.
|
||||
|
||||
This is a thread safe pool of objects that get re-used. It is
|
||||
primarily designed to eliminate the need for many memory allocations
|
||||
and frees when temporary buffers are needed for operations.
|
||||
|
||||
To use it, first declare a structure containing the information
|
||||
that you want to recycle. Then when you want to use a recycled object
|
||||
put a ScopedItem on your stack:
|
||||
|
||||
@code
|
||||
|
||||
struct StdString
|
||||
{
|
||||
std::string data;
|
||||
};
|
||||
|
||||
RecycledObjectPool <StdString> pool;
|
||||
|
||||
void foo ()
|
||||
{
|
||||
RecycledObjectPool <StdString>::ScopedItem item;
|
||||
|
||||
item.getObject ().data = "text";
|
||||
}
|
||||
|
||||
@endcode
|
||||
*/
|
||||
template <class Object>
|
||||
class RecycledObjectPool
|
||||
{
|
||||
public:
|
||||
struct Item : Object, LockFreeStack <Item>::Node, LeakChecked <Item>
|
||||
{
|
||||
};
|
||||
|
||||
class ScopedItem
|
||||
{
|
||||
public:
|
||||
explicit ScopedItem (RecycledObjectPool <Object>& pool)
|
||||
: m_pool (pool)
|
||||
, m_item (pool.get ())
|
||||
{
|
||||
}
|
||||
|
||||
~ScopedItem ()
|
||||
{
|
||||
m_pool.release (m_item);
|
||||
}
|
||||
|
||||
Object& getObject () noexcept
|
||||
{
|
||||
return *m_item;
|
||||
}
|
||||
|
||||
private:
|
||||
RecycledObjectPool <Object>& m_pool;
|
||||
Item* const m_item;
|
||||
};
|
||||
|
||||
public:
|
||||
RecycledObjectPool () noexcept
|
||||
{
|
||||
}
|
||||
|
||||
~RecycledObjectPool ()
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
Item* const item = m_stack.pop_front ();
|
||||
|
||||
if (item != nullptr)
|
||||
delete item;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Item* get ()
|
||||
{
|
||||
Item* item = m_stack.pop_front ();
|
||||
|
||||
if (item == nullptr)
|
||||
{
|
||||
item = new Item;
|
||||
|
||||
if (item == nullptr)
|
||||
Throw (std::bad_alloc ());
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
void release (Item* item) noexcept
|
||||
{
|
||||
m_stack.push_front (item);
|
||||
}
|
||||
|
||||
private:
|
||||
LockFreeStack <Item> m_stack;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,204 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_WEAKREFERENCE_H_INCLUDED
|
||||
#define BEAST_WEAKREFERENCE_H_INCLUDED
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
This class acts as a pointer which will automatically become null if the object
|
||||
to which it points is deleted.
|
||||
|
||||
To accomplish this, the source object needs to cooperate by performing a couple of simple tasks.
|
||||
It must embed a WeakReference::Master object, which stores a shared pointer object, and must clear
|
||||
this master pointer in its destructor.
|
||||
|
||||
E.g.
|
||||
@code
|
||||
class MyObject
|
||||
{
|
||||
public:
|
||||
MyObject()
|
||||
{
|
||||
// If you're planning on using your WeakReferences in a multi-threaded situation, you may choose
|
||||
// to create a WeakReference to the object here in the constructor, which will pre-initialise the
|
||||
// embedded object, avoiding an (extremely unlikely) race condition that could occur if multiple
|
||||
// threads overlap while creating the first WeakReference to it.
|
||||
}
|
||||
|
||||
~MyObject()
|
||||
{
|
||||
// This will zero all the references - you need to call this in your destructor.
|
||||
masterReference.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
// You need to embed a variable of this type, with the name "masterReference" inside your object. If the
|
||||
// variable is not public, you should make your class a friend of WeakReference<MyObject> so that the
|
||||
// WeakReference class can access it.
|
||||
WeakReference<MyObject>::Master masterReference;
|
||||
friend class WeakReference<MyObject>;
|
||||
};
|
||||
|
||||
// Here's an example of using a pointer..
|
||||
|
||||
MyObject* n = new MyObject();
|
||||
WeakReference<MyObject> myObjectRef = n;
|
||||
|
||||
MyObject* pointer1 = myObjectRef; // returns a valid pointer to 'n'
|
||||
delete n;
|
||||
MyObject* pointer2 = myObjectRef; // returns a null pointer
|
||||
@endcode
|
||||
|
||||
@see WeakReference::Master
|
||||
*/
|
||||
template <class ObjectType, class ReferenceCountingType = SharedObject>
|
||||
class WeakReference
|
||||
{
|
||||
public:
|
||||
/** Creates a null SafePointer. */
|
||||
inline WeakReference() noexcept {}
|
||||
|
||||
/** Creates a WeakReference that points at the given object. */
|
||||
WeakReference (ObjectType* const object) : holder (getRef (object)) {}
|
||||
|
||||
/** Creates a copy of another WeakReference. */
|
||||
WeakReference (const WeakReference& other) noexcept : holder (other.holder) {}
|
||||
|
||||
/** Copies another pointer to this one. */
|
||||
WeakReference& operator= (const WeakReference& other) { holder = other.holder; return *this; }
|
||||
|
||||
/** Copies another pointer to this one. */
|
||||
WeakReference& operator= (ObjectType* const newObject) { holder = getRef (newObject); return *this; }
|
||||
|
||||
#if BEAST_COMPILER_SUPPORTS_MOVE_SEMANTICS
|
||||
WeakReference (WeakReference&& other) noexcept : holder (static_cast <SharedRef&&> (other.holder)) {}
|
||||
WeakReference& operator= (WeakReference&& other) noexcept { holder = static_cast <SharedRef&&> (other.holder); return *this; }
|
||||
#endif
|
||||
|
||||
/** Returns the object that this pointer refers to, or null if the object no longer exists. */
|
||||
ObjectType* get() const noexcept { return holder != nullptr ? holder->get() : nullptr; }
|
||||
|
||||
/** Returns the object that this pointer refers to, or null if the object no longer exists. */
|
||||
operator ObjectType*() const noexcept { return get(); }
|
||||
|
||||
/** Returns the object that this pointer refers to, or null if the object no longer exists. */
|
||||
ObjectType* operator->() noexcept { return get(); }
|
||||
|
||||
/** Returns the object that this pointer refers to, or null if the object no longer exists. */
|
||||
const ObjectType* operator->() const noexcept { return get(); }
|
||||
|
||||
/** This returns true if this reference has been pointing at an object, but that object has
|
||||
since been deleted.
|
||||
|
||||
If this reference was only ever pointing at a null pointer, this will return false. Using
|
||||
operator=() to make this refer to a different object will reset this flag to match the status
|
||||
of the reference from which you're copying.
|
||||
*/
|
||||
bool wasObjectDeleted() const noexcept { return holder != nullptr && holder->get() == nullptr; }
|
||||
|
||||
bool operator== (ObjectType* const object) const noexcept { return get() == object; }
|
||||
bool operator!= (ObjectType* const object) const noexcept { return get() != object; }
|
||||
|
||||
//==============================================================================
|
||||
/** This class is used internally by the WeakReference class - don't use it directly
|
||||
in your code!
|
||||
@see WeakReference
|
||||
*/
|
||||
class SharedPointer
|
||||
: public ReferenceCountingType
|
||||
, public Uncopyable
|
||||
{
|
||||
public:
|
||||
explicit SharedPointer (ObjectType* const obj) noexcept : owner (obj) {}
|
||||
|
||||
inline ObjectType* get() const noexcept { return owner; }
|
||||
void clearPointer() noexcept { owner = nullptr; }
|
||||
|
||||
private:
|
||||
ObjectType* volatile owner;
|
||||
};
|
||||
|
||||
typedef SharedPtr<SharedPointer> SharedRef;
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
This class is embedded inside an object to which you want to attach WeakReference pointers.
|
||||
See the WeakReference class notes for an example of how to use this class.
|
||||
@see WeakReference
|
||||
*/
|
||||
class Master : public Uncopyable
|
||||
{
|
||||
public:
|
||||
Master() noexcept {}
|
||||
|
||||
~Master()
|
||||
{
|
||||
// You must remember to call clear() in your source object's destructor! See the notes
|
||||
// for the WeakReference class for an example of how to do this.
|
||||
bassert (sharedPointer == nullptr || sharedPointer->get() == nullptr);
|
||||
}
|
||||
|
||||
/** The first call to this method will create an internal object that is shared by all weak
|
||||
references to the object.
|
||||
*/
|
||||
SharedPointer* getSharedPointer (ObjectType* const object)
|
||||
{
|
||||
if (sharedPointer == nullptr)
|
||||
{
|
||||
sharedPointer = new SharedPointer (object);
|
||||
}
|
||||
else
|
||||
{
|
||||
// You're trying to create a weak reference to an object that has already been deleted!!
|
||||
bassert (sharedPointer->get() != nullptr);
|
||||
}
|
||||
|
||||
return sharedPointer;
|
||||
}
|
||||
|
||||
/** The object that owns this master pointer should call this before it gets destroyed,
|
||||
to zero all the references to this object that may be out there. See the WeakReference
|
||||
class notes for an example of how to do this.
|
||||
*/
|
||||
void clear()
|
||||
{
|
||||
if (sharedPointer != nullptr)
|
||||
sharedPointer->clearPointer();
|
||||
}
|
||||
|
||||
private:
|
||||
SharedRef sharedPointer;
|
||||
};
|
||||
|
||||
private:
|
||||
SharedRef holder;
|
||||
|
||||
static inline SharedPointer* getRef (ObjectType* const o)
|
||||
{
|
||||
return (o != nullptr) ? o->masterReference.getSharedPointer (o) : nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // BEAST_WEAKREFERENCE_H_INCLUDED
|
||||
@@ -63,7 +63,8 @@ namespace functional
|
||||
using std::ref;
|
||||
using std::cref;
|
||||
using std::bind;
|
||||
using std::function;
|
||||
|
||||
//using std::function;
|
||||
|
||||
}
|
||||
|
||||
@@ -166,7 +167,8 @@ namespace functional
|
||||
using std::tr1::ref;
|
||||
using std::tr1::cref;
|
||||
using std::tr1::bind;
|
||||
using std::tr1::function;
|
||||
|
||||
//using std::tr1::function;
|
||||
|
||||
}
|
||||
|
||||
@@ -269,7 +271,8 @@ namespace functional
|
||||
using boost::ref;
|
||||
using boost::cref;
|
||||
using boost::bind;
|
||||
using boost::function;
|
||||
|
||||
//using boost::function;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
Semaphore::Semaphore (int initialCount)
|
||||
: m_counter (initialCount)
|
||||
{
|
||||
}
|
||||
|
||||
Semaphore::~Semaphore ()
|
||||
{
|
||||
// Can't delete the semaphore while threads are waiting on it!!
|
||||
bassert (m_waitingThreads.pop_front () == nullptr);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
WaitingThread* waitingThread = m_deleteList.pop_front ();
|
||||
|
||||
if (waitingThread != nullptr)
|
||||
delete waitingThread;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Semaphore::signal (int amount)
|
||||
{
|
||||
bassert (amount > 0);
|
||||
|
||||
while (amount--)
|
||||
{
|
||||
// Make counter and list operations atomic.
|
||||
LockType::ScopedLockType lock (m_mutex);
|
||||
|
||||
if (++m_counter <= 0)
|
||||
{
|
||||
WaitingThread* waitingThread = m_waitingThreads.pop_front ();
|
||||
|
||||
bassert (waitingThread != nullptr);
|
||||
|
||||
waitingThread->signal ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Semaphore::wait (int timeOutMilliseconds)
|
||||
{
|
||||
bool signaled = true;
|
||||
|
||||
// Always prepare the WaitingThread object first, either
|
||||
// from the delete list or through a new allocation.
|
||||
//
|
||||
WaitingThread* waitingThread = m_deleteList.pop_front ();
|
||||
|
||||
if (waitingThread == nullptr)
|
||||
waitingThread = new WaitingThread;
|
||||
|
||||
{
|
||||
// Make counter and list operations atomic.
|
||||
LockType::ScopedLockType lock (m_mutex);
|
||||
|
||||
if (--m_counter >= 0)
|
||||
{
|
||||
// Acquired the resource so put waitingThread back.
|
||||
m_deleteList.push_front (waitingThread);
|
||||
|
||||
waitingThread = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Out of resources, go on to the waiting list.
|
||||
m_waitingThreads.push_front (waitingThread);
|
||||
}
|
||||
}
|
||||
|
||||
// Do we need to wait?
|
||||
if (waitingThread != nullptr)
|
||||
{
|
||||
// Yes so do it.
|
||||
signaled = waitingThread->wait (timeOutMilliseconds);
|
||||
|
||||
// If the wait is satisfied, then we've been taken off the
|
||||
// waiting list so put waitingThread back in the delete list.
|
||||
//
|
||||
m_deleteList.push_front (waitingThread);
|
||||
}
|
||||
|
||||
return signaled;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
Semaphore::WaitingThread::WaitingThread ()
|
||||
: m_event (false) // auto-reset
|
||||
{
|
||||
}
|
||||
|
||||
bool Semaphore::WaitingThread::wait (int timeOutMilliseconds)
|
||||
{
|
||||
return m_event.wait (timeOutMilliseconds);
|
||||
}
|
||||
|
||||
void Semaphore::WaitingThread::signal ()
|
||||
{
|
||||
m_event.signal ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// VFALCO TODO Unit Tests!
|
||||
@@ -1,82 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_SEMAPHORE_H_INCLUDED
|
||||
#define BEAST_SEMAPHORE_H_INCLUDED
|
||||
|
||||
/*============================================================================*/
|
||||
/**
|
||||
A semaphore.
|
||||
|
||||
This provides a traditional semaphore synchronization primitive. There is no
|
||||
upper limit on the number of signals.
|
||||
|
||||
@note There is no tryWait() or timeout facility for acquiring a resource.
|
||||
|
||||
@ingroup beast_core
|
||||
*/
|
||||
class BEAST_API Semaphore
|
||||
{
|
||||
public:
|
||||
/** Create a semaphore with the specified number of resources.
|
||||
|
||||
@param initialCount The starting number of resources.
|
||||
*/
|
||||
explicit Semaphore (int initialCount = 0);
|
||||
|
||||
~Semaphore ();
|
||||
|
||||
/** Increase the number of available resources.
|
||||
|
||||
@param amount The number of new resources available.
|
||||
*/
|
||||
void signal (int amount = 1);
|
||||
|
||||
/** Wait for a resource.
|
||||
|
||||
A negative time-out value means that the method will wait indefinitely.
|
||||
|
||||
@returns true if the event has been signalled, false if the timeout expires.
|
||||
*/
|
||||
bool wait (int timeOutMilliseconds = -1);
|
||||
|
||||
private:
|
||||
class WaitingThread
|
||||
: public LockFreeStack <WaitingThread>::Node
|
||||
, LeakChecked <WaitingThread>
|
||||
{
|
||||
public:
|
||||
WaitingThread ();
|
||||
|
||||
bool wait (int timeOutMilliseconds);
|
||||
void signal ();
|
||||
|
||||
private:
|
||||
WaitableEvent m_event;
|
||||
};
|
||||
|
||||
typedef SpinLock LockType;
|
||||
|
||||
LockType m_mutex;
|
||||
Atomic <int> m_counter;
|
||||
LockFreeStack <WaitingThread> m_waitingThreads;
|
||||
LockFreeStack <WaitingThread> m_deleteList;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -134,7 +134,7 @@ private:
|
||||
CPUMeter m_usage; // CPU utilization across threads
|
||||
String m_threadNames; // The name to give each thread
|
||||
WaitableEvent m_allPaused; // signaled when all threads paused
|
||||
Semaphore m_semaphore; // each pending task is 1 resource
|
||||
semaphore m_semaphore; // each pending task is 1 resource
|
||||
int m_numberOfThreads; // how many we want active now
|
||||
Atomic <int> m_activeCount; // to know when all are paused
|
||||
Atomic <int> m_pauseCount; // how many threads need to pause now
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "BeastConfig.h"
|
||||
|
||||
#include "beast_crypto.h"
|
||||
|
||||
namespace beast
|
||||
{
|
||||
|
||||
#include "math/BinaryEncoding.cpp"
|
||||
#include "math/UnsignedInteger.cpp"
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_BEAST_CRYPTO_H_INCLUDED
|
||||
#define BEAST_BEAST_CRYPTO_H_INCLUDED
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/* If you fail to make sure that all your compile units are building Beast with
|
||||
the same set of option flags, then there's a risk that different compile
|
||||
units will treat the classes as having different memory layouts, leading to
|
||||
very nasty memory corruption errors when they all get linked together.
|
||||
That's why it's best to always include the BeastConfig.h file before any
|
||||
beast headers.
|
||||
*/
|
||||
#ifndef BEAST_BEASTCONFIG_H_INCLUDED
|
||||
# ifdef _MSC_VER
|
||||
# pragma message ("Have you included your BeastConfig.h file before including the Beast headers?")
|
||||
# else
|
||||
# warning "Have you included your BeastConfig.h file before including the Beast headers?"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include "../beast_core/beast_core.h"
|
||||
#include "../../beast/crypto/MurmurHash.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace beast {
|
||||
|
||||
# include "math/UnsignedIntegerCalc.h"
|
||||
#include "math/UnsignedInteger.h"
|
||||
#include "math/BinaryEncoding.h"
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,395 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
/** Generic algorithms for base encoding and decoding. */
|
||||
class BinaryEncoding
|
||||
{
|
||||
public:
|
||||
/** Concept: Conversion
|
||||
|
||||
X denotes a Conversion class, a is a value of type X,
|
||||
i is an integral type.
|
||||
|
||||
Requirements:
|
||||
|
||||
Expression Type Notes/Contracts
|
||||
------------- ----------- ------------------
|
||||
X a;
|
||||
X::radix size_type constexpr
|
||||
a.map (i) char maps base numeral to a char
|
||||
*/
|
||||
|
||||
/** Encode the unsigned integer into a string using the specified conversion. */
|
||||
template <typename Conversion, std::size_t Bytes>
|
||||
static std::string encode (UnsignedInteger <Bytes> v, Conversion c = Conversion ())
|
||||
{
|
||||
// bi is destroyed in this process
|
||||
typename UnsignedInteger <Bytes>::CalcType bi (v.toCalcType ());
|
||||
std::size_t const radix (Conversion::radix);
|
||||
std::string s;
|
||||
s.reserve (bi.size() * 3); // guess
|
||||
while (bi.isNotZero ())
|
||||
{
|
||||
std::size_t const m (bi % radix);
|
||||
bi /= radix;
|
||||
s.push_back (c.map (m));
|
||||
}
|
||||
std::reverse (s.begin(), s.end());
|
||||
return s;
|
||||
}
|
||||
|
||||
/** Decode the string into an unsigned integer.
|
||||
The size must match exactly
|
||||
@return `true` on success.
|
||||
*/
|
||||
template <typename Conversion, std::size_t Bytes>
|
||||
static bool decode (UnsignedInteger <Bytes>& rv,
|
||||
std::string const& s, Conversion c = Conversion ())
|
||||
{
|
||||
typename UnsignedInteger <Bytes>::CalcType bi (rv.toCalcType (false));
|
||||
std::size_t const radix (Conversion::radix);
|
||||
bi.clear ();
|
||||
for (std::string::const_iterator iter (s.begin()); iter != s.end(); ++iter)
|
||||
{
|
||||
int const v (c.invert (*iter));
|
||||
if (v == -1)
|
||||
return false;
|
||||
bi *= radix;
|
||||
bi += v;
|
||||
}
|
||||
bi.toCanonical();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Some common code
|
||||
template <class Conversion>
|
||||
class BaseConversion
|
||||
{
|
||||
public:
|
||||
char map (std::size_t i) const
|
||||
{
|
||||
return Conversion::alphabet () [i];
|
||||
}
|
||||
|
||||
int invert (char c) const
|
||||
{
|
||||
return Conversion::inverse_alphabet () [c];
|
||||
}
|
||||
|
||||
static std::vector <int> const& inverse_alphabet ()
|
||||
{
|
||||
static std::vector <int> t (invert (Conversion::alphabet(), Conversion::radix));
|
||||
return t;
|
||||
}
|
||||
|
||||
/** Build the inverse mapping table from characters to digits. */
|
||||
static std::vector <int> invert (std::string const& alphabet, std::size_t radix)
|
||||
{
|
||||
std::vector <int> table (256, -1);
|
||||
for (std::size_t i (0); i < radix; ++i)
|
||||
table [alphabet [i]] = i;
|
||||
return table;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Foolproof hexadecimal encoding and decoding facility.
|
||||
This is to check the correctness of the more complex converters.
|
||||
*/
|
||||
class HexEncoding
|
||||
{
|
||||
public:
|
||||
template <std::size_t Bytes>
|
||||
static std::string encode (UnsignedInteger <Bytes> const& v)
|
||||
{
|
||||
std::string s;
|
||||
uint8 const* src (v.cbegin()-1);
|
||||
char const* const tab (alphabet().c_str());
|
||||
s.reserve (Bytes * 2);
|
||||
for (std::size_t bytes (v.size);bytes--;)
|
||||
{
|
||||
uint8 const v (*++src);
|
||||
s.push_back (tab [v>>4]);
|
||||
s.push_back (tab [v&0x0f]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
template <std::size_t Bytes>
|
||||
static bool decode (UnsignedInteger <Bytes>& rv,
|
||||
std::string const& s)
|
||||
{
|
||||
// can't have an odd size
|
||||
if (s.size() & 1)
|
||||
return false;
|
||||
uint8* dest (rv.begin()-1);
|
||||
int const* const tab (&inverse_alphabet().front());
|
||||
for (std::string::const_iterator iter (s.begin()); iter != s.end();)
|
||||
{
|
||||
int const n1 (tab [*iter++]);
|
||||
if (n1 == -1)
|
||||
return false;
|
||||
int const n2 (tab [*iter++]);
|
||||
if (n2 == -1)
|
||||
return false;
|
||||
*++dest = ((uint8)((n1<<4)|n2));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string const& alphabet ()
|
||||
{
|
||||
static std::string s ("0123456789ABCDEF");
|
||||
return s;
|
||||
}
|
||||
|
||||
static std::vector <int> const& inverse_alphabet ()
|
||||
{
|
||||
static std::vector <int> s (invert (alphabet(), 16));
|
||||
return s;
|
||||
}
|
||||
|
||||
/** Build the inverse mapping table from characters to digits. */
|
||||
static std::vector <int> invert (std::string const& alphabet, std::size_t radix)
|
||||
{
|
||||
std::vector <int> table (256, -1);
|
||||
for (std::size_t i (0); i < radix; ++i)
|
||||
table [alphabet [i]] = i;
|
||||
return table;
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Base58 conversion used by Bitcoin. */
|
||||
class BitcoinBase58Conversion : public BaseConversion <BitcoinBase58Conversion>
|
||||
{
|
||||
public:
|
||||
static std::size_t const radix = 58;
|
||||
|
||||
char const* name () const
|
||||
{
|
||||
return "BitcoinBase58";
|
||||
}
|
||||
|
||||
static std::string const& alphabet ()
|
||||
{
|
||||
static std::string s (
|
||||
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
||||
);
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Base58 conversion used by Ripple. */
|
||||
class RippleBase58Conversion : public BaseConversion <RippleBase58Conversion>
|
||||
{
|
||||
public:
|
||||
static std::size_t const radix = 58;
|
||||
|
||||
char const* name () const
|
||||
{
|
||||
return "RippleBase58";
|
||||
}
|
||||
|
||||
static std::string const& alphabet ()
|
||||
{
|
||||
static std::string s (
|
||||
"rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz"
|
||||
);
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class Base64Conversion : public BaseConversion <Base64Conversion>
|
||||
{
|
||||
public:
|
||||
static std::size_t const radix = 64;
|
||||
|
||||
char const* name () const
|
||||
{
|
||||
return "Base64";
|
||||
}
|
||||
|
||||
static std::string const& alphabet ()
|
||||
{
|
||||
static std::string s (
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
);
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class Base16Conversion : public BaseConversion <Base16Conversion>
|
||||
{
|
||||
public:
|
||||
static std::size_t const radix = 16;
|
||||
|
||||
char const* name () const
|
||||
{
|
||||
return "Hex";
|
||||
}
|
||||
|
||||
static std::string const& alphabet ()
|
||||
{
|
||||
static std::string s (
|
||||
"0123456789ABCDEF"
|
||||
);
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class BinaryEncodingTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
// This is a baseline for the other tests
|
||||
template <std::size_t Bytes>
|
||||
void testBase16 ()
|
||||
{
|
||||
beginTestCase ("base16");
|
||||
for (int i = 0; i < 50; ++i)
|
||||
{
|
||||
typedef UnsignedInteger <Bytes> UInt;
|
||||
UInt v0;
|
||||
random().fillBitsRandomly (v0.begin(), UInt::size);
|
||||
std::string const good (HexEncoding::encode (v0));
|
||||
|
||||
UInt v1;
|
||||
bool const success (HexEncoding::decode (v1, good));
|
||||
if (expect (success))
|
||||
{
|
||||
expect (v0 == v1);
|
||||
|
||||
Base16Conversion c;
|
||||
std::string const check (BinaryEncoding::encode (v0, c));
|
||||
if (! expect (good == check))
|
||||
logMessage (String ("expected ") + good + " but got " + check);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <std::size_t Bytes>
|
||||
void testBase64Bytes (
|
||||
std::string const& vin, std::string const& vout,
|
||||
Base64Conversion c = Base64Conversion ())
|
||||
{
|
||||
typedef UnsignedInteger <Bytes> UInt;
|
||||
UInt v1 (vin.c_str());
|
||||
std::string const s1 (BinaryEncoding::encode (v1, c));
|
||||
logMessage (vout + " to " + s1);
|
||||
expect (vout == s1);
|
||||
|
||||
UInt v2;
|
||||
bool const success (BinaryEncoding::decode (v2, vout, c));
|
||||
if (expect (success))
|
||||
{
|
||||
std::string const s2 (BinaryEncoding::encode (v2, c));
|
||||
logMessage (vin + " to " + s2);
|
||||
//expect (vin == v2);
|
||||
}
|
||||
}
|
||||
|
||||
void testBase64 ()
|
||||
{
|
||||
beginTestCase ("Base64");
|
||||
|
||||
// input (uint)
|
||||
std::string const vin [] = {
|
||||
"","f","fo","foo","foob","fooba","foobar"
|
||||
};
|
||||
|
||||
// output (encoded string)
|
||||
std::string const vout [] = {
|
||||
"","Zg==","Zm8=","Zm9v","Zm9vYg==","Zm9vYmE=","Zm9vYmFy"
|
||||
};
|
||||
|
||||
//testBase64Bytes <0> (vin [0], vout [0]);
|
||||
testBase64Bytes <1> (vin [1], vout [1]);
|
||||
testBase64Bytes <2> (vin [2], vout [2]);
|
||||
testBase64Bytes <3> (vin [3], vout [3]);
|
||||
testBase64Bytes <4> (vin [4], vout [4]);
|
||||
testBase64Bytes <5> (vin [5], vout [5]);
|
||||
testBase64Bytes <6> (vin [6], vout [6]);
|
||||
}
|
||||
|
||||
template <typename Conversion, std::size_t Bytes>
|
||||
void testEncode (Conversion c = Conversion())
|
||||
{
|
||||
typedef UnsignedInteger <Bytes> UInt;
|
||||
|
||||
beginTestCase (String (c.name()) + " <" + String::fromNumber (Bytes) + ">");
|
||||
|
||||
for (int i = 0; i < 50; ++i)
|
||||
{
|
||||
UInt v1;
|
||||
random().fillBitsRandomly (v1.begin(), UInt::size);
|
||||
std::string const s1 (BinaryEncoding::encode (v1, c));
|
||||
|
||||
UInt v2;
|
||||
bool success (BinaryEncoding::decode (v2, s1, c));
|
||||
if (expect (success))
|
||||
expect (v1 == v2);
|
||||
}
|
||||
}
|
||||
|
||||
void runTest ()
|
||||
{
|
||||
testBase16 <10> ();
|
||||
|
||||
#if 0
|
||||
testEncode <Base16Conversion, 1> ();
|
||||
testEncode <Base16Conversion, 2> ();
|
||||
testEncode <Base16Conversion, 3> ();
|
||||
testEncode <Base16Conversion, 4> ();
|
||||
testEncode <Base16Conversion, 5> ();
|
||||
testEncode <Base16Conversion, 6> ();
|
||||
testEncode <Base16Conversion, 7> ();
|
||||
testEncode <Base16Conversion, 8> ();
|
||||
testEncode <Base16Conversion, 9> ();
|
||||
testEncode <Base16Conversion, 10> ();
|
||||
|
||||
testBase64 ();
|
||||
testEncode <Base64Conversion, 20> ();
|
||||
testEncode <Base64Conversion, 33> ();
|
||||
testEncode <RippleBase58Conversion, 20> ();
|
||||
testEncode <RippleBase58Conversion, 33> ();
|
||||
testEncode <BitcoinBase58Conversion, 20> ();
|
||||
testEncode <BitcoinBase58Conversion, 33> ();
|
||||
#endif
|
||||
}
|
||||
|
||||
BinaryEncodingTests () : UnitTest ("BinaryEncoding", "beast", runManual)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
static BinaryEncodingTests BinaryEncodingTests;
|
||||
@@ -1,24 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_CRYPTO_BINARYENCODING_H_INCLUDED
|
||||
#define BEAST_CRYPTO_BINARYENCODING_H_INCLUDED
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,396 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/* Copyright (c) 2013 the authors listed at the following URL, and/or
|
||||
the authors of referenced articles or incorporated external code:
|
||||
http://en.literateprograms.org/Arbitrary-precision_integer_arithmetic_(C)?action=history&offset=20100923155004
|
||||
|
||||
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.
|
||||
|
||||
Retrieved from: http://en.literateprograms.org/Arbitrary-precision_integer_arithmetic_(C)?oldid=16902
|
||||
*/
|
||||
|
||||
namespace multiprecsion
|
||||
{
|
||||
|
||||
#if 0
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
typedef unsigned short component_t;
|
||||
typedef unsigned long double_component_t;
|
||||
|
||||
#define MAX_COMPONENT ((component_t)(-1))
|
||||
#define COMPONENT_BITS (sizeof(component_t)*CHAR_BIT)
|
||||
|
||||
#define LOG_2_10 3.3219280948873623478703194294894
|
||||
|
||||
#define MIN(x,y) ((x)<(y) ? (x) : (y))
|
||||
#define MAX(x,y) ((x)>(y) ? (x) : (y))
|
||||
|
||||
typedef struct {
|
||||
component_t* c; /* least-significant word first */
|
||||
int num_components;
|
||||
} integer;
|
||||
|
||||
|
||||
integer create_integer(int components);
|
||||
void free_integer(integer i);
|
||||
void set_zero_integer(integer i);
|
||||
void copy_integer(integer source, integer target);
|
||||
void add_integer(integer left, integer right, integer result);
|
||||
void subtract_integer(integer left, integer right, integer result);
|
||||
void multiply_small_integer(integer left, component_t right, integer result);
|
||||
void multiply_integer(integer left, integer right, integer result);
|
||||
int compare_integers(integer left, integer right);
|
||||
void shift_left_one_integer(integer arg);
|
||||
void shift_right_one_integer(integer arg);
|
||||
component_t mod_small_integer(integer left, component_t right);
|
||||
void mod_integer(integer left, integer right, integer result);
|
||||
void divide_small_integer(integer left, component_t right, integer result);
|
||||
integer string_to_integer(char* s);
|
||||
char* integer_to_string(integer x);
|
||||
int is_zero_integer(integer x);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
integer create_integer(int components) {
|
||||
integer result;
|
||||
result.num_components = components;
|
||||
result.c = (component_t*)malloc(sizeof(component_t)*components);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void free_integer(integer i) {
|
||||
free(i.c);
|
||||
}
|
||||
|
||||
|
||||
void set_zero_integer(integer i) {
|
||||
memset(i.c, 0, sizeof(component_t)*i.num_components);
|
||||
}
|
||||
|
||||
|
||||
int is_zero_integer(integer x) {
|
||||
int i;
|
||||
for(i=0; i < x.num_components; i++) {
|
||||
if (x.c[i] != 0) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void copy_integer(integer source, integer target) {
|
||||
memmove(target.c, source.c,
|
||||
sizeof(component_t)*MIN(source.num_components, target.num_components));
|
||||
if (target.num_components > source.num_components) {
|
||||
memset(target.c + source.num_components, 0,
|
||||
sizeof(component_t)*(target.num_components - source.num_components));
|
||||
}
|
||||
}
|
||||
|
||||
void add_integer(integer left, integer right, integer result) {
|
||||
double_component_t carry = 0;
|
||||
int i;
|
||||
for(i=0; i<left.num_components || i<right.num_components || carry != 0; i++) {
|
||||
double_component_t partial_sum = carry;
|
||||
carry = 0;
|
||||
if (i < left.num_components) partial_sum += left.c[i];
|
||||
if (i < right.num_components) partial_sum += right.c[i];
|
||||
if (partial_sum > MAX_COMPONENT) {
|
||||
partial_sum &= MAX_COMPONENT;
|
||||
carry = 1;
|
||||
}
|
||||
result.c[i] = (component_t)partial_sum;
|
||||
}
|
||||
for ( ; i < result.num_components; i++) { result.c[i] = 0; }
|
||||
}
|
||||
|
||||
void subtract_integer(integer left, integer right, integer result) {
|
||||
int borrow = 0;
|
||||
int i;
|
||||
for(i=0; i<left.num_components; i++) {
|
||||
double_component_t lhs = left.c[i];
|
||||
double_component_t rhs = (i < right.num_components) ? right.c[i] : 0;
|
||||
if (borrow) {
|
||||
if (lhs <= rhs) {
|
||||
/* leave borrow set to 1 */
|
||||
lhs += (MAX_COMPONENT + 1) - 1;
|
||||
} else {
|
||||
borrow = 0;
|
||||
lhs--;
|
||||
}
|
||||
}
|
||||
if (lhs < rhs) {
|
||||
borrow = 1;
|
||||
lhs += MAX_COMPONENT + 1;
|
||||
}
|
||||
result.c[i] = lhs - rhs;
|
||||
}
|
||||
for ( ; i < result.num_components; i++) { result.c[i] = 0; }
|
||||
}
|
||||
|
||||
void multiply_small_integer(integer left, component_t right, integer result) {
|
||||
double_component_t carry = 0;
|
||||
int i;
|
||||
for(i=0; i<left.num_components || carry != 0; i++) {
|
||||
double_component_t partial_sum = carry;
|
||||
carry = 0;
|
||||
if (i < left.num_components) partial_sum += left.c[i]*right;
|
||||
carry = partial_sum >> COMPONENT_BITS;
|
||||
result.c[i] = (component_t)(partial_sum & MAX_COMPONENT);
|
||||
}
|
||||
for ( ; i < result.num_components; i++) { result.c[i] = 0; }
|
||||
}
|
||||
|
||||
void multiply_integer(integer left, integer right, integer result) {
|
||||
int i, lidx, ridx;
|
||||
double_component_t carry = 0;
|
||||
int max_size_no_carry;
|
||||
int left_max_component = left.num_components - 1;
|
||||
int right_max_component = right.num_components - 1;
|
||||
while(left.c[left_max_component] == 0) left_max_component--;
|
||||
while(right.c[right_max_component] == 0) right_max_component--;
|
||||
max_size_no_carry = left_max_component + right_max_component;
|
||||
for(i=0; i <= max_size_no_carry || carry != 0; i++) {
|
||||
double_component_t partial_sum = carry;
|
||||
carry = 0;
|
||||
lidx = MIN(i, left_max_component);
|
||||
ridx = i - lidx;
|
||||
while(lidx >= 0 && ridx <= right_max_component) {
|
||||
partial_sum += ((double_component_t)left.c[lidx])*right.c[ridx];
|
||||
carry += partial_sum >> COMPONENT_BITS;
|
||||
partial_sum &= MAX_COMPONENT;
|
||||
lidx--; ridx++;
|
||||
}
|
||||
result.c[i] = partial_sum;
|
||||
}
|
||||
for ( ; i < result.num_components; i++) { result.c[i] = 0; }
|
||||
}
|
||||
|
||||
int compare_integers(integer left, integer right) {
|
||||
int i = MAX(left.num_components - 1, right.num_components - 1);
|
||||
for ( ; i >= 0; i--) {
|
||||
component_t left_comp =
|
||||
(i < left.num_components) ? left.c[i] : 0;
|
||||
component_t right_comp =
|
||||
(i < right.num_components) ? right.c[i] : 0;
|
||||
if (left_comp < right_comp)
|
||||
return -1;
|
||||
else if (left_comp > right_comp)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void shift_left_one_integer(integer arg) {
|
||||
int i;
|
||||
arg.c[arg.num_components - 1] <<= 1;
|
||||
for (i = arg.num_components - 2; i >= 0; i--) {
|
||||
arg.c[i + 1] |= arg.c[i] >> (COMPONENT_BITS - 1);
|
||||
arg.c[i] <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
void shift_right_one_integer(integer arg) {
|
||||
int i;
|
||||
arg.c[0] >>= 1;
|
||||
for (i = 1; i < arg.num_components; i++) {
|
||||
arg.c[i - 1] |= (arg.c[i] & 1) << (COMPONENT_BITS - 1);
|
||||
arg.c[i] >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
component_t mod_small_integer(integer left, component_t right) {
|
||||
double_component_t mod_two_power = 1;
|
||||
double_component_t result = 0;
|
||||
int i, bit;
|
||||
for(i=0; i<left.num_components; i++) {
|
||||
for(bit=0; bit<COMPONENT_BITS; bit++) {
|
||||
if ((left.c[i] & (1 << bit)) != 0) {
|
||||
result += mod_two_power;
|
||||
if (result >= right) {
|
||||
result -= right;
|
||||
}
|
||||
}
|
||||
mod_two_power <<= 1;
|
||||
if (mod_two_power >= right) {
|
||||
mod_two_power -= right;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (component_t)result;
|
||||
}
|
||||
|
||||
void mod_integer(integer left, integer right, integer result) {
|
||||
integer mod_two_power = create_integer(right.num_components + 1);
|
||||
int i, bit;
|
||||
set_zero_integer(result);
|
||||
set_zero_integer(mod_two_power);
|
||||
mod_two_power.c[0] = 1;
|
||||
for(i=0; i<left.num_components; i++) {
|
||||
for(bit=0; bit<COMPONENT_BITS; bit++) {
|
||||
if ((left.c[i] & (1 << bit)) != 0) {
|
||||
add_integer(result, mod_two_power, result);
|
||||
if (compare_integers(result, right) >= 0) {
|
||||
subtract_integer(result, right, result);
|
||||
}
|
||||
}
|
||||
shift_left_one_integer(mod_two_power);
|
||||
if (compare_integers(mod_two_power, right) >= 0) {
|
||||
subtract_integer(mod_two_power, right, mod_two_power);
|
||||
}
|
||||
}
|
||||
}
|
||||
free_integer(mod_two_power);
|
||||
}
|
||||
|
||||
void divide_small_integer(integer left, component_t right, integer result) {
|
||||
double_component_t dividend = 0;
|
||||
int i;
|
||||
for (i = left.num_components - 1; i >= 0; i--) {
|
||||
dividend |= left.c[i];
|
||||
result.c[i] = dividend/right;
|
||||
dividend = (dividend % right) << COMPONENT_BITS;
|
||||
}
|
||||
}
|
||||
|
||||
integer string_to_integer(char* s) {
|
||||
integer result = create_integer((int)ceil(LOG_2_10*strlen(s)/COMPONENT_BITS));
|
||||
set_zero_integer(result);
|
||||
integer digit = create_integer(1);
|
||||
int i;
|
||||
for (i = 0; s[i] != '\0'; i++) {
|
||||
multiply_small_integer(result, 10, result);
|
||||
digit.c[0] = s[i] - '0';
|
||||
add_integer(result, digit, result);
|
||||
}
|
||||
free_integer(digit);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
char* integer_to_string(integer x) {
|
||||
int i, result_len;
|
||||
char* result =
|
||||
(char*)malloc((int)ceil(COMPONENT_BITS*x.num_components/LOG_2_10) + 2);
|
||||
integer ten = create_integer(1);
|
||||
ten.c[0] = 10;
|
||||
|
||||
if (is_zero_integer(x)) {
|
||||
strcpy(result, "0");
|
||||
} else {
|
||||
for (i = 0; !is_zero_integer(x); i++) {
|
||||
result[i] = (char)mod_small_integer(x, 10) + '0';
|
||||
divide_small_integer(x, 10, x);
|
||||
}
|
||||
result[i] = '\0';
|
||||
}
|
||||
|
||||
result_len = strlen(result);
|
||||
for(i=0; i < result_len/2; i++) {
|
||||
char temp = result[i];
|
||||
result[i] = result[result_len - i - 1];
|
||||
result[result_len - i - 1] = temp;
|
||||
}
|
||||
|
||||
free_integer(ten);
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class UnsignedIntegerTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
UnsignedIntegerTests () : UnitTest ("UnsignedInteger", "beast")
|
||||
{
|
||||
}
|
||||
|
||||
template <unsigned int Bytes>
|
||||
void runTest ()
|
||||
{
|
||||
typedef UnsignedInteger <Bytes> UInt;
|
||||
|
||||
String s;
|
||||
|
||||
s << "Bytes=" << String(Bytes);
|
||||
|
||||
beginTestCase (s);
|
||||
|
||||
UInt zero;
|
||||
zero.fill (0);
|
||||
expect (zero.isZero (), "should be zero");
|
||||
expect (! zero.isNotZero (), "sould not be non-zero");
|
||||
|
||||
UInt one (UInt::createFromInteger (1U));
|
||||
expect (one == UInt::createFromInteger (1U), "should be equal");
|
||||
|
||||
expect (! one.isZero (), "should not be zero");
|
||||
expect (one.isNotZero (), "sould be non-zero");
|
||||
|
||||
expect (zero < one, "should be less");
|
||||
expect (one > zero, "should be greater");
|
||||
expect (zero >= zero, "should be less than or equal");
|
||||
expect (one <= one, "should be less than or equal");
|
||||
|
||||
expect (zero == zero, "should be equal");
|
||||
expect (zero != one, "should not be equal");
|
||||
|
||||
expect (zero == UInt::createFromInteger (0U), "should be zero");
|
||||
expect (one == UInt::createFromInteger (1U), "should be one");
|
||||
expect (one != UInt::createFromInteger (2U), "should not be two");
|
||||
|
||||
UInt largest = UInt::createFilled (0xff);
|
||||
|
||||
expect (largest > zero && largest > one, "should be greater");
|
||||
}
|
||||
|
||||
void runTest()
|
||||
{
|
||||
runTest <16> ();
|
||||
runTest <33> ();
|
||||
}
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
static UnsignedIntegerTests unsignedIntegerTests;
|
||||
@@ -1,299 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_CRYPTO_UNSIGNEDINTEGER_H_INCLUDED
|
||||
#define BEAST_CRYPTO_UNSIGNEDINTEGER_H_INCLUDED
|
||||
|
||||
/** Represents a set of bits of fixed size.
|
||||
|
||||
The data is stored in "canonical" format which is network (big endian)
|
||||
byte order, most significant byte first.
|
||||
|
||||
In this implementation the pointer to the beginning of the canonical format
|
||||
may not be aligned.
|
||||
*/
|
||||
template <std::size_t Bytes>
|
||||
class UnsignedInteger : public SafeBool <UnsignedInteger <Bytes> >
|
||||
{
|
||||
public:
|
||||
/** Constant for determining the number of bytes. */
|
||||
static std::size_t const size = Bytes;
|
||||
|
||||
// The underlying integer type we use when converting to calculation format.
|
||||
typedef uint32 IntCalcType;
|
||||
|
||||
// The type of object resulting from a conversion to calculation format.
|
||||
typedef UnsignedIntegerCalc <IntCalcType> CalcType;
|
||||
|
||||
// Standard container compatibility
|
||||
typedef uint8 value_type;
|
||||
typedef value_type* iterator;
|
||||
typedef value_type const* const_iterator;
|
||||
|
||||
/** Hardened hash function for use with hash based containers.
|
||||
The seed is used to make the hash unpredictable. This prevents
|
||||
attackers from exploiting crafted inputs to produce degenerate
|
||||
containers.
|
||||
*/
|
||||
class hasher
|
||||
{
|
||||
public:
|
||||
/** Construct a hash function
|
||||
If a seed is specified it will be used, else a random seed
|
||||
will be generated from the system
|
||||
@param seedToUse An optional seed to use.
|
||||
*/
|
||||
explicit hasher (std::size_t seedToUse = Random::getSystemRandom ().nextInt ())
|
||||
: m_seed (seedToUse)
|
||||
{
|
||||
}
|
||||
|
||||
/** Generates a simple hash from an UnsignedInteger. */
|
||||
std::size_t operator() (UnsignedInteger const& key) const
|
||||
{
|
||||
std::size_t hash;
|
||||
Murmur::Hash (key.cbegin (), key.size, m_seed, &hash);
|
||||
return hash;
|
||||
}
|
||||
|
||||
private:
|
||||
std::size_t m_seed;
|
||||
};
|
||||
|
||||
/** Determins if two UnsignedInteger objects are equal. */
|
||||
class equal
|
||||
{
|
||||
public:
|
||||
bool operator() (UnsignedInteger const& lhs, UnsignedInteger const& rhs) const
|
||||
{
|
||||
return lhs.compare (rhs) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/** Construct the object.
|
||||
The values are uninitialized.
|
||||
*/
|
||||
UnsignedInteger ()
|
||||
{
|
||||
}
|
||||
|
||||
/** Construct a copy. */
|
||||
UnsignedInteger (UnsignedInteger const& other)
|
||||
{
|
||||
this->operator= (other);
|
||||
}
|
||||
|
||||
/** Construct from a raw memory.
|
||||
The area pointed to by buffer must be at least Bytes in size,
|
||||
or else undefined behavior will result.
|
||||
*/
|
||||
/** @{ */
|
||||
explicit UnsignedInteger (void const* buf)
|
||||
{
|
||||
m_values [0] = 0; // clear any pad bytes
|
||||
std::memcpy (begin(), buf, Bytes);
|
||||
}
|
||||
|
||||
template <typename InputIt>
|
||||
UnsignedInteger (InputIt first, InputIt last)
|
||||
{
|
||||
m_values [0] = 0; // clear any pad bytes
|
||||
check_precondition (std::distance (first, last) == size);
|
||||
std::copy (first, last, begin());
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/** Assign from another UnsignedInteger. */
|
||||
UnsignedInteger& operator= (UnsignedInteger const& other)
|
||||
{
|
||||
// Perform an aligned, all inclusive copy that includes padding.
|
||||
std::copy (other.m_values, other.m_values + CalcCount, m_values);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Create from an integer type.
|
||||
@invariant IntegerType must be an unsigned integer type.
|
||||
*/
|
||||
template <class UnsignedIntegralType>
|
||||
static UnsignedInteger createFromInteger (UnsignedIntegralType value)
|
||||
{
|
||||
static_bassert (Bytes >= sizeof (UnsignedIntegralType));
|
||||
UnsignedInteger <Bytes> result;
|
||||
value = toNetworkByteOrder <UnsignedIntegralType> (value);
|
||||
result.clear ();
|
||||
std::memcpy (result.end () - sizeof (value), &value, bmin (Bytes, sizeof (value)));
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Construct with a filled value. */
|
||||
static UnsignedInteger createFilled (value_type value)
|
||||
{
|
||||
UnsignedInteger result;
|
||||
result.fill (value);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Fill with a particular byte value. */
|
||||
void fill (value_type value)
|
||||
{
|
||||
IntCalcType c;
|
||||
memset (&c, value, sizeof (c));
|
||||
std::fill (m_values, m_values + CalcCount, c);
|
||||
}
|
||||
|
||||
/** Clear the contents to zero. */
|
||||
void clear ()
|
||||
{
|
||||
std::fill (m_values, m_values + CalcCount, 0);
|
||||
}
|
||||
|
||||
/** Convert to calculation format. */
|
||||
CalcType toCalcType (bool convert = true)
|
||||
{
|
||||
return CalcType::fromCanonical (m_values, Bytes, convert);
|
||||
}
|
||||
|
||||
/** Determine if all bits are zero. */
|
||||
bool isZero () const
|
||||
{
|
||||
for (int i = 0; i < CalcCount; ++i)
|
||||
{
|
||||
if (m_values [i] != 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Determine if any bit is non-zero. */
|
||||
bool isNotZero () const
|
||||
{
|
||||
return ! isZero ();
|
||||
}
|
||||
|
||||
/** Support conversion to `bool`.
|
||||
@return `true` if any bit is non-zero.
|
||||
@see SafeBool
|
||||
*/
|
||||
bool asBoolean () const
|
||||
{
|
||||
return isNotZero ();
|
||||
}
|
||||
|
||||
/** Get an iterator to the beginning. */
|
||||
iterator begin ()
|
||||
{
|
||||
return get();
|
||||
}
|
||||
|
||||
/** Get an iterator to past-the-end. */
|
||||
iterator end ()
|
||||
{
|
||||
return get()+Bytes;
|
||||
}
|
||||
|
||||
/** Get a const iterator to the beginning. */
|
||||
const_iterator begin () const
|
||||
{
|
||||
return get();
|
||||
}
|
||||
|
||||
/** Get a const iterator to past-the-end. */
|
||||
const_iterator end () const
|
||||
{
|
||||
return get()+Bytes;
|
||||
}
|
||||
|
||||
/** Get a const iterator to the beginning. */
|
||||
const_iterator cbegin () const
|
||||
{
|
||||
return get();
|
||||
}
|
||||
|
||||
/** Get a const iterator to past-the-end. */
|
||||
const_iterator cend () const
|
||||
{
|
||||
return get()+Bytes;
|
||||
}
|
||||
|
||||
/** Compare two objects of equal size.
|
||||
The comparison is performed using a numeric lexicographical comparison.
|
||||
*/
|
||||
int compare (UnsignedInteger const& other) const
|
||||
{
|
||||
return memcmp (cbegin (), other.cbegin (), Bytes);
|
||||
}
|
||||
|
||||
/** Determine equality. */
|
||||
bool operator== (UnsignedInteger const& other) const
|
||||
{
|
||||
return compare (other) == 0;
|
||||
}
|
||||
|
||||
/** Determine inequality. */
|
||||
bool operator!= (UnsignedInteger const& other) const
|
||||
{
|
||||
return compare (other) != 0;
|
||||
}
|
||||
|
||||
/** Ordered comparison. */
|
||||
bool operator< (UnsignedInteger const& other) const
|
||||
{
|
||||
return compare (other) < 0;
|
||||
}
|
||||
|
||||
/** Ordered comparison. */
|
||||
bool operator<= (UnsignedInteger const& other) const
|
||||
{
|
||||
return compare (other) <= 0;
|
||||
}
|
||||
|
||||
/** Ordered comparison. */
|
||||
bool operator> (UnsignedInteger const& other) const
|
||||
{
|
||||
return compare (other) > 0;
|
||||
}
|
||||
|
||||
/** Ordered comparison. */
|
||||
bool operator>= (UnsignedInteger const& other) const
|
||||
{
|
||||
return compare (other) >= 0;
|
||||
}
|
||||
|
||||
private:
|
||||
static std::size_t const CalcCount = (Bytes + sizeof (IntCalcType) - 1) / sizeof (IntCalcType);
|
||||
|
||||
value_type* get ()
|
||||
{
|
||||
return (reinterpret_cast <value_type*> (&m_values [0])) +
|
||||
((sizeof(IntCalcType)-(Bytes&(sizeof(IntCalcType)-1)))&(sizeof(IntCalcType)-1));
|
||||
}
|
||||
|
||||
value_type const* get () const
|
||||
{
|
||||
return (reinterpret_cast <value_type const*> (&m_values [0])) +
|
||||
((sizeof(IntCalcType)-(Bytes&(sizeof(IntCalcType)-1)))&(sizeof(IntCalcType)-1));
|
||||
}
|
||||
|
||||
IntCalcType m_values [CalcCount];
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,430 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_CRYPTO_UNSIGNEDINTEGERCALC_H_INCLUDED
|
||||
#define BEAST_CRYPTO_UNSIGNEDINTEGERCALC_H_INCLUDED
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <typename UInt>
|
||||
struct DoubleWidthUInt;
|
||||
|
||||
template <>
|
||||
struct DoubleWidthUInt <uint16>
|
||||
{
|
||||
typedef uint32 type;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DoubleWidthUInt <uint32>
|
||||
{
|
||||
typedef uint64 type;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/** Multiprecision unsigned integer suitable for calculations.
|
||||
|
||||
The data is stored in "calculation" format, which means it can be
|
||||
readily used for performing calculations, but no raw access to the
|
||||
bytes are provided. To transmit a serialized unsigned integer or
|
||||
perform base encodings, it must be converted back into UnsignedInteger.
|
||||
The number is represented as a series of native UInt unsigned integer
|
||||
types, in order of increasing significance.
|
||||
|
||||
This is a lightweight object, storage and ownership of the underlying
|
||||
data buffer is an external responsibility. The makes the class cheap to
|
||||
copy and pass by value.
|
||||
|
||||
A consequence of this ownership model is that arithmetics operators
|
||||
which return results by value cannot be included in the interface.
|
||||
*/
|
||||
template <typename UInt>
|
||||
class UnsignedIntegerCalc : public SafeBool <UnsignedIntegerCalc <UInt> >
|
||||
{
|
||||
public:
|
||||
typedef typename detail::DoubleWidthUInt <UInt>::type UIntBig;
|
||||
|
||||
typedef std::size_t size_type;
|
||||
|
||||
static UInt const maxUInt = ((UInt)(-1));
|
||||
static size_type const numBits = (sizeof(UInt)*8);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/** Construct an empty integer / zero bits. */
|
||||
UnsignedIntegerCalc ()
|
||||
: m_size (0)
|
||||
, m_values (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
/** Construct a reference to an existing buffer. */
|
||||
UnsignedIntegerCalc (UnsignedIntegerCalc const& other)
|
||||
: m_size (other.m_size)
|
||||
, m_values (other.m_values)
|
||||
{
|
||||
}
|
||||
|
||||
/** Construct from an existing array of values.
|
||||
The existing data must already be in the "calculation" format.
|
||||
*/
|
||||
UnsignedIntegerCalc (size_type count, UInt* values)
|
||||
: m_size (count)
|
||||
, m_values (values)
|
||||
{
|
||||
}
|
||||
|
||||
/** Convert to calculation format from canonical format.
|
||||
This overwrites the callers memory without transferring ownership.
|
||||
Canonical format is defined as a big endian byte oriented
|
||||
multiprecision integer format. The buffer should point to the
|
||||
beginning of the storage area and not the beginning of the canonical
|
||||
data. Bytes is the desired canonical bytes.
|
||||
*/
|
||||
static UnsignedIntegerCalc fromCanonical (
|
||||
void* buffer, size_type const bytes, bool swizzle = true)
|
||||
{
|
||||
UInt* const values (reinterpret_cast <UInt*> (buffer));
|
||||
size_type const count ((bytes + sizeof (UInt) - 1) / sizeof (UInt));
|
||||
if (swizzle)
|
||||
{
|
||||
// Zero fill the possibly uninitialized pad bytes
|
||||
memset (buffer, 0,
|
||||
((sizeof(UInt)-(bytes&(sizeof(UInt)-1)))&(sizeof(UInt)-1)));
|
||||
// Swap and swizzle
|
||||
UInt* lo (values);
|
||||
UInt* hi (values + count - 1);
|
||||
while (lo < hi)
|
||||
{
|
||||
std::swap (*lo, *hi);
|
||||
*lo = fromNetworkByteOrder <UInt> (*lo);
|
||||
++lo;
|
||||
*hi = fromNetworkByteOrder <UInt> (*hi);
|
||||
++hi;
|
||||
}
|
||||
if (lo == hi)
|
||||
*lo = fromNetworkByteOrder <UInt> (*lo);
|
||||
}
|
||||
return UnsignedIntegerCalc (count, values);
|
||||
}
|
||||
|
||||
/** Convert the buffer back into canonical format.
|
||||
Since ownership was never transferred, the caller's data is
|
||||
restored to its original format. Typically this will be done
|
||||
as the last step of a series of operations.
|
||||
*/
|
||||
void toCanonical ()
|
||||
{
|
||||
// Swap and swizzle
|
||||
UInt* lo (m_values);
|
||||
UInt* hi (m_values + m_size - 1);
|
||||
while (lo < hi)
|
||||
{
|
||||
std::swap (*lo, *hi);
|
||||
*lo = toNetworkByteOrder <UInt> (*lo); ++lo;
|
||||
*hi = toNetworkByteOrder <UInt> (*hi); --hi;
|
||||
}
|
||||
if (lo == hi)
|
||||
*lo = toNetworkByteOrder <UInt> (*lo);
|
||||
}
|
||||
|
||||
/** Assign a value from another integer.
|
||||
@note This does not transfer the reference to the buffer, it
|
||||
copies the values from one buffer to the other.
|
||||
*/
|
||||
UnsignedIntegerCalc& operator= (UnsignedIntegerCalc const& other)
|
||||
{
|
||||
bassert (other.size() <= size());
|
||||
size_type n (size());
|
||||
UInt* dest (m_values + size());
|
||||
for (; n-- > other.size();)
|
||||
*--dest = 0;
|
||||
UInt const* rhs (other.m_values + n);
|
||||
for (; n--;)
|
||||
*--dest = *--rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Returns `true` if this represents the number zero. */
|
||||
bool isZero () const
|
||||
{
|
||||
for (size_type n (size()); n--;)
|
||||
if (m_values [n] != 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Returns `true` if this represents any number other than zero. */
|
||||
bool isNotZero () const
|
||||
{
|
||||
return ! isZero ();
|
||||
}
|
||||
|
||||
/** Safe conversion to `bool`, `true` means a non-zero value. */
|
||||
bool asBoolean () const
|
||||
{
|
||||
return isNotZero ();
|
||||
}
|
||||
|
||||
/** Returns `true` if the buffer has 0 values. */
|
||||
bool empty () const
|
||||
{
|
||||
return m_size == 0;
|
||||
}
|
||||
|
||||
/** Returns the size of the buffer, in values. */
|
||||
size_type size () const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
/** Safe array indexing to arbitrary positions.
|
||||
If the index is out of range, zero is returned.
|
||||
*/
|
||||
UInt operator[] (size_type n) const
|
||||
{
|
||||
if (n >= 0 && n < size())
|
||||
return m_values [n];
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Universal comparison.
|
||||
The comparison is performed numerically.
|
||||
The return values have the same meaning as memcmp().
|
||||
*/
|
||||
int compare (UnsignedIntegerCalc const& other) const
|
||||
{
|
||||
if (size() == 0)
|
||||
{
|
||||
if (other.size() == 0)
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
else if (other.size() == 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (size_type n (std::max (size(), other.size())); n--;)
|
||||
{
|
||||
UInt lhs ((*this)[n]);
|
||||
UInt rhs (other[n]);
|
||||
if (lhs < rhs)
|
||||
return -1;
|
||||
else if (lhs > rhs)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Determine equality. */
|
||||
bool operator== (UnsignedIntegerCalc const& other) const
|
||||
{
|
||||
return compare (other) == 0;
|
||||
}
|
||||
|
||||
/** Determine inequality. */
|
||||
bool operator!= (UnsignedIntegerCalc const& other) const
|
||||
{
|
||||
return compare (other) != 0;
|
||||
}
|
||||
|
||||
/** Ordered comparison. */
|
||||
bool operator< (UnsignedIntegerCalc const& other) const
|
||||
{
|
||||
return compare (other) < 0;
|
||||
}
|
||||
|
||||
/** Ordered comparison. */
|
||||
bool operator<= (UnsignedIntegerCalc const& other) const
|
||||
{
|
||||
return compare (other) <= 0;
|
||||
}
|
||||
|
||||
/** Ordered comparison. */
|
||||
bool operator> (UnsignedIntegerCalc const& other) const
|
||||
{
|
||||
return compare (other) > 0;
|
||||
}
|
||||
|
||||
/** Ordered comparison. */
|
||||
bool operator>= (UnsignedIntegerCalc const& other) const
|
||||
{
|
||||
return compare (other) >= 0;
|
||||
}
|
||||
|
||||
/** Assign zero. */
|
||||
void clear ()
|
||||
{
|
||||
UInt* dest (m_values - 1);
|
||||
for (size_type n (size()); n--;)
|
||||
*++dest = 0;
|
||||
}
|
||||
|
||||
/** Perform bitwise logical-not. */
|
||||
/*
|
||||
UnsignedIntegerCalc& not ()
|
||||
{
|
||||
unaryAssign (Not());
|
||||
return *this;
|
||||
}
|
||||
*/
|
||||
|
||||
/** Perform bitwise logical-or. */
|
||||
UnsignedIntegerCalc& operator|= (UnsignedIntegerCalc const& rhs)
|
||||
{
|
||||
binaryAssign (rhs, Or());
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Perform bitwise logical-and. */
|
||||
UnsignedIntegerCalc& operator&= (UnsignedIntegerCalc const& rhs)
|
||||
{
|
||||
binaryAssign (rhs, And());
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Perform bitwise logical-xor. */
|
||||
UnsignedIntegerCalc& operator^= (UnsignedIntegerCalc const& rhs)
|
||||
{
|
||||
binaryAssign (rhs, Xor());
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Perform addition. */
|
||||
UnsignedIntegerCalc& operator+= (UnsignedIntegerCalc const& v)
|
||||
{
|
||||
UIntBig carry (0);
|
||||
UInt* lhs (m_values);
|
||||
UInt const* rhs (v.m_values - 1);
|
||||
for (size_type n (0); n<size() || n<v.size(); ++n)
|
||||
{
|
||||
UIntBig part (carry);
|
||||
carry = 0;
|
||||
if (n < size())
|
||||
part += *lhs;
|
||||
if (n < v.size())
|
||||
part += *++rhs;
|
||||
if (part > maxUInt)
|
||||
{
|
||||
part &= maxUInt;
|
||||
carry = 1;
|
||||
}
|
||||
*lhs++ = UInt (part);
|
||||
}
|
||||
bassert (carry == 0) // overflow
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Perform small addition. */
|
||||
UnsignedIntegerCalc& operator+= (UInt rhs)
|
||||
{
|
||||
UnsignedIntegerCalc const v (1, &rhs);
|
||||
return operator+= (v);
|
||||
}
|
||||
|
||||
/** Perform small multiply. */
|
||||
UnsignedIntegerCalc& operator*= (UInt rhs)
|
||||
{
|
||||
UIntBig carry (0);
|
||||
UInt* lhs (m_values - 1);
|
||||
for (size_type n (size()); n--;)
|
||||
{
|
||||
UIntBig part (carry);
|
||||
carry = 0;
|
||||
part += (*++lhs) * UIntBig(rhs);
|
||||
carry = part >> numBits;
|
||||
*lhs = UInt (part & maxUInt);
|
||||
}
|
||||
bassert (carry == 0); // overflow
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Small division. */
|
||||
UnsignedIntegerCalc operator/= (UInt rhs)
|
||||
{
|
||||
UIntBig dividend (0);
|
||||
UInt* lhs (m_values+size());
|
||||
for (size_type n (size()); n--;)
|
||||
{
|
||||
dividend |= *--lhs;
|
||||
*lhs = UInt (dividend / rhs);
|
||||
dividend = (dividend % rhs) << numBits;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Small modulus. */
|
||||
UInt operator% (UInt rhs) const
|
||||
{
|
||||
UIntBig modsq = 1;
|
||||
UIntBig result = 0;
|
||||
UInt const* lhs (m_values);
|
||||
for (size_type n (size()); n--; ++lhs)
|
||||
{
|
||||
for (int bit (0); bit < numBits; ++bit)
|
||||
{
|
||||
if (((*lhs) & (1 << bit)) != 0)
|
||||
{
|
||||
result += modsq;
|
||||
if (result >= rhs)
|
||||
result -= rhs;
|
||||
}
|
||||
modsq <<= 1;
|
||||
if (modsq >= rhs)
|
||||
modsq -= rhs;
|
||||
}
|
||||
}
|
||||
return UInt (result);
|
||||
}
|
||||
|
||||
private:
|
||||
struct Not { void operator() (UInt& rv) const { rv = ~rv; } };
|
||||
struct Or { void operator() (UInt& lhs, UInt rhs) const { lhs |= rhs; } };
|
||||
struct And { void operator() (UInt& lhs, UInt rhs) const { lhs &= rhs; } };
|
||||
struct Xor { void operator() (UInt& lhs, UInt rhs) const { lhs ^= rhs; } };
|
||||
|
||||
template <class Operator>
|
||||
void unaryAssign (Operator op = Operator())
|
||||
{
|
||||
UInt* dest (m_values-1);
|
||||
for (size_type n (size()); n--;)
|
||||
op (*++dest);
|
||||
}
|
||||
|
||||
template <class Operator>
|
||||
void binaryAssign (UnsignedIntegerCalc const& other, Operator op = Operator ())
|
||||
{
|
||||
UInt* dest (m_values + size());
|
||||
size_type n (size());
|
||||
for (; n-- > other.size();)
|
||||
*--dest = 0;
|
||||
UInt const* rhs (other.m_values + n);
|
||||
for (UInt const* rhs (other.m_values + n); n--;)
|
||||
op (*--dest, *--rhs);
|
||||
}
|
||||
|
||||
size_type m_size;
|
||||
UInt* m_values;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,38 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "BeastConfig.h"
|
||||
|
||||
#include "beast_db.h"
|
||||
|
||||
#include "../beast_crypto/beast_crypto.h"
|
||||
|
||||
namespace beast
|
||||
{
|
||||
|
||||
#if BEAST_GCC
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
||||
#endif
|
||||
#include "keyvalue/KeyvaDB.cpp"
|
||||
#if BEAST_GCC
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_BEAST_DB_H_INCLUDED
|
||||
#define BEAST_BEAST_DB_H_INCLUDED
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/* If you fail to make sure that all your compile units are building Beast with
|
||||
the same set of option flags, then there's a risk that different compile
|
||||
units will treat the classes as having different memory layouts, leading to
|
||||
very nasty memory corruption errors when they all get linked together.
|
||||
That's why it's best to always include the BeastConfig.h file before any
|
||||
beast headers.
|
||||
*/
|
||||
#ifndef BEAST_BEASTCONFIG_H_INCLUDED
|
||||
# ifdef _MSC_VER
|
||||
# pragma message ("Have you included your BeastConfig.h file before including the Beast headers?")
|
||||
# else
|
||||
# warning "Have you included your BeastConfig.h file before including the Beast headers?"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include "../beast_core/beast_core.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace beast
|
||||
{
|
||||
|
||||
#include "keyvalue/KeyvaDB.h"
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,861 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
/*
|
||||
|
||||
TODO
|
||||
|
||||
- Check consistency / range checking on read
|
||||
|
||||
- Cache top level tree nodes
|
||||
|
||||
- Coalesce I/O in RandomAccessFile
|
||||
|
||||
- Delete / file compaction
|
||||
|
||||
*/
|
||||
|
||||
class KeyvaDBImp : public KeyvaDB
|
||||
{
|
||||
private:
|
||||
// These are stored in big endian format in the file.
|
||||
|
||||
// A file offset.
|
||||
typedef int64 FileOffset;
|
||||
|
||||
// Index of a key.
|
||||
//
|
||||
// The value is broken up into two parts. The key block index,
|
||||
// and a 1 based index within the keyblock corresponding to the
|
||||
// internal key number.
|
||||
//
|
||||
typedef int32 KeyIndex;
|
||||
typedef int32 KeyBlockIndex;
|
||||
|
||||
// Size of a value.
|
||||
typedef uint32 ByteSize;
|
||||
|
||||
private:
|
||||
// returns the number of keys in a key block with the specified depth
|
||||
static int calcKeysAtDepth (int depth)
|
||||
{
|
||||
return (1U << depth) - 1;
|
||||
}
|
||||
|
||||
// returns the number of bytes in a key record
|
||||
static int calcKeyRecordBytes (int keyBytes)
|
||||
{
|
||||
// This depends on the format of a serialized key record
|
||||
return
|
||||
sizeof (FileOffset) +
|
||||
sizeof (ByteSize) +
|
||||
sizeof (KeyIndex) +
|
||||
sizeof (KeyIndex) +
|
||||
keyBytes
|
||||
;
|
||||
}
|
||||
|
||||
// returns the number of bytes in a key block
|
||||
static int calcKeyBlockBytes (int depth, int keyBytes)
|
||||
{
|
||||
return calcKeysAtDepth (depth) * calcKeyRecordBytes (keyBytes);
|
||||
}
|
||||
|
||||
public:
|
||||
enum
|
||||
{
|
||||
currentVersion = 1
|
||||
};
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
struct KeyAddress
|
||||
{
|
||||
// 1 based key block number
|
||||
uint32 blockNumber;
|
||||
|
||||
// 1 based key index within the block, breadth-first left to right
|
||||
uint32 keyNumber;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
// The size of the fixed area at the beginning of the key file.
|
||||
// This is used to store some housekeeping information like the
|
||||
// key size and version number.
|
||||
//
|
||||
masterHeaderBytes = 1000
|
||||
};
|
||||
|
||||
// The master record is at the beginning of the key file
|
||||
struct MasterRecord
|
||||
{
|
||||
// version number, starting from 1
|
||||
int32 version;
|
||||
|
||||
KeyBlockIndex nextKeyBlockIndex;
|
||||
|
||||
void write (OutputStream& stream)
|
||||
{
|
||||
stream.writeTypeBigEndian (version);
|
||||
}
|
||||
|
||||
void read (InputStream& stream)
|
||||
{
|
||||
stream.readTypeBigEndianInto (&version);
|
||||
}
|
||||
};
|
||||
|
||||
// Key records are indexed starting at one.
|
||||
struct KeyRecord : public Uncopyable
|
||||
{
|
||||
explicit KeyRecord (void* const keyStorage)
|
||||
: key (keyStorage)
|
||||
{
|
||||
}
|
||||
|
||||
// Absolute byte FileOffset in the value file.
|
||||
FileOffset valFileOffset;
|
||||
|
||||
// Size of the corresponding value, in bytes.
|
||||
ByteSize valSize;
|
||||
|
||||
// Key record index of left node, or 0.
|
||||
KeyIndex leftIndex;
|
||||
|
||||
// Key record index of right node, or 0.
|
||||
KeyIndex rightIndex;
|
||||
|
||||
// Points to keyBytes storage of the key.
|
||||
void* const key;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// A complete keyblock. The contents of the memory for the key block
|
||||
// are identical to the format on disk. Therefore it is necessary to
|
||||
// use the serialization routines to extract or update the key records.
|
||||
//
|
||||
class KeyBlock : public Uncopyable
|
||||
{
|
||||
public:
|
||||
KeyBlock (int depth, int keyBytes)
|
||||
: m_depth (depth)
|
||||
, m_keyBytes (keyBytes)
|
||||
, m_storage (calcKeyBlockBytes (depth, keyBytes))
|
||||
{
|
||||
}
|
||||
|
||||
void read (InputStream& stream)
|
||||
{
|
||||
stream.read (m_storage.getData (), calcKeyBlockBytes (m_depth, m_keyBytes));
|
||||
}
|
||||
|
||||
void write (OutputStream& stream)
|
||||
{
|
||||
stream.write (m_storage.getData (), calcKeyBlockBytes (m_depth, m_keyBytes));
|
||||
}
|
||||
|
||||
void readKeyRecord (KeyRecord* keyRecord, int keyIndex)
|
||||
{
|
||||
bassert (keyIndex >=1 && keyIndex <= calcKeysAtDepth (m_depth));
|
||||
|
||||
size_t const byteOffset = (keyIndex - 1) * calcKeyRecordBytes (m_keyBytes);
|
||||
|
||||
MemoryInputStream stream (
|
||||
addBytesToPointer (m_storage.getData (), byteOffset),
|
||||
calcKeyRecordBytes (m_keyBytes),
|
||||
false);
|
||||
|
||||
stream.readTypeBigEndianInto (&keyRecord->valFileOffset);
|
||||
stream.readTypeBigEndianInto (&keyRecord->valSize);
|
||||
stream.readTypeBigEndianInto (&keyRecord->leftIndex);
|
||||
stream.readTypeBigEndianInto (&keyRecord->rightIndex);
|
||||
stream.read (keyRecord->key, m_keyBytes);
|
||||
}
|
||||
|
||||
#if 0
|
||||
void writeKeyRecord (KeyRecord const& keyRecord, int keyIndex)
|
||||
{
|
||||
bassert (keyIndex >=1 && keyIndex <= calcKeysAtDepth (m_depth));
|
||||
|
||||
#if 0
|
||||
size_t const byteOffset = (keyIndex - 1) * calcKeyRecordBytes (m_keyBytes);
|
||||
|
||||
MemoryOutputStream stream (
|
||||
addBytesToPointer (m_storage.getData (), byteOffset),
|
||||
calcKeyRecordBytes (m_keyBytes));
|
||||
|
||||
stream.writeTypeBigEndian (keyRecord.valFileOffset);
|
||||
stream.writeTypeBigEndian (keyRecord.valSize);
|
||||
stream.writeTypeBigEndian (keyRecord.leftIndex);
|
||||
stream.writeTypeBigEndian (keyRecord.rightIndex);
|
||||
stream.write (keyRecord.key, m_keyBytes);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
int const m_depth;
|
||||
int const m_keyBytes;
|
||||
MemoryBlock m_storage;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// Concurrent data
|
||||
//
|
||||
struct State
|
||||
{
|
||||
RandomAccessFile keyFile;
|
||||
RandomAccessFile valFile;
|
||||
MasterRecord masterRecord;
|
||||
KeyIndex newKeyIndex;
|
||||
FileOffset valFileSize;
|
||||
|
||||
bool hasKeys () const noexcept
|
||||
{
|
||||
return newKeyIndex > 1;
|
||||
}
|
||||
};
|
||||
|
||||
typedef SharedData <State> SharedState;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
int const m_keyBytes;
|
||||
int const m_keyBlockDepth;
|
||||
SharedState m_state;
|
||||
HeapBlock <char> m_keyStorage;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
KeyvaDBImp (int keyBytes,
|
||||
int keyBlockDepth,
|
||||
File keyPath,
|
||||
File valPath)
|
||||
: m_keyBytes (keyBytes)
|
||||
, m_keyBlockDepth (keyBlockDepth)
|
||||
, m_keyStorage (keyBytes)
|
||||
{
|
||||
SharedState::Access state (m_state);
|
||||
|
||||
openFile (&state->keyFile, keyPath);
|
||||
|
||||
int64 const fileSize = state->keyFile.getFile ().getSize ();
|
||||
|
||||
if (fileSize == 0)
|
||||
{
|
||||
// VFALCO TODO Better error handling here
|
||||
// initialize the key file
|
||||
Result result = state->keyFile.setPosition (masterHeaderBytes - 1);
|
||||
if (result.wasOk ())
|
||||
{
|
||||
char byte = 0;
|
||||
|
||||
result = state->keyFile.write (&byte, 1);
|
||||
|
||||
if (result.wasOk ())
|
||||
{
|
||||
state->keyFile.flush ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state->newKeyIndex = 1 + static_cast <KeyIndex> ((state->keyFile.getFile ().getSize () - masterHeaderBytes)
|
||||
/ calcKeyRecordBytes (m_keyBytes));
|
||||
|
||||
openFile (&state->valFile, valPath);
|
||||
|
||||
state->valFileSize = state->valFile.getFile ().getSize ();
|
||||
}
|
||||
|
||||
~KeyvaDBImp ()
|
||||
{
|
||||
SharedState::Access state (m_state);
|
||||
|
||||
flushInternal (state);
|
||||
}
|
||||
|
||||
// Open a file for reading and writing.
|
||||
// Creates the file if it doesn't exist.
|
||||
static void openFile (RandomAccessFile* file, File path)
|
||||
{
|
||||
Result const result = file->open (path, RandomAccessFile::readWrite);
|
||||
|
||||
if (! result)
|
||||
{
|
||||
String s;
|
||||
s << "KeyvaDB: Couldn't open " << path.getFileName () << " for writing.";
|
||||
Throw (std::runtime_error (s.toStdString ()));
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
Result createMasterRecord (SharedState::Access& state)
|
||||
{
|
||||
MemoryBlock buffer (masterHeaderBytes, true);
|
||||
|
||||
Result result = state->keyFile.setPosition (0);
|
||||
|
||||
if (result.wasOk ())
|
||||
{
|
||||
//MasterRecord mr;
|
||||
|
||||
//mr.version = 1;
|
||||
|
||||
result = state->keyFile.write (buffer.getData (), buffer.getSize ());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
FileOffset calcKeyRecordOffset (KeyIndex keyIndex)
|
||||
{
|
||||
bassert (keyIndex > 0);
|
||||
|
||||
FileOffset const byteOffset = masterHeaderBytes + (keyIndex - 1) * calcKeyRecordBytes (m_keyBytes);
|
||||
|
||||
return byteOffset;
|
||||
}
|
||||
|
||||
// Read a key record into memory.
|
||||
// VFALCO TODO Return a Result and do validity checking on all inputs
|
||||
//
|
||||
void readKeyRecord (KeyRecord* const keyRecord,
|
||||
KeyIndex const keyIndex,
|
||||
SharedState::Access& state)
|
||||
{
|
||||
FileOffset const byteOffset = calcKeyRecordOffset (keyIndex);
|
||||
|
||||
Result result = state->keyFile.setPosition (byteOffset);
|
||||
|
||||
if (result.wasOk ())
|
||||
{
|
||||
MemoryBlock data (calcKeyRecordBytes (m_keyBytes));
|
||||
|
||||
size_t bytesRead;
|
||||
|
||||
result = state->keyFile.read (data.getData (), calcKeyRecordBytes (m_keyBytes), &bytesRead);
|
||||
|
||||
if (result.wasOk ())
|
||||
{
|
||||
if (bytesRead == static_cast <size_t> (calcKeyRecordBytes (m_keyBytes)))
|
||||
{
|
||||
MemoryInputStream stream (data, false);
|
||||
|
||||
// This defines the file format!
|
||||
stream.readTypeBigEndianInto (&keyRecord->valFileOffset);
|
||||
stream.readTypeBigEndianInto (&keyRecord->valSize);
|
||||
stream.readTypeBigEndianInto (&keyRecord->leftIndex);
|
||||
stream.readTypeBigEndianInto (&keyRecord->rightIndex);
|
||||
|
||||
// Grab the key
|
||||
stream.read (keyRecord->key, m_keyBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = Result::fail ("KeyvaDB: amountRead != calcKeyRecordBytes()");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! result.wasOk ())
|
||||
{
|
||||
String s;
|
||||
s << "KeyvaDB readKeyRecord failed in " << state->keyFile.getFile ().getFileName ();
|
||||
Throw (std::runtime_error (s.toStdString ()));
|
||||
}
|
||||
}
|
||||
|
||||
// Write a key record from memory
|
||||
void writeKeyRecord (KeyRecord const& keyRecord,
|
||||
KeyIndex const keyIndex,
|
||||
SharedState::Access& state,
|
||||
bool includingKey)
|
||||
{
|
||||
FileOffset const byteOffset = calcKeyRecordOffset (keyIndex);
|
||||
|
||||
int const bytes = calcKeyRecordBytes (m_keyBytes) - (includingKey ? 0 : m_keyBytes);
|
||||
|
||||
// VFALCO TODO Recycle this buffer
|
||||
MemoryBlock data (bytes);
|
||||
|
||||
{
|
||||
MemoryOutputStream stream (data, false);
|
||||
|
||||
// This defines the file format!
|
||||
stream.writeTypeBigEndian (keyRecord.valFileOffset);
|
||||
stream.writeTypeBigEndian (keyRecord.valSize);
|
||||
stream.writeTypeBigEndian (keyRecord.leftIndex);
|
||||
stream.writeTypeBigEndian (keyRecord.rightIndex);
|
||||
|
||||
// Write the key
|
||||
if (includingKey)
|
||||
stream.write (keyRecord.key, m_keyBytes);
|
||||
}
|
||||
|
||||
Result result = state->keyFile.setPosition (byteOffset);
|
||||
|
||||
if (result.wasOk ())
|
||||
{
|
||||
size_t bytesWritten;
|
||||
|
||||
result = state->keyFile.write (data.getData (), bytes, &bytesWritten);
|
||||
|
||||
if (result.wasOk ())
|
||||
{
|
||||
if (bytesWritten != static_cast <size_t> (bytes))
|
||||
{
|
||||
result = Result::fail ("KeyvaDB: bytesWritten != bytes");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!result.wasOk ())
|
||||
{
|
||||
String s;
|
||||
s << "KeyvaDB: writeKeyRecord failed in " << state->keyFile.getFile ().getFileName ();
|
||||
Throw (std::runtime_error (s.toStdString ()));
|
||||
}
|
||||
}
|
||||
|
||||
// Append a value to the value file.
|
||||
// VFALCO TODO return a Result
|
||||
void writeValue (void const* const value, ByteSize valueBytes, SharedState::Access& state)
|
||||
{
|
||||
Result result = state->valFile.setPosition (state->valFileSize);
|
||||
|
||||
if (result.wasOk ())
|
||||
{
|
||||
size_t bytesWritten;
|
||||
|
||||
result = state->valFile.write (value, valueBytes, &bytesWritten);
|
||||
|
||||
if (result.wasOk ())
|
||||
{
|
||||
if (bytesWritten == valueBytes)
|
||||
{
|
||||
state->valFileSize += valueBytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = Result::fail ("KeyvaDB: bytesWritten != valueBytes");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! result.wasOk ())
|
||||
{
|
||||
String s;
|
||||
s << "KeyvaDB: writeValue failed in " << state->valFile.getFile ().getFileName ();
|
||||
Throw (std::runtime_error (s.toStdString ()));
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
struct FindResult : public Uncopyable
|
||||
{
|
||||
FindResult (void* const keyStorage)
|
||||
: keyRecord (keyStorage)
|
||||
{
|
||||
}
|
||||
|
||||
int compare; // result of the last comparison
|
||||
KeyIndex keyIndex; // index we looked at last
|
||||
//KeyBlock keyBlock; // KeyBlock we looked at last
|
||||
KeyRecord keyRecord; // KeyRecord we looked at last
|
||||
};
|
||||
|
||||
// Find a key. If the key doesn't exist, enough information
|
||||
// is left behind in the result to perform an insertion.
|
||||
//
|
||||
// Returns true if the key was found.
|
||||
//
|
||||
bool find (FindResult* findResult, void const* key, SharedState::Access& state)
|
||||
{
|
||||
// Not okay to call this with an empty key file!
|
||||
bassert (state->hasKeys ());
|
||||
|
||||
// This performs a standard binary search
|
||||
|
||||
findResult->keyIndex = 1;
|
||||
|
||||
do
|
||||
{
|
||||
readKeyRecord (&findResult->keyRecord, findResult->keyIndex, state);
|
||||
|
||||
findResult->compare = memcmp (key, findResult->keyRecord.key, m_keyBytes);
|
||||
|
||||
if (findResult->compare < 0)
|
||||
{
|
||||
if (findResult->keyRecord.leftIndex != 0)
|
||||
{
|
||||
// Go left
|
||||
findResult->keyIndex = findResult->keyRecord.leftIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Insert position is to the left
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (findResult->compare > 0)
|
||||
{
|
||||
if (findResult->keyRecord.rightIndex != 0)
|
||||
{
|
||||
// Go right
|
||||
findResult->keyIndex = findResult->keyRecord.rightIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Insert position is to the right
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (findResult->compare != 0);
|
||||
|
||||
return findResult->compare == 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
bool get (void const* key, GetCallback* callback)
|
||||
{
|
||||
FindResult findResult (m_keyStorage.getData ());
|
||||
|
||||
SharedState::Access state (m_state);
|
||||
|
||||
bool found = false;
|
||||
|
||||
if (state->hasKeys ())
|
||||
{
|
||||
found = find (&findResult, key, state);
|
||||
|
||||
if (found)
|
||||
{
|
||||
void* const destStorage = callback->getStorageForValue (findResult.keyRecord.valSize);
|
||||
|
||||
Result result = state->valFile.setPosition (findResult.keyRecord.valFileOffset);
|
||||
|
||||
if (result.wasOk ())
|
||||
{
|
||||
size_t bytesRead;
|
||||
|
||||
result = state->valFile.read (destStorage, findResult.keyRecord.valSize, &bytesRead);
|
||||
|
||||
if (result.wasOk ())
|
||||
{
|
||||
if (bytesRead != findResult.keyRecord.valSize)
|
||||
{
|
||||
result = Result::fail ("KeyvaDB: bytesRead != valSize");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! result.wasOk ())
|
||||
{
|
||||
String s;
|
||||
s << "KeyvaDB: get in " << state->valFile.getFile ().getFileName ();
|
||||
Throw (std::runtime_error (s.toStdString ()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// Write a key value pair. Does nothing if the key exists.
|
||||
void put (void const* key, void const* value, int valueBytes)
|
||||
{
|
||||
bassert (valueBytes > 0);
|
||||
|
||||
SharedState::Access state (m_state);
|
||||
|
||||
if (state->hasKeys ())
|
||||
{
|
||||
// Search for the key
|
||||
|
||||
FindResult findResult (m_keyStorage.getData ());
|
||||
|
||||
bool const found = find (&findResult, key, state);
|
||||
|
||||
if (! found )
|
||||
{
|
||||
bassert (findResult.compare != 0);
|
||||
|
||||
// Binary tree insertion.
|
||||
// Link the last key record to the new key
|
||||
{
|
||||
if (findResult.compare < 0)
|
||||
{
|
||||
findResult.keyRecord.leftIndex = state->newKeyIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
findResult.keyRecord.rightIndex = state->newKeyIndex;
|
||||
}
|
||||
|
||||
writeKeyRecord (findResult.keyRecord, findResult.keyIndex, state, false);
|
||||
}
|
||||
|
||||
// Write the new key
|
||||
{
|
||||
findResult.keyRecord.valFileOffset = state->valFileSize;
|
||||
findResult.keyRecord.valSize = valueBytes;
|
||||
findResult.keyRecord.leftIndex = 0;
|
||||
findResult.keyRecord.rightIndex = 0;
|
||||
|
||||
memcpy (findResult.keyRecord.key, key, m_keyBytes);
|
||||
|
||||
writeKeyRecord (findResult.keyRecord, state->newKeyIndex, state, true);
|
||||
}
|
||||
|
||||
// Key file has grown by one.
|
||||
++state->newKeyIndex;
|
||||
|
||||
// Write the value
|
||||
writeValue (value, valueBytes, state);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Key already exists, do nothing.
|
||||
// We could check to make sure the payloads are the same.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// Write first key
|
||||
//
|
||||
|
||||
KeyRecord keyRecord (m_keyStorage.getData ());
|
||||
|
||||
keyRecord.valFileOffset = state->valFileSize;
|
||||
keyRecord.valSize = valueBytes;
|
||||
keyRecord.leftIndex = 0;
|
||||
keyRecord.rightIndex = 0;
|
||||
|
||||
memcpy (keyRecord.key, key, m_keyBytes);
|
||||
|
||||
writeKeyRecord (keyRecord, state->newKeyIndex, state, true);
|
||||
|
||||
// Key file has grown by one.
|
||||
++state->newKeyIndex;
|
||||
|
||||
//
|
||||
// Write value
|
||||
//
|
||||
|
||||
bassert (state->valFileSize == 0);
|
||||
|
||||
writeValue (value, valueBytes, state);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void flush ()
|
||||
{
|
||||
SharedState::Access state (m_state);
|
||||
|
||||
flushInternal (state);
|
||||
}
|
||||
|
||||
void flushInternal (SharedState::Access& state)
|
||||
{
|
||||
state->keyFile.flush ();
|
||||
state->valFile.flush ();
|
||||
}
|
||||
};
|
||||
|
||||
KeyvaDB* KeyvaDB::New (int keyBytes, int keyBlockDepth, File keyPath, File valPath)
|
||||
{
|
||||
return new KeyvaDBImp (keyBytes, keyBlockDepth, keyPath, valPath);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class KeyvaDBTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
maxPayloadBytes = 8 * 1024
|
||||
};
|
||||
|
||||
// Retrieval callback stores the value in a Payload object for comparison
|
||||
struct PayloadGetCallback : KeyvaDB::GetCallback
|
||||
{
|
||||
UnitTestUtilities::Payload payload;
|
||||
|
||||
PayloadGetCallback () : payload (maxPayloadBytes)
|
||||
{
|
||||
}
|
||||
|
||||
void* getStorageForValue (int valueBytes)
|
||||
{
|
||||
bassert (valueBytes <= maxPayloadBytes);
|
||||
|
||||
payload.bytes = valueBytes;
|
||||
|
||||
return payload.data.getData ();
|
||||
}
|
||||
};
|
||||
|
||||
KeyvaDB* createDB (unsigned int keyBytes, File const& path)
|
||||
{
|
||||
File const keyPath = path.withFileExtension (".key");
|
||||
File const valPath = path.withFileExtension (".val");
|
||||
|
||||
return KeyvaDB::New (keyBytes, 1, keyPath, valPath);
|
||||
}
|
||||
|
||||
void deleteDBFiles (File const& path)
|
||||
{
|
||||
File const keyPath = path.withFileExtension (".key");
|
||||
File const valPath = path.withFileExtension (".val");
|
||||
|
||||
keyPath.deleteFile ();
|
||||
valPath.deleteFile ();
|
||||
}
|
||||
|
||||
template <size_t KeyBytes>
|
||||
void testKeySize (unsigned int const maxItems)
|
||||
{
|
||||
using namespace UnitTestUtilities;
|
||||
|
||||
typedef UnsignedInteger <KeyBytes> KeyType;
|
||||
|
||||
int64 const seedValue = 50;
|
||||
|
||||
String s;
|
||||
|
||||
s << "keyBytes=" << String (uint64(KeyBytes)) << ", maxItems=" << String (maxItems);
|
||||
beginTestCase (s);
|
||||
|
||||
// Set up the key and value files
|
||||
File const path (File::createTempFile (""));
|
||||
|
||||
{
|
||||
// open the db
|
||||
ScopedPointer <KeyvaDB> db (createDB (KeyBytes, path));
|
||||
|
||||
Payload payload (maxPayloadBytes);
|
||||
Payload check (maxPayloadBytes);
|
||||
|
||||
{
|
||||
// Create an array of ascending integers.
|
||||
HeapBlock <unsigned int> items (maxItems);
|
||||
for (unsigned int i = 0; i < maxItems; ++i)
|
||||
items [i] = i;
|
||||
|
||||
// Now shuffle it deterministically.
|
||||
repeatableShuffle (maxItems, items, seedValue);
|
||||
|
||||
// Write all the keys of integers.
|
||||
for (unsigned int i = 0; i < maxItems; ++i)
|
||||
{
|
||||
unsigned int keyIndex = items [i];
|
||||
|
||||
KeyType const key = KeyType::createFromInteger (keyIndex);
|
||||
|
||||
payload.repeatableRandomFill (1, maxPayloadBytes, keyIndex + seedValue);
|
||||
|
||||
db->put (key.cbegin (), payload.data.getData (), payload.bytes);
|
||||
|
||||
{
|
||||
// VFALCO TODO Check what we just wrote?
|
||||
//db->get (key.cbegin (), check.data.getData (), payload.bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Go through all of our keys and try to retrieve them.
|
||||
// since this is done in ascending order, we should get
|
||||
// random seeks at this point.
|
||||
//
|
||||
PayloadGetCallback cb;
|
||||
for (unsigned int keyIndex = 0; keyIndex < maxItems; ++keyIndex)
|
||||
{
|
||||
KeyType const v = KeyType::createFromInteger (keyIndex);
|
||||
|
||||
bool const found = db->get (v.cbegin (), &cb);
|
||||
|
||||
expect (found, "Should be found");
|
||||
|
||||
if (found)
|
||||
{
|
||||
payload.repeatableRandomFill (1, maxPayloadBytes, keyIndex + seedValue);
|
||||
|
||||
expect (payload == cb.payload, "Should be equal");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Re-open the database and confirm the data
|
||||
ScopedPointer <KeyvaDB> db (createDB (KeyBytes, path));
|
||||
|
||||
Payload payload (maxPayloadBytes);
|
||||
Payload check (maxPayloadBytes);
|
||||
|
||||
PayloadGetCallback cb;
|
||||
for (unsigned int keyIndex = 0; keyIndex < maxItems; ++keyIndex)
|
||||
{
|
||||
KeyType const v = KeyType::createFromInteger (keyIndex);
|
||||
|
||||
bool const found = db->get (v.cbegin (), &cb);
|
||||
|
||||
expect (found, "Should be found");
|
||||
|
||||
if (found)
|
||||
{
|
||||
payload.repeatableRandomFill (1, maxPayloadBytes, keyIndex + seedValue);
|
||||
|
||||
expect (payload == cb.payload, "Should be equal");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deleteDBFiles (path);
|
||||
}
|
||||
|
||||
void runTest ()
|
||||
{
|
||||
testKeySize <4> (500);
|
||||
testKeySize <32> (4000);
|
||||
}
|
||||
|
||||
KeyvaDBTests () : UnitTest ("KeyvaDB", "beast", runManual)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
static KeyvaDBTests keyvaDBTests;
|
||||
@@ -1,55 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_KEYVADB_H_INCLUDED
|
||||
#define BEAST_KEYVADB_H_INCLUDED
|
||||
|
||||
/** Specialized Key/value database
|
||||
|
||||
Once written, a value can never be modified.
|
||||
*/
|
||||
class KeyvaDB : LeakChecked <KeyvaDB>
|
||||
{
|
||||
public:
|
||||
class GetCallback
|
||||
{
|
||||
public:
|
||||
virtual void* getStorageForValue (int valueBytes) = 0;
|
||||
};
|
||||
|
||||
static KeyvaDB* New (int keyBytes,
|
||||
int keyBlockDepth,
|
||||
File keyPath,
|
||||
File valPath);
|
||||
|
||||
virtual ~KeyvaDB () { }
|
||||
|
||||
// VFALCO TODO Make the return value a Result so we can
|
||||
// detect corruption and errors!
|
||||
//
|
||||
virtual bool get (void const* key, GetCallback* callback) = 0;
|
||||
|
||||
// VFALCO TODO Use Result for return value
|
||||
//
|
||||
virtual void put (void const* key, void const* value, int valueBytes) = 0;
|
||||
|
||||
virtual void flush () = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,29 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "BeastConfig.h"
|
||||
|
||||
#include "beast_extras.h"
|
||||
|
||||
namespace beast
|
||||
{
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_EXTRAS_H_INCLUDED
|
||||
#define BEAST_EXTRAS_H_INCLUDED
|
||||
|
||||
// Adds boost-specific features to beast.
|
||||
|
||||
#include <boost/version.hpp>
|
||||
|
||||
// lockable_traits was added in 1.53.0
|
||||
#ifndef BEAST_BOOST_HAS_LOCKABLES
|
||||
# if BOOST_VERSION >= 105300
|
||||
# define BEAST_BOOST_HAS_LOCKABLES 1
|
||||
# else
|
||||
# define BEAST_BOOST_HAS_LOCKABLES 0
|
||||
# endif
|
||||
#endif
|
||||
#if BEAST_BOOST_HAS_LOCKABLES
|
||||
# include <boost/thread/lockable_traits.hpp>
|
||||
#endif
|
||||
|
||||
#if BEAST_BOOST_HAS_LOCKABLES
|
||||
# include "traits/BoostLockableTraits.h"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,125 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_BOOSTLOCKABLERAITS_H_INCLUDED
|
||||
#define BEAST_BOOSTLOCKABLERAITS_H_INCLUDED
|
||||
|
||||
// Adds beast specializations for boost lockables.
|
||||
|
||||
// beast forward declarations
|
||||
|
||||
namespace beast
|
||||
{
|
||||
|
||||
class CriticalSection;
|
||||
|
||||
template <typename Mutex>
|
||||
class TrackedMutex;
|
||||
|
||||
template <typename Mutex>
|
||||
class UntrackedMutex;
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// boost Mutex concepts
|
||||
//
|
||||
// http://www.boost.org/doc/libs/1_54_0/doc/html/thread/synchronization.html#thread.synchronization.mutex_concepts
|
||||
|
||||
namespace boost
|
||||
{
|
||||
|
||||
namespace sync
|
||||
{
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// CriticalSection
|
||||
//
|
||||
|
||||
// BasicLockable
|
||||
//
|
||||
template <>
|
||||
struct is_basic_lockable <beast::CriticalSection>
|
||||
: public boost::true_type { };
|
||||
|
||||
// Lockable
|
||||
//
|
||||
template <>
|
||||
struct is_lockable <beast::CriticalSection>
|
||||
: public boost::true_type { };
|
||||
|
||||
// RecursiveLockable
|
||||
//
|
||||
template <>
|
||||
struct is_recursive_mutex_sur_parole <beast::CriticalSection>
|
||||
: public boost::true_type { };
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// TrackedMutex <>
|
||||
//
|
||||
|
||||
// BasicLockable
|
||||
//
|
||||
template <class Mutex>
|
||||
class is_basic_lockable <beast::TrackedMutex <Mutex> >
|
||||
: public boost::sync::is_basic_lockable <Mutex> { };
|
||||
|
||||
// Lockable
|
||||
//
|
||||
template <class Mutex>
|
||||
class is_lockable <beast::TrackedMutex <Mutex> >
|
||||
: public boost::sync::is_lockable <Mutex> { };
|
||||
|
||||
// RecursiveLockable
|
||||
//
|
||||
template <class Mutex>
|
||||
struct is_recursive_mutex_sur_parole <beast::TrackedMutex <Mutex> >
|
||||
: public boost::true_type { };
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// UntrackedMutex <>
|
||||
//
|
||||
|
||||
// BasicLockable
|
||||
//
|
||||
template <class Mutex>
|
||||
class is_basic_lockable <beast::UntrackedMutex <Mutex> >
|
||||
: public boost::sync::is_basic_lockable <Mutex> { };
|
||||
|
||||
// Lockable
|
||||
//
|
||||
template <class Mutex>
|
||||
class is_lockable <beast::UntrackedMutex <Mutex> >
|
||||
: public boost::sync::is_lockable <Mutex> { };
|
||||
|
||||
// RecursiveLockable
|
||||
//
|
||||
template <class Mutex>
|
||||
struct is_recursive_mutex_sur_parole <beast::UntrackedMutex <Mutex> >
|
||||
: public boost::true_type { };
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user