mirror of
https://github.com/WinampDesktop/winamp.git
synced 2025-06-17 16:45:47 -04:00
Initial community commit
This commit is contained in:
49
Src/replicant/nu/AutoBuffer.h
Normal file
49
Src/replicant/nu/AutoBuffer.h
Normal file
@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#include "foundation/error.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
/* an automatically growing buffer */
|
||||
|
||||
class AutoBuffer
|
||||
{
|
||||
public:
|
||||
AutoBuffer()
|
||||
{
|
||||
buffer=0;
|
||||
buffer_length=0;
|
||||
}
|
||||
|
||||
~AutoBuffer()
|
||||
{
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
operator void *()
|
||||
{
|
||||
return buffer;
|
||||
}
|
||||
|
||||
template <class ptr_t>
|
||||
ptr_t Get()
|
||||
{
|
||||
return (ptr_t)buffer;
|
||||
}
|
||||
|
||||
int Reserve(size_t new_size)
|
||||
{
|
||||
if (new_size <= buffer_length)
|
||||
return NErr_Success;
|
||||
|
||||
void *new_buffer = realloc(buffer, new_size);
|
||||
if (!new_buffer)
|
||||
return NErr_OutOfMemory;
|
||||
|
||||
buffer = new_buffer;
|
||||
buffer_length = new_size;
|
||||
return NErr_Success;
|
||||
}
|
||||
|
||||
private:
|
||||
void *buffer;
|
||||
size_t buffer_length;
|
||||
};
|
141
Src/replicant/nu/AutoChar.h
Normal file
141
Src/replicant/nu/AutoChar.h
Normal file
@ -0,0 +1,141 @@
|
||||
#ifndef NULLSOFT_AUTOCHARH
|
||||
#define NULLSOFT_AUTOCHARH
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
inline char *AutoCharDupN(const wchar_t *convert, size_t len, UINT codePage = CP_ACP, UINT flags=0)
|
||||
{
|
||||
if (!convert)
|
||||
return 0;
|
||||
int size = WideCharToMultiByte(codePage, flags, convert, (int)len, 0, 0, NULL, NULL);
|
||||
|
||||
if (!size)
|
||||
return 0;
|
||||
|
||||
char *narrow = (char *)malloc((size+1)*sizeof(char));
|
||||
if ( narrow == 0 )
|
||||
return 0;
|
||||
|
||||
if (!WideCharToMultiByte(codePage, flags, convert, (int)len, narrow, size, NULL, NULL))
|
||||
{
|
||||
free(narrow);
|
||||
narrow=0;
|
||||
}
|
||||
else
|
||||
narrow[size]=0;
|
||||
|
||||
return narrow;
|
||||
}
|
||||
|
||||
inline char *AutoCharDup(const wchar_t *convert, UINT codePage = CP_ACP, UINT flags=0)
|
||||
{
|
||||
if (!convert)
|
||||
return 0;
|
||||
|
||||
int size = WideCharToMultiByte(codePage, flags, convert, -1, 0, 0, NULL, NULL);
|
||||
|
||||
if (!size)
|
||||
return 0;
|
||||
|
||||
char *narrow = (char *)malloc(size*sizeof(char));
|
||||
if ( narrow == 0 )
|
||||
return 0;
|
||||
|
||||
if (!WideCharToMultiByte(codePage, flags, convert, -1, narrow, size, NULL, NULL))
|
||||
{
|
||||
free(narrow);
|
||||
narrow=0;
|
||||
}
|
||||
return narrow;
|
||||
}
|
||||
|
||||
class AutoChar
|
||||
{
|
||||
public:
|
||||
AutoChar(const wchar_t *convert, UINT codePage = CP_ACP, UINT flags=0) : narrow(0)
|
||||
{
|
||||
narrow = AutoCharDup(convert, codePage, flags);
|
||||
}
|
||||
~AutoChar()
|
||||
{
|
||||
free(narrow);
|
||||
narrow=0;
|
||||
}
|
||||
operator const char *()
|
||||
{
|
||||
return narrow;
|
||||
}
|
||||
operator char *()
|
||||
{
|
||||
return narrow;
|
||||
}
|
||||
protected:
|
||||
AutoChar() : narrow(0)
|
||||
{
|
||||
}
|
||||
char *narrow;
|
||||
};
|
||||
|
||||
class AutoCharN : public AutoChar
|
||||
{
|
||||
public:
|
||||
AutoCharN(const wchar_t *convert, size_t len, UINT codePage = CP_ACP, UINT flags=0)
|
||||
{
|
||||
narrow = AutoCharDupN(convert, len, codePage, flags);
|
||||
}
|
||||
};
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#include <wchar.h>
|
||||
|
||||
inline char *AutoCharDup(const wchar_t *convert)
|
||||
{
|
||||
if (!convert)
|
||||
return 0;
|
||||
|
||||
size_t size = wcslen(convert)+1;
|
||||
|
||||
if (!size)
|
||||
return 0;
|
||||
|
||||
char *narrow = (char *)malloc(size*sizeof(char));
|
||||
if ( narrow == 0 )
|
||||
return 0;
|
||||
|
||||
if (wcstombs(narrow, convert, size) == (size_t)-1)
|
||||
{
|
||||
free(narrow);
|
||||
narrow=0;
|
||||
}
|
||||
return narrow;
|
||||
}
|
||||
|
||||
|
||||
class AutoChar
|
||||
{
|
||||
public:
|
||||
|
||||
AutoChar(const wchar_t *convert) : narrow(0)
|
||||
{
|
||||
narrow = AutoCharDup(convert);
|
||||
}
|
||||
~AutoChar()
|
||||
{
|
||||
free(narrow);
|
||||
narrow=0;
|
||||
}
|
||||
operator const char *()
|
||||
{
|
||||
return narrow;
|
||||
}
|
||||
operator char *()
|
||||
{
|
||||
return narrow;
|
||||
}
|
||||
private:
|
||||
char *narrow;
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
377
Src/replicant/nu/AutoLock.h
Normal file
377
Src/replicant/nu/AutoLock.h
Normal file
@ -0,0 +1,377 @@
|
||||
#ifndef AUTOLOCKH
|
||||
#define AUTOLOCKH
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma warning (disable:4786)
|
||||
#include <windows.h>
|
||||
#elif defined(__linux__) || defined(__APPLE__)
|
||||
#include <pthread.h>
|
||||
#else
|
||||
#error port me!!
|
||||
#endif
|
||||
|
||||
/*
|
||||
NULLSOFT_LOCK_OUTPUT_STATUS turns on/off debugging output
|
||||
this can be VERY useful if you are trying to find a deadlock
|
||||
each time the guard is locked or unlocked, it outputs a list of
|
||||
any threads using the mutex, and their function stack
|
||||
*/
|
||||
//#define NULLSOFT_LOCK_OUTPUT_STATS
|
||||
|
||||
#ifdef NULLSOFT_LOCK_OUTPUT_STATS
|
||||
|
||||
#include <string> // we save each function name as a string
|
||||
#include <deque> // we make a list of the recursive function stack for each thread
|
||||
#include <map> // and map
|
||||
#include <iostream> // we output to std::cerr
|
||||
#include <windows.h>
|
||||
|
||||
|
||||
/*****
|
||||
Description:
|
||||
This class uses scoping to wrap a critical section (lightweight in-process mutex)
|
||||
The constructor enters the mutex and the destructor leaves it. This allows it to
|
||||
take advantage of automatic scoping in C++, because C++ automatically calls the destructor
|
||||
when an object leaves scope.
|
||||
|
||||
This is _especially_ useful when you have multiple return paths, since you don't have to
|
||||
repeat mutex-leaving code.
|
||||
|
||||
To use:
|
||||
Make a LockGuard for a resource you want to protect. The guard is shared, so make it part
|
||||
of your class, or a global, or whatever. The LockGuard is essentially a "token", equivalent
|
||||
to your mutex handle or critical section handle.
|
||||
|
||||
Make an AutoLock object on the stack to lock. It will unlock automatically when the object
|
||||
leaves scope.
|
||||
|
||||
Note: You'll want to make an object on the stack - don't use a heap object (new/delete)
|
||||
unless you have weird requirements and know what you are doing.
|
||||
|
||||
Example:
|
||||
|
||||
class MyClass
|
||||
{
|
||||
LockGuard fileGuard;
|
||||
fstream file;
|
||||
void DumpSomeData() //
|
||||
{
|
||||
AutoLock lock(fileGuard);
|
||||
file << GetData();
|
||||
}
|
||||
|
||||
void CALLBACK NewData() // potentially called by another thread
|
||||
{
|
||||
AutoLock lock(fileGuard)
|
||||
file << newData;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Tip: You can use "false scoping" to tweak the mutex lifetime, for example:
|
||||
|
||||
void DoStuff()
|
||||
{
|
||||
a = GetData();
|
||||
{ // false scope
|
||||
AutoLock lock(dataGuard);
|
||||
DoCalculationsWith(a);
|
||||
} // mutex will release here
|
||||
SetData(a);
|
||||
}
|
||||
|
||||
Tip: A common mistake is making a temporary object.
|
||||
i.e.
|
||||
CORRECT: AutoLock lock(fileGuard); // an AutoLock object called "lock" is put on the stack
|
||||
INCORRECT: AutoLock(fileGuard); // An unnamed temporary is created which will be destroyed IMMEDIATELY
|
||||
|
||||
*******/
|
||||
#define MANUALLOCKNAME(x) x
|
||||
#define LOCKNAME(x) ,x
|
||||
#define GUARDNAME(x) (x)
|
||||
namespace Nullsoft
|
||||
{
|
||||
namespace Utility
|
||||
{
|
||||
/* the token which represents a resource to be locked */
|
||||
class LockGuard
|
||||
{
|
||||
public:
|
||||
inline LockGuard(const char *name = "Unnamed Guard")
|
||||
{
|
||||
lockName = name;
|
||||
InitializeCriticalSection(&cerr_cs);
|
||||
InitializeCriticalSection(&map_cs);
|
||||
InitializeCriticalSection(&m_cs);
|
||||
}
|
||||
|
||||
inline ~LockGuard()
|
||||
{
|
||||
DeleteCriticalSection(&cerr_cs);
|
||||
DeleteCriticalSection(&map_cs);
|
||||
DeleteCriticalSection(&m_cs);
|
||||
}
|
||||
|
||||
inline void Lock()
|
||||
{
|
||||
EnterCriticalSection(&m_cs);
|
||||
}
|
||||
|
||||
inline void Unlock()
|
||||
{
|
||||
LeaveCriticalSection(&m_cs);
|
||||
}
|
||||
|
||||
int ThreadCount()
|
||||
{
|
||||
EnterCriticalSection(&map_cs);
|
||||
int count = 0;
|
||||
for (ThreadMap::iterator itr = threads.begin(); itr != threads.end(); itr++)
|
||||
{
|
||||
if (!itr->second.empty())
|
||||
count++;
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&map_cs);
|
||||
return count;
|
||||
}
|
||||
|
||||
void Display()
|
||||
{
|
||||
EnterCriticalSection(&map_cs);
|
||||
EnterCriticalSection(&cerr_cs);
|
||||
|
||||
if (ThreadCount() > 1 && owner)
|
||||
{
|
||||
std::cerr << "Guard: " << lockName << std::endl;
|
||||
for (ThreadMap::iterator itr = threads.begin(); itr != threads.end(); itr++)
|
||||
{
|
||||
if (itr->second.empty())
|
||||
continue;
|
||||
|
||||
std::cerr << " Thread ID: " << std::hex << itr->first << std::dec;
|
||||
if (owner == itr->first)
|
||||
std::cerr << " [holding the mutex] *****";
|
||||
else
|
||||
std::cerr << " [blocked]";
|
||||
std::cerr << std::endl;
|
||||
for (FunctionStack::iterator fitr = itr->second.begin(); fitr != itr->second.end(); fitr++)
|
||||
{
|
||||
std::cerr << " " << *fitr << "();" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
LeaveCriticalSection(&cerr_cs);
|
||||
LeaveCriticalSection(&map_cs);
|
||||
}
|
||||
|
||||
void In(DWORD thread, const char *functionName)
|
||||
{
|
||||
EnterCriticalSection(&map_cs);
|
||||
threads[thread].push_back(functionName);
|
||||
LeaveCriticalSection(&map_cs);
|
||||
}
|
||||
|
||||
void Out(DWORD thread)
|
||||
{
|
||||
EnterCriticalSection(&map_cs);
|
||||
threads[thread].pop_back();
|
||||
LeaveCriticalSection(&map_cs);
|
||||
}
|
||||
std::string lockName;
|
||||
CRITICAL_SECTION cerr_cs, map_cs;
|
||||
typedef std::deque<std::string> FunctionStack; // this typedef reduce ugly c++ <>::<>::<> overkill
|
||||
typedef std::map<DWORD, FunctionStack> ThreadMap;
|
||||
ThreadMap threads;
|
||||
|
||||
DWORD owner;
|
||||
private:
|
||||
CRITICAL_SECTION m_cs;
|
||||
|
||||
};
|
||||
|
||||
/* an AutoLock locks a resource (represented by a LockGuard) for the duration of its lifetime */
|
||||
class AutoLock
|
||||
{
|
||||
public:
|
||||
/*
|
||||
@param functionName The function name which wants the mutex
|
||||
we pass it in as a char * even though it'll be converted to a std::string
|
||||
to reduce overhead when OUTPUT_STATS is off
|
||||
*/
|
||||
inline AutoLock(LockGuard &_guard, const char *functionName = "function name not passed") : guard(&_guard)
|
||||
{
|
||||
ManualLock(functionName);
|
||||
}
|
||||
inline void ManualLock(char *functionName = "manual lock")
|
||||
{
|
||||
thisThread = GetCurrentThreadId();
|
||||
guard->In(thisThread, functionName);
|
||||
guard->Display();
|
||||
guard->Lock();
|
||||
guard->owner = thisThread;
|
||||
guard->Display();
|
||||
}
|
||||
|
||||
inline void ManualUnlock()
|
||||
{
|
||||
guard->Display();
|
||||
guard->Unlock();
|
||||
|
||||
InterlockedCompareExchange((LONG volatile *)&guard->owner, 0, (LONG)thisThread);
|
||||
/* above line is functionally equivalent to:
|
||||
if (guard->owner == thisThread)
|
||||
guard->owner=0;
|
||||
*/
|
||||
guard->Out(thisThread);
|
||||
guard->Display();
|
||||
}
|
||||
|
||||
inline ~AutoLock()
|
||||
{
|
||||
ManualUnlock();
|
||||
}
|
||||
|
||||
LockGuard *guard;
|
||||
DWORD thisThread;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
#define MANUALLOCKNAME(x)
|
||||
#define LOCKNAME(x)
|
||||
#define GUARDNAME(x)
|
||||
namespace nu
|
||||
{
|
||||
/* the token which represents a resource to be locked */
|
||||
class LockGuard
|
||||
{
|
||||
public:
|
||||
inline LockGuard(const char *guardName = "")
|
||||
{
|
||||
#ifdef _WIN32
|
||||
InitializeCriticalSection(&m_cs);
|
||||
#elif defined(__linux__)
|
||||
pthread_mutexattr_t mtxattr;
|
||||
pthread_mutexattr_init(&mtxattr);
|
||||
pthread_mutexattr_settype(&mtxattr, PTHREAD_MUTEX_RECURSIVE_NP );
|
||||
pthread_mutex_init(&mtx, &mtxattr);
|
||||
pthread_mutexattr_destroy(&mtxattr);
|
||||
#elif defined(__APPLE__)
|
||||
pthread_mutexattr_t mtxattr;
|
||||
pthread_mutexattr_init(&mtxattr);
|
||||
pthread_mutexattr_settype(&mtxattr, PTHREAD_MUTEX_RECURSIVE);
|
||||
pthread_mutex_init(&mtx, &mtxattr);
|
||||
pthread_mutexattr_destroy(&mtxattr);
|
||||
#else
|
||||
#error port me
|
||||
#endif
|
||||
}
|
||||
inline ~LockGuard()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
DeleteCriticalSection(&m_cs);
|
||||
#elif defined(__linux__) || defined(__APPLE__)
|
||||
pthread_mutex_destroy(&mtx);
|
||||
#else
|
||||
#error port me!
|
||||
#endif
|
||||
}
|
||||
inline void Lock()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
EnterCriticalSection(&m_cs);
|
||||
#elif defined(__linux__) || defined(__APPLE__)
|
||||
pthread_mutex_lock(&mtx);
|
||||
#else
|
||||
#error por tme!
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void Unlock()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
LeaveCriticalSection(&m_cs);
|
||||
#elif defined(__linux__) || defined(__APPLE__)
|
||||
pthread_mutex_unlock(&mtx);
|
||||
#else
|
||||
#error port me!
|
||||
#endif
|
||||
}
|
||||
private:
|
||||
#ifdef _WIN32
|
||||
CRITICAL_SECTION m_cs;
|
||||
#elif defined(__linux__) || defined(__APPLE__)
|
||||
pthread_mutex_t mtx;
|
||||
#else
|
||||
#error port me!
|
||||
#endif
|
||||
};
|
||||
|
||||
/* an AutoLock locks a resource (represented by a LockGuard) for the duration of its lifetime */
|
||||
class AutoLock
|
||||
{
|
||||
public:
|
||||
inline AutoLock(LockGuard &_guard) : guard(&_guard)
|
||||
{
|
||||
guard->Lock();
|
||||
}
|
||||
|
||||
inline AutoLock(LockGuard *_guard) : guard(_guard)
|
||||
{
|
||||
guard->Lock();
|
||||
}
|
||||
inline void ManualLock()
|
||||
{
|
||||
guard->Lock();
|
||||
}
|
||||
|
||||
inline void ManualUnlock()
|
||||
{
|
||||
guard->Unlock();
|
||||
|
||||
}
|
||||
inline ~AutoLock()
|
||||
{
|
||||
guard->Unlock();
|
||||
}
|
||||
LockGuard *guard;
|
||||
};
|
||||
|
||||
// will lock anything that implements Lock() and Unlock()
|
||||
template <class LockGuard_t>
|
||||
class AutoLockT
|
||||
{
|
||||
public:
|
||||
inline AutoLockT(LockGuard_t &_guard) : guard(&_guard)
|
||||
{
|
||||
guard->Lock();
|
||||
}
|
||||
|
||||
inline AutoLockT(LockGuard_t *_guard) : guard(_guard)
|
||||
{
|
||||
guard->Lock();
|
||||
}
|
||||
inline void ManualLock()
|
||||
{
|
||||
guard->Lock();
|
||||
}
|
||||
|
||||
inline void ManualUnlock()
|
||||
{
|
||||
guard->Unlock();
|
||||
|
||||
}
|
||||
inline ~AutoLockT()
|
||||
{
|
||||
guard->Unlock();
|
||||
}
|
||||
LockGuard_t *guard;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
50
Src/replicant/nu/AutoWide.h
Normal file
50
Src/replicant/nu/AutoWide.h
Normal file
@ -0,0 +1,50 @@
|
||||
#ifndef AUTOWIDEH
|
||||
#define AUTOWIDEH
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
inline wchar_t *AutoWideDup(const char *convert, UINT codePage=CP_ACP)
|
||||
{
|
||||
if (!convert)
|
||||
return 0;
|
||||
|
||||
|
||||
wchar_t *wide = 0;
|
||||
|
||||
int size = MultiByteToWideChar(codePage, 0, convert, -1, 0,0);
|
||||
if (!size)
|
||||
return 0;
|
||||
|
||||
wide = (wchar_t *)malloc(size*sizeof(wchar_t));
|
||||
if (!MultiByteToWideChar(codePage, 0, convert, -1, wide,size))
|
||||
{
|
||||
free(wide);
|
||||
wide=0;
|
||||
}
|
||||
return wide;
|
||||
}
|
||||
|
||||
class AutoWide
|
||||
{
|
||||
public:
|
||||
AutoWide(const char *convert, UINT codePage=CP_ACP) : wide(0)
|
||||
{
|
||||
wide = AutoWideDup(convert, codePage);
|
||||
}
|
||||
~AutoWide()
|
||||
{
|
||||
free(wide);
|
||||
wide=0;
|
||||
}
|
||||
operator wchar_t *()
|
||||
{
|
||||
return wide;
|
||||
}
|
||||
private:
|
||||
wchar_t *wide;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
44
Src/replicant/nu/Benchmark.h
Normal file
44
Src/replicant/nu/Benchmark.h
Normal file
@ -0,0 +1,44 @@
|
||||
#ifndef BENCHMARKH
|
||||
#define BENCHMARKH
|
||||
|
||||
|
||||
#if defined(_WIN32)
|
||||
static uint64_t Benchmark()
|
||||
{
|
||||
return GetTickCount64();
|
||||
}
|
||||
|
||||
#elif defined(__ANDROID__)
|
||||
#include <time.h>
|
||||
|
||||
// Make sure to divide by 1000000 (1 million) to get results in Milliseconds, as they are returned in nanoseconds
|
||||
static uint64_t Benchmark()
|
||||
{
|
||||
struct timespec ts;
|
||||
uint64_t count;
|
||||
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
|
||||
count=(uint64_t)ts.tv_sec*1000000000ULL + (uint64_t)ts.tv_nsec;
|
||||
return count;
|
||||
}
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
#include <mach/mach_time.h>
|
||||
static uint64_t Benchmark()
|
||||
{
|
||||
uint64_t absoluteTime;
|
||||
static mach_timebase_info_data_t timeBase = {0,0};
|
||||
|
||||
absoluteTime = mach_absolute_time();
|
||||
|
||||
if (0 == timeBase.denom)
|
||||
{
|
||||
kern_return_t err = mach_timebase_info(&timeBase);
|
||||
if (0 != err)
|
||||
return 0;
|
||||
}
|
||||
uint64_t nanoTime = absoluteTime * timeBase.numer / timeBase.denom;
|
||||
return nanoTime/(1000*1000);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
109
Src/replicant/nu/BitReader.cpp
Normal file
109
Src/replicant/nu/BitReader.cpp
Normal file
@ -0,0 +1,109 @@
|
||||
#include "BitReader.h"
|
||||
#include <string.h>
|
||||
|
||||
static uint32_t mask[8]=
|
||||
{
|
||||
0x1,
|
||||
0x3,
|
||||
0x7,
|
||||
0xF,
|
||||
0x1F,
|
||||
0x3F,
|
||||
0x7F,
|
||||
0xFF
|
||||
};
|
||||
|
||||
static uint32_t msk[33] =
|
||||
{
|
||||
0x00000000,0x00000001,0x00000003,0x00000007,
|
||||
0x0000000f,0x0000001f,0x0000003f,0x0000007f,
|
||||
0x000000ff,0x000001ff,0x000003ff,0x000007ff,
|
||||
0x00000fff,0x00001fff,0x00003fff,0x00007fff,
|
||||
0x0000ffff,0x0001ffff,0x0003ffff,0x0007ffff,
|
||||
0x000fffff,0x001fffff,0x003fffff,0x007fffff,
|
||||
0x00ffffff,0x01ffffff,0x03ffffff,0x07ffffff,
|
||||
0x0fffffff,0x1fffffff,0x3fffffff,0x7fffffff,
|
||||
0xffffffff
|
||||
};
|
||||
|
||||
void BitReader::alignbyte()
|
||||
{
|
||||
flushbits(numBits&7);
|
||||
}
|
||||
|
||||
void BitReader::getbytes(void *data, uint32_t n)
|
||||
{
|
||||
memcpy(data, this->data, n);
|
||||
flushbits(n*8);
|
||||
}
|
||||
|
||||
uint8_t BitReader::getbits1()
|
||||
{
|
||||
uint8_t byte = data[0];
|
||||
uint32_t count = (numBits-1) & 7;
|
||||
byte &= mask[count];
|
||||
byte >>= count;
|
||||
|
||||
numBits--;
|
||||
if ((numBits % 8) == 0)
|
||||
data++;
|
||||
return byte;
|
||||
}
|
||||
|
||||
uint32_t BitReader::getbits(uint32_t n)
|
||||
{
|
||||
uint32_t val = showbits(n);
|
||||
flushbits(n);
|
||||
return val;
|
||||
}
|
||||
|
||||
uint8_t BitReader::showbits1() const
|
||||
{
|
||||
uint8_t byte = data[0];
|
||||
uint32_t count = (numBits-1) & 7;
|
||||
byte &= mask[count];
|
||||
byte >>= count;
|
||||
return byte;
|
||||
}
|
||||
|
||||
uint32_t BitReader::showbits(uint32_t n) const
|
||||
{
|
||||
uint32_t val;
|
||||
switch((numBits+7) >> 3)
|
||||
{
|
||||
case 0:
|
||||
return 0;
|
||||
case 1:
|
||||
val=(data[0]<<24);
|
||||
break;
|
||||
case 2:
|
||||
val=(data[0]<<24) | (data[1]<<16);
|
||||
break;
|
||||
case 3:
|
||||
val=(data[0]<<24) | (data[1]<<16) | (data[2]<<8);
|
||||
break;
|
||||
default:
|
||||
val=(data[0]<<24) | (data[1]<<16) | (data[2]<<8) | data[3];
|
||||
break;
|
||||
}
|
||||
uint32_t c = ((numBits-1) & 7) + 25;
|
||||
return (val>>(c-n)) & msk[n];
|
||||
}
|
||||
|
||||
void BitReader::flushbits(uint32_t n)
|
||||
{
|
||||
uint32_t oldpos = (numBits+7)>>3;
|
||||
numBits-=n;
|
||||
uint32_t newpos = (numBits+7)>>3;
|
||||
data += (oldpos - newpos);
|
||||
}
|
||||
|
||||
bool BitReader::empty()
|
||||
{
|
||||
return numBits==0;
|
||||
}
|
||||
|
||||
uint32_t BitReader::size() const
|
||||
{
|
||||
return numBits;
|
||||
}
|
18
Src/replicant/nu/BitReader.h
Normal file
18
Src/replicant/nu/BitReader.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include "foundation/types.h"
|
||||
class BitReader
|
||||
{
|
||||
public:
|
||||
uint8_t getbits1();
|
||||
uint32_t getbits(uint32_t n);
|
||||
uint8_t showbits1() const;
|
||||
uint32_t showbits(uint32_t n) const;
|
||||
void flushbits(uint32_t n);
|
||||
bool empty();
|
||||
uint32_t size() const; // in bits
|
||||
void alignbyte(); // aligns bitstream to the next byte (or current byte if already aligned)
|
||||
void getbytes(void *data, uint32_t n);
|
||||
//private:
|
||||
const uint8_t *data;
|
||||
uint32_t numBits;
|
||||
};
|
203
Src/replicant/nu/ByteReader.c
Normal file
203
Src/replicant/nu/ByteReader.c
Normal file
@ -0,0 +1,203 @@
|
||||
#include "ByteReader.h"
|
||||
#include <string.h>
|
||||
/* generic LITTLE ENDIAN implementation */
|
||||
void bytereader_init(bytereader_t byte_reader, const void *data, size_t byte_length)
|
||||
{
|
||||
byte_reader->data = data;
|
||||
byte_reader->byte_length = byte_length;
|
||||
byte_reader->data_ptr = (const uint8_t *)data;
|
||||
}
|
||||
|
||||
|
||||
void bytereader_advance(bytereader_t byte_reader, size_t bytes)
|
||||
{
|
||||
byte_reader->byte_length -= bytes;
|
||||
byte_reader->data_ptr += bytes;
|
||||
}
|
||||
|
||||
void bytereader_reset(bytereader_t byte_reader)
|
||||
{
|
||||
size_t diff = byte_reader->data_ptr - (const uint8_t *)byte_reader->data;
|
||||
byte_reader->byte_length += diff;
|
||||
byte_reader->data_ptr = (const uint8_t *)byte_reader->data;
|
||||
}
|
||||
|
||||
size_t bytereader_find_zero(bytereader_t byte_reader)
|
||||
{
|
||||
size_t i=0;
|
||||
|
||||
for (i=0;i<byte_reader->byte_length && byte_reader->data_ptr[i];i++)
|
||||
{
|
||||
// empty loop
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/* n byte functions */
|
||||
void bytereader_show_n(bytereader_t byte_reader, void *destination, size_t bytes)
|
||||
{
|
||||
memcpy(destination, byte_reader->data_ptr, bytes);
|
||||
}
|
||||
|
||||
void bytereader_read_n(bytereader_t byte_reader, void *destination, size_t bytes)
|
||||
{
|
||||
memcpy(destination, byte_reader->data_ptr, bytes);
|
||||
bytereader_advance(byte_reader, bytes);
|
||||
}
|
||||
|
||||
/* 1 byte functions */
|
||||
uint8_t bytereader_show_u8(bytereader_t byte_reader)
|
||||
{
|
||||
return byte_reader->data_ptr[0];
|
||||
}
|
||||
|
||||
uint8_t bytereader_read_u8(bytereader_t byte_reader)
|
||||
{
|
||||
uint8_t ret = byte_reader->data_ptr[0];
|
||||
bytereader_advance(byte_reader, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int8_t bytereader_show_s8(bytereader_t byte_reader)
|
||||
{
|
||||
return *(const int8_t *)(byte_reader->data_ptr);
|
||||
}
|
||||
|
||||
int8_t bytereader_read_s8(bytereader_t byte_reader)
|
||||
{
|
||||
int8_t ret = *(const int8_t *)(byte_reader->data_ptr);
|
||||
bytereader_advance(byte_reader, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* 2 byte little-endian functions */
|
||||
|
||||
uint16_t bytereader_show_u16_le(bytereader_t byte_reader)
|
||||
{
|
||||
return (uint16_t)byte_reader->data_ptr[0] | ((uint16_t)byte_reader->data_ptr[1] << 8);
|
||||
}
|
||||
|
||||
uint16_t bytereader_read_u16_le(bytereader_t byte_reader)
|
||||
{
|
||||
uint16_t ret = bytereader_show_u16_le(byte_reader);
|
||||
bytereader_advance(byte_reader, 2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int16_t bytereader_show_s16_le(bytereader_t byte_reader)
|
||||
{
|
||||
return (int16_t)byte_reader->data_ptr[0] | ((int16_t)byte_reader->data_ptr[1] << 8);
|
||||
}
|
||||
|
||||
int16_t bytereader_read_s16_le(bytereader_t byte_reader)
|
||||
{
|
||||
int16_t ret = bytereader_show_s16_le(byte_reader);
|
||||
bytereader_advance(byte_reader, 2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* 2 byte big-endian functions */
|
||||
uint16_t bytereader_show_u16_be(bytereader_t byte_reader)
|
||||
{
|
||||
return (uint16_t)byte_reader->data_ptr[1] | ((uint16_t)byte_reader->data_ptr[0] << 8);
|
||||
}
|
||||
|
||||
uint16_t bytereader_read_u16_be(bytereader_t byte_reader)
|
||||
{
|
||||
uint16_t ret = bytereader_show_u16_be(byte_reader);
|
||||
bytereader_advance(byte_reader, 2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int16_t bytereader_show_s16_be(bytereader_t byte_reader)
|
||||
{
|
||||
return (int16_t)byte_reader->data_ptr[1] | ((int16_t)byte_reader->data_ptr[0] << 8);
|
||||
}
|
||||
|
||||
int16_t bytereader_read_s16_be(bytereader_t byte_reader)
|
||||
{
|
||||
int16_t ret = bytereader_show_s16_be(byte_reader);
|
||||
bytereader_advance(byte_reader, 2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* 4 byte functions */
|
||||
|
||||
uint32_t bytereader_show_u32_be(bytereader_t byte_reader)
|
||||
{
|
||||
uint32_t x;
|
||||
// big endian extract
|
||||
|
||||
x = byte_reader->data_ptr[0];
|
||||
x <<= 8;
|
||||
x |= byte_reader->data_ptr[1];
|
||||
x <<= 8;
|
||||
x |= byte_reader->data_ptr[2];
|
||||
x <<= 8;
|
||||
x |= byte_reader->data_ptr[3];
|
||||
return x;
|
||||
|
||||
}
|
||||
|
||||
uint32_t bytereader_read_u32_be(bytereader_t byte_reader)
|
||||
{
|
||||
uint32_t ret = bytereader_show_u32_be(byte_reader);
|
||||
bytereader_advance(byte_reader, 4);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* 4 byte little-endian functions */
|
||||
uint32_t bytereader_show_u32_le(bytereader_t byte_reader)
|
||||
{
|
||||
uint32_t x;
|
||||
// little endian extract
|
||||
|
||||
x = byte_reader->data_ptr[3];
|
||||
x <<= 8;
|
||||
x |= byte_reader->data_ptr[2];
|
||||
x <<= 8;
|
||||
x |= byte_reader->data_ptr[1];
|
||||
x <<= 8;
|
||||
x |= byte_reader->data_ptr[0];
|
||||
return x;
|
||||
}
|
||||
|
||||
uint32_t bytereader_read_u32_le(bytereader_t byte_reader)
|
||||
{
|
||||
uint32_t ret = bytereader_show_u32_le(byte_reader);
|
||||
bytereader_advance(byte_reader, 4);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* float functions */
|
||||
float bytereader_show_f32_be(bytereader_t byte_reader)
|
||||
{
|
||||
uint32_t ret = bytereader_show_u32_be(byte_reader);
|
||||
return *(float *)(&ret);
|
||||
}
|
||||
|
||||
float bytereader_read_f32_be(bytereader_t byte_reader)
|
||||
{
|
||||
uint32_t ret = bytereader_read_u32_be(byte_reader);
|
||||
return *(float *)(&ret);
|
||||
}
|
||||
|
||||
GUID bytereader_read_uuid_be(bytereader_t byte_reader)
|
||||
{
|
||||
GUID guid_value;
|
||||
guid_value.Data1 = bytereader_read_u32_be(byte_reader);
|
||||
guid_value.Data2 = bytereader_read_u16_be(byte_reader);
|
||||
guid_value.Data3 = bytereader_read_u16_be(byte_reader);
|
||||
bytereader_read_n(byte_reader, guid_value.Data4, 8);
|
||||
return guid_value;
|
||||
}
|
||||
|
||||
GUID bytereader_read_uuid_le(bytereader_t byte_reader)
|
||||
{
|
||||
GUID guid_value;
|
||||
guid_value.Data1 = bytereader_read_u32_le(byte_reader);
|
||||
guid_value.Data2 = bytereader_read_u16_le(byte_reader);
|
||||
guid_value.Data3 = bytereader_read_u16_le(byte_reader);
|
||||
bytereader_read_n(byte_reader, guid_value.Data4, 8);
|
||||
return guid_value;
|
||||
}
|
83
Src/replicant/nu/ByteReader.h
Normal file
83
Src/replicant/nu/ByteReader.h
Normal file
@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include "foundation/types.h"
|
||||
|
||||
/* A simple byte-oriented reader.
|
||||
use this instead of manual parsing as this deals with memory alignment issues
|
||||
for you.
|
||||
memory alignment can be critical and annoying on some architectures (e.g. PowerPC)
|
||||
it also handles little-endian/big-endian issues
|
||||
|
||||
Usually you just make one of these things on the stack, passing in your buffer and length
|
||||
|
||||
S is signed and U is unsigned
|
||||
Show functions will give you data w/o moving the stream position
|
||||
Align versions of the functions will assume the stream is properly aligned
|
||||
LE versions of the functions treat the byte stream as little-endian oriented
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct bytereader_struct_t
|
||||
{
|
||||
size_t byte_length;
|
||||
const uint8_t *data_ptr;
|
||||
const void *data;
|
||||
} bytereader_value_t, bytereader_s, *bytereader_t;
|
||||
|
||||
void bytereader_init(bytereader_t byte_reader, const void *data, size_t byte_length);
|
||||
static size_t bytereader_size(bytereader_t byte_reader) /* returns remaining bytes in stream */
|
||||
{
|
||||
return byte_reader->byte_length;
|
||||
}
|
||||
void bytereader_advance(bytereader_t byte_reader, size_t bytes); /* advances the byte stream */
|
||||
void bytereader_reset(bytereader_t byte_reader); /* reset the data pointer and size back to the original position */
|
||||
static const void *bytereader_pointer(bytereader_t byte_reader) /* returns a pointer to the current bitstream position */
|
||||
{
|
||||
return byte_reader->data_ptr;
|
||||
}
|
||||
|
||||
/* returns the number of bytes to the next 0, or the end of the buffer */
|
||||
size_t bytereader_find_zero(bytereader_t byte_reader);
|
||||
|
||||
/* n byte functions (basically memcpy) */
|
||||
void bytereader_show_n(bytereader_t byte_reader, void *destination, size_t bytes);
|
||||
void bytereader_read_n(bytereader_t byte_reader, void *destination, size_t bytes);
|
||||
|
||||
/* 1 byte functions */
|
||||
uint8_t bytereader_show_u8(bytereader_t byte_reader);
|
||||
uint8_t bytereader_read_u8(bytereader_t byte_reader);
|
||||
int8_t bytereader_show_s8(bytereader_t byte_reader);
|
||||
int8_t bytereader_read_s8(bytereader_t byte_reader);
|
||||
|
||||
/* 2 byte little endian functions */
|
||||
uint16_t bytereader_show_u16_le(bytereader_t byte_reader);
|
||||
uint16_t bytereader_read_u16_le(bytereader_t byte_reader);
|
||||
int16_t bytereader_show_s16_le(bytereader_t byte_reader);
|
||||
int16_t bytereader_read_s16_le(bytereader_t byte_reader);
|
||||
|
||||
/* 2 byte big-endian functions */
|
||||
uint16_t bytereader_show_u16_be(bytereader_t byte_reader);
|
||||
uint16_t bytereader_read_u16_be(bytereader_t byte_reader);
|
||||
int16_t bytereader_show_s16_be(bytereader_t byte_reader);
|
||||
int16_t bytereader_read_s16_be(bytereader_t byte_reader);
|
||||
|
||||
/* 4 byte big-endian functions */
|
||||
uint32_t bytereader_show_u32_be(bytereader_t byte_reader);
|
||||
uint32_t bytereader_read_u32_be(bytereader_t byte_reader);
|
||||
|
||||
/* 4 byte little-endian functions */
|
||||
uint32_t bytereader_show_u32_le(bytereader_t byte_reader);
|
||||
uint32_t bytereader_read_u32_le(bytereader_t byte_reader);
|
||||
|
||||
/* float functions */
|
||||
float bytereader_show_f32_be(bytereader_t byte_reader);
|
||||
float bytereader_read_f32_be(bytereader_t byte_reader);
|
||||
|
||||
GUID bytereader_read_uuid_be(bytereader_t byte_reader);
|
||||
GUID bytereader_read_uuid_le(bytereader_t byte_reader);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
77
Src/replicant/nu/ByteWriter.c
Normal file
77
Src/replicant/nu/ByteWriter.c
Normal file
@ -0,0 +1,77 @@
|
||||
#include "ByteWriter.h"
|
||||
#include <string.h>
|
||||
|
||||
/* generic endian-agnostic implementation. this code assumes that unaligned accesses are illegal */
|
||||
void bytewriter_init(bytewriter_t byte_writer, void *data, size_t byte_length)
|
||||
{
|
||||
byte_writer->data = data;
|
||||
byte_writer->byte_length = byte_length;
|
||||
byte_writer->data_ptr = (uint8_t *)data;
|
||||
}
|
||||
|
||||
/* n byte functions */
|
||||
void bytewriter_write_n(bytewriter_t byte_writer, const void *source, size_t bytes)
|
||||
{
|
||||
memcpy(byte_writer->data_ptr, source, bytes);
|
||||
byte_writer->data_ptr += bytes;
|
||||
}
|
||||
|
||||
void bytewriter_write_u8(bytewriter_t byte_writer, uint8_t value)
|
||||
{
|
||||
*byte_writer->data_ptr++ = value;
|
||||
byte_writer->byte_length--;
|
||||
}
|
||||
|
||||
void bytewriter_write_u16_le(bytewriter_t byte_writer, uint16_t value)
|
||||
{
|
||||
*byte_writer->data_ptr++ = value & 0xFF;
|
||||
value >>= 8;
|
||||
*byte_writer->data_ptr++ = value & 0xFF;
|
||||
byte_writer->byte_length -= 2;
|
||||
}
|
||||
|
||||
void bytewriter_write_u16_be(bytewriter_t byte_writer, uint16_t value)
|
||||
{
|
||||
*byte_writer->data_ptr++ = (value >> 8) & 0xFF;
|
||||
*byte_writer->data_ptr++ = value & 0xFF;
|
||||
byte_writer->byte_length -= 2;
|
||||
}
|
||||
|
||||
void bytewriter_write_u32_le(bytewriter_t byte_writer, uint32_t value)
|
||||
{
|
||||
*byte_writer->data_ptr++ = value & 0xFF;
|
||||
value >>= 8;
|
||||
*byte_writer->data_ptr++ = value & 0xFF;
|
||||
value >>= 8;
|
||||
*byte_writer->data_ptr++ = value & 0xFF;
|
||||
value >>= 8;
|
||||
*byte_writer->data_ptr++ = value & 0xFF;
|
||||
byte_writer->byte_length -= 4;
|
||||
}
|
||||
|
||||
void bytewriter_write_u32_be(bytewriter_t byte_writer, uint32_t value)
|
||||
{
|
||||
*byte_writer->data_ptr++ = (value >> 24) & 0xFF;
|
||||
*byte_writer->data_ptr++ = (value >> 16) & 0xFF;
|
||||
*byte_writer->data_ptr++ = (value >> 8) & 0xFF;
|
||||
*byte_writer->data_ptr++ = value & 0xFF;
|
||||
byte_writer->byte_length -= 4;
|
||||
}
|
||||
|
||||
void bytewriter_write_zero_n(bytewriter_t byte_writer, size_t bytes)
|
||||
{
|
||||
size_t i;
|
||||
for (i=0;i<bytes;i++)
|
||||
{
|
||||
*byte_writer->data_ptr++ = 0;
|
||||
}
|
||||
byte_writer->byte_length -= bytes;
|
||||
}
|
||||
|
||||
void bytewriter_write_uuid_be(bytewriter_t byte_writer, GUID guid_value)
|
||||
{
|
||||
bytewriter_write_u32_be(byte_writer, guid_value.Data1);
|
||||
bytewriter_write_u16_be(byte_writer, guid_value.Data2);
|
||||
bytewriter_write_u16_be(byte_writer, guid_value.Data3);
|
||||
bytewriter_write_n(byte_writer, guid_value.Data4, 8);
|
||||
}
|
48
Src/replicant/nu/ByteWriter.h
Normal file
48
Src/replicant/nu/ByteWriter.h
Normal file
@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
#include "foundation/types.h"
|
||||
/* A simple byte-oriented writer.
|
||||
use this instead of manually writing data as this deals with memory alignment issues
|
||||
for you.
|
||||
memory alignment can be critical and annoying on some architectures (e.g. PowerPC)
|
||||
it also handles little-endian/big-endian issues
|
||||
|
||||
Usually you just make one of these things on the stack, passing in your buffer and length
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct bytewriter_struct_t
|
||||
{
|
||||
size_t byte_length;
|
||||
uint8_t *data_ptr;
|
||||
void *data;
|
||||
} bytewriter_s, *bytewriter_t;
|
||||
|
||||
void bytewriter_init(bytewriter_t byte_writer, void *data, size_t byte_length);
|
||||
static size_t bytewriter_size(bytewriter_t byte_writer) /* returns remaining bytes in stream */
|
||||
{
|
||||
return byte_writer->byte_length;
|
||||
}
|
||||
static void *bytewriter_pointer(bytewriter_t byte_writer) /* returns a pointer to the current bitstream position */
|
||||
{
|
||||
return byte_writer->data_ptr;
|
||||
}
|
||||
static void bytewriter_advance(bytewriter_t byte_writer, size_t bytes) /* advances the byte stream */
|
||||
{
|
||||
byte_writer->byte_length-=bytes;
|
||||
byte_writer->data_ptr+=bytes;
|
||||
}
|
||||
|
||||
void bytewriter_write_n(bytewriter_t byte_writer, const void *source, size_t bytes);
|
||||
void bytewriter_write_zero_n(bytewriter_t byte_writer, size_t bytes);
|
||||
void bytewriter_write_u8(bytewriter_t byte_writer, uint8_t value);
|
||||
void bytewriter_write_u16_le(bytewriter_t byte_writer, uint16_t value);
|
||||
void bytewriter_write_u16_be(bytewriter_t byte_writer, uint16_t value);
|
||||
void bytewriter_write_u32_le(bytewriter_t byte_writer, uint32_t value);
|
||||
void bytewriter_write_u32_be(bytewriter_t byte_writer, uint32_t value);
|
||||
void bytewriter_write_uuid_be(bytewriter_t byte_writer, GUID guid_value);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
110
Src/replicant/nu/LockFreeFIFO.cpp
Normal file
110
Src/replicant/nu/LockFreeFIFO.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
#include "LockFreeFIFO.h"
|
||||
#include "foundation/align.h"
|
||||
#include "foundation/atomics.h"
|
||||
|
||||
#if 1 //def USE_VISTA_UP_API
|
||||
static int CAS2(FIFO_POINTER *fifo, queue_node_t *tail, size_t icount, queue_node_t *next, size_t icount2)
|
||||
{
|
||||
NALIGN(8) FIFO_POINTER compare = { tail, icount };
|
||||
NALIGN(8) FIFO_POINTER exchange = { next, icount2 };
|
||||
return nx_atomic_cmpxchg2(*(int64_t *)&compare, *(int64_t *)&exchange, (volatile int64_t *)fifo);
|
||||
//return atomic_compare_exchange_doublepointer((volatile intptr2_t *)fifo, *(intptr2_t *)&exchange, *(intptr2_t *)&compare);
|
||||
}
|
||||
#else
|
||||
inline char CAS2 (volatile void * addr, volatile void * v1, volatile long v2, void * n1, long n2)
|
||||
{
|
||||
register char c;
|
||||
__asm {
|
||||
push ebx
|
||||
push ecx
|
||||
push edx
|
||||
push esi
|
||||
mov esi, addr
|
||||
mov eax, v1
|
||||
mov ebx, n1
|
||||
mov ecx, n2
|
||||
mov edx, v2
|
||||
LOCK cmpxchg8b qword ptr [esi]
|
||||
sete c
|
||||
pop esi
|
||||
pop edx
|
||||
pop ecx
|
||||
pop ebx
|
||||
}
|
||||
return c;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int CAS(queue_node_t **fifo, queue_node_t *compare, queue_node_t *exchange)
|
||||
{
|
||||
return nx_atomic_cmpxchg_pointer(compare, exchange, (void* volatile *)fifo);
|
||||
}
|
||||
|
||||
#define ENDFIFO(ff) ((queue_node_t*)0)
|
||||
void fifo_init(fifo_t *fifo)
|
||||
{
|
||||
fifo->dummy.Next = ENDFIFO(fifo); //makes the cell the only cell in the list
|
||||
fifo->head.fifo_node_t = fifo->tail.fifo_node_t = &fifo->dummy;
|
||||
fifo->head.count = fifo->tail.count = 0;
|
||||
fifo->count = 0;
|
||||
}
|
||||
|
||||
void fifo_push(fifo_t *fifo, queue_node_t *cl)
|
||||
{
|
||||
queue_node_t * volatile tail;
|
||||
size_t icount;
|
||||
cl->Next = ENDFIFO(fifo); // // set the cell next pointer to end marker
|
||||
for (;;) // try until enqueue is done
|
||||
{
|
||||
icount = fifo->tail.count; // read the _tail modification count
|
||||
tail = fifo->tail.fifo_node_t; // read the _tail cell
|
||||
if (CAS(&tail->Next, ENDFIFO(fifo), cl)) // try to link the cell to the _tail cell
|
||||
{
|
||||
break; // enqueue is done, exit the loop
|
||||
}
|
||||
else // _tail was not pointing to the last cell, try to set _tail to the next cell
|
||||
{
|
||||
CAS2(&fifo->tail, tail, icount, tail->Next, icount+1);
|
||||
}
|
||||
}
|
||||
CAS2 (&fifo->tail, tail, icount, cl, icount+1); // enqueue is done, try to set _tail to the enqueued cell
|
||||
nx_atomic_inc(&fifo->count);
|
||||
}
|
||||
|
||||
queue_node_t *fifo_pop(fifo_t *fifo)
|
||||
{
|
||||
queue_node_t * volatile head;
|
||||
for (;;)
|
||||
{
|
||||
size_t ocount = fifo->head.count; // read the _head modification count
|
||||
size_t icount = fifo->tail.count; // read the _tail modification count
|
||||
head = fifo->head.fifo_node_t; // read the _head cell
|
||||
queue_node_t *next = head->Next; // read the next cell
|
||||
if (ocount == fifo->head.count) // ensures that next is a valid pointer to avoid failure when reading next value
|
||||
{
|
||||
if (head == fifo->tail.fifo_node_t) // is queue empty or _tail falling behind ?
|
||||
{
|
||||
if (next == ENDFIFO(fifo)) // is queue empty ?
|
||||
{
|
||||
return 0; // queue is empty: return NULL
|
||||
}
|
||||
// _tail is pointing to _head in a non empty queue, try to set _tail to the next cell
|
||||
CAS2(&fifo->tail, head, icount, next, icount+1);
|
||||
}
|
||||
else if (next != ENDFIFO(fifo)) // if we are not competing on the dummy next
|
||||
{
|
||||
if (CAS2(&fifo->head, head, ocount, next, ocount+1)) // try to set _head to the next cell
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
nx_atomic_dec(&fifo->count);
|
||||
if (head == &fifo->dummy)
|
||||
{
|
||||
fifo_push(fifo,head);
|
||||
head = fifo_pop(fifo);
|
||||
}
|
||||
return head;
|
||||
}
|
34
Src/replicant/nu/LockFreeFIFO.h
Normal file
34
Src/replicant/nu/LockFreeFIFO.h
Normal file
@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
#include "queue_node.h"
|
||||
/* Algorithm taken from
|
||||
Lock-Free Techniques for Concurrent Access to Shared Objects
|
||||
Dominique Fober, Yann Orlarey, Stephane Letz
|
||||
http://www.grame.fr/Ressources/pub/LockFree.pdf
|
||||
http://nedko.arnaudov.name/soft/L17_Fober.pdf
|
||||
http://www.grame.fr/Ressources/pub/TR-050523.pdf
|
||||
This implementation (c) 2010 Nullsoft, Inc.
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
struct FIFO_POINTER
|
||||
{
|
||||
queue_node_t *fifo_node_t;
|
||||
size_t count;
|
||||
};
|
||||
|
||||
struct fifo_t
|
||||
{
|
||||
FIFO_POINTER head; // _head pointer and total count of pop operations (ocount)
|
||||
FIFO_POINTER tail; // _tail pointer and total count of push operations (icount)
|
||||
queue_node_t dummy;
|
||||
size_t count;
|
||||
};
|
||||
void fifo_init(fifo_t *fifo);
|
||||
void fifo_push(fifo_t *fifo, queue_node_t *cl);
|
||||
queue_node_t *fifo_pop(fifo_t *fifo);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
35
Src/replicant/nu/LockFreeItem.h
Normal file
35
Src/replicant/nu/LockFreeItem.h
Normal file
@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
#include "foundation/atomics.h"
|
||||
#include "lfitem.h"
|
||||
/* This data structure holds one item, and returns you the old item when you replace it
|
||||
it's sort of a "stack of 1" */
|
||||
|
||||
template <class value_t>
|
||||
class LockFreeItem
|
||||
{
|
||||
public:
|
||||
typedef value_t *ptr_t;
|
||||
LockFreeItem()
|
||||
{
|
||||
lfitem_init(&item);
|
||||
}
|
||||
|
||||
value_t *GetItem()
|
||||
{
|
||||
return (value_t *)lfitem_get(&item);
|
||||
}
|
||||
|
||||
// returns the previous value
|
||||
value_t *SetItem(value_t *new_item)
|
||||
{
|
||||
return (value_t *)lfitem_set(&item, new_item);
|
||||
}
|
||||
|
||||
// if there's already a value, returns what you passed in
|
||||
value_t *SetItemIfZero(value_t *new_item)
|
||||
{
|
||||
return (value_t *)lfitem_set_if_zero(&item, new_item);
|
||||
}
|
||||
|
||||
lfitem_value_t item;
|
||||
};
|
61
Src/replicant/nu/LockFreeLIFO.c
Normal file
61
Src/replicant/nu/LockFreeLIFO.c
Normal file
@ -0,0 +1,61 @@
|
||||
#include "LockFreeLIFO.h"
|
||||
#include "foundation/atomics.h"
|
||||
|
||||
/* TODO: on windows, replace with InitializeSListHead/InterlockedPushEntrySList/InterlockedPopEntrySList just to be safe */
|
||||
void lifo_init(lifo_t *lifo)
|
||||
{
|
||||
lifo->head = 0;
|
||||
}
|
||||
|
||||
void lifo_push(lifo_t *lifo, queue_node_t *cl)
|
||||
{
|
||||
queue_node_t *new_head = cl;
|
||||
queue_node_t *old_head = 0;
|
||||
do
|
||||
{
|
||||
old_head = (queue_node_t *)lifo->head;
|
||||
new_head->Next = old_head;
|
||||
} while (!nx_atomic_cmpxchg_pointer(old_head, new_head, (void * volatile *)&lifo->head));
|
||||
}
|
||||
|
||||
queue_node_t *lifo_pop(lifo_t *lifo)
|
||||
{
|
||||
queue_node_t *new_head = 0, *old_head = 0;
|
||||
do
|
||||
{
|
||||
old_head = (queue_node_t *)lifo->head;
|
||||
if (old_head)
|
||||
new_head = old_head->Next;
|
||||
else
|
||||
new_head = 0;
|
||||
} while (!nx_atomic_cmpxchg_pointer(old_head, new_head, (void * volatile *)&lifo->head));
|
||||
return old_head;
|
||||
}
|
||||
|
||||
queue_node_t *lifo_malloc(size_t bytes)
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
# ifdef __APPLE__
|
||||
void *v = 0;
|
||||
(void) posix_memalign(&v, sizeof(void *), bytes);
|
||||
return v;
|
||||
# else
|
||||
return memalign(bytes, sizeof(void *));
|
||||
# endif
|
||||
#elif defined(_WIN32)
|
||||
return _aligned_malloc(bytes, MEMORY_ALLOCATION_ALIGNMENT);
|
||||
#else
|
||||
#error port me!
|
||||
#endif
|
||||
}
|
||||
|
||||
void lifo_free(queue_node_t *ptr)
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
free(ptr);
|
||||
#elif defined(_WIN32)
|
||||
_aligned_free(ptr);
|
||||
#else
|
||||
#error port me!
|
||||
#endif
|
||||
}
|
18
Src/replicant/nu/LockFreeLIFO.h
Normal file
18
Src/replicant/nu/LockFreeLIFO.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#if defined(__ANDROID__) && defined(__ARM_ARCH_7A__)
|
||||
#include "android-armv7/LockFreeLIFO.h"
|
||||
#elif defined(__ANDROID__)
|
||||
#include "android-arm/LockFreeLIFO.h"
|
||||
#elif defined(_WIN32) && defined(_M_IX86)
|
||||
#include "win-x86/LockFreeLIFO.h"
|
||||
#elif defined(_WIN32) && defined(_M_X64)
|
||||
#include "win-amd64/LockFreeLIFO.h"
|
||||
#elif defined(__APPLE__) && defined(__amd64__)
|
||||
#include "osx-amd64/LockFreeLIFO.h"
|
||||
#elif defined(__APPLE__) && defined(__i386__)
|
||||
#include "osx-x86/LockFreeLIFO.h"
|
||||
#elif defined(__linux__)
|
||||
#include "linux/LockFreeLIFO.h"
|
||||
#else
|
||||
#error port me!
|
||||
#endif
|
421
Src/replicant/nu/LockFreeRingBuffer.cpp
Normal file
421
Src/replicant/nu/LockFreeRingBuffer.cpp
Normal file
@ -0,0 +1,421 @@
|
||||
/*
|
||||
* LockFreeRingBuffer.cpp
|
||||
* Lock-free ring buffer data structure.
|
||||
* One thread can be consumer and one can be producer
|
||||
*
|
||||
* Created by Ben Allison on 11/10/07.
|
||||
* Copyright 2007 Nullsoft, Inc. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "LockFreeRingBuffer.h"
|
||||
#include "foundation/types.h"
|
||||
#include "foundation/atomics.h"
|
||||
#include "foundation/error.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define MIN(a,b) ((a<b)?(a):(b))
|
||||
|
||||
|
||||
LockFreeRingBuffer::LockFreeRingBuffer()
|
||||
{
|
||||
ringBuffer=0;
|
||||
ringBufferSize=0;
|
||||
ringBufferUsed=0;
|
||||
ringWritePosition=0;
|
||||
ringReadPosition=0;
|
||||
}
|
||||
|
||||
LockFreeRingBuffer::~LockFreeRingBuffer()
|
||||
{
|
||||
free(ringBuffer);
|
||||
ringBuffer=0;
|
||||
}
|
||||
|
||||
void LockFreeRingBuffer::Reset()
|
||||
{
|
||||
free(ringBuffer);
|
||||
ringBuffer=0;
|
||||
}
|
||||
|
||||
bool LockFreeRingBuffer::reserve(size_t bytes)
|
||||
{
|
||||
void *new_ring_buffer = realloc(ringBuffer, bytes);
|
||||
if (!new_ring_buffer)
|
||||
return false;
|
||||
|
||||
ringBufferSize=bytes;
|
||||
ringBuffer = (char *)new_ring_buffer;
|
||||
clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
int LockFreeRingBuffer::expand(size_t bytes)
|
||||
{
|
||||
|
||||
if (bytes > ringBufferSize)
|
||||
{
|
||||
char *new_buffer = (char *)realloc(ringBuffer, bytes);
|
||||
if (!new_buffer)
|
||||
return NErr_OutOfMemory;
|
||||
|
||||
size_t write_offset = ringReadPosition-ringBuffer;
|
||||
size_t read_offset = ringWritePosition-ringBuffer;
|
||||
|
||||
/* update write pointer for the new buffer */
|
||||
ringWritePosition = new_buffer + write_offset;
|
||||
|
||||
if (write_offset > read_offset || !ringBufferUsed) /* ringBufferUsed will resolve the ambiguity when ringWritePosition == ringReadPosition */
|
||||
{
|
||||
/* the ring buffer looks like [ RXXXW ], so we don't need to move anything.
|
||||
Just update the read pointer */
|
||||
|
||||
ringReadPosition = new_buffer + write_offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* [XXW RXX] needs to become [XXW RXX] */
|
||||
size_t end_bytes = ringBufferSize-write_offset; // number of bytes that we need to relocate (the RXX portion)
|
||||
char *new_read_pointer = &new_buffer[bytes - end_bytes];
|
||||
memmove(new_read_pointer, ringReadPosition, end_bytes);
|
||||
ringReadPosition = new_read_pointer; /* update read pointer */
|
||||
}
|
||||
ringBufferSize=bytes;
|
||||
ringBuffer = new_buffer;
|
||||
#if defined(__ARM_ARCH_7A__)
|
||||
__asm__ __volatile__ ("dmb" : : : "memory");
|
||||
#endif
|
||||
return NErr_Success;
|
||||
}
|
||||
else
|
||||
return NErr_NoAction;
|
||||
}
|
||||
|
||||
|
||||
bool LockFreeRingBuffer::empty() const
|
||||
{
|
||||
return (ringBufferUsed==0);
|
||||
}
|
||||
|
||||
size_t LockFreeRingBuffer::read(void *dest, size_t len)
|
||||
{
|
||||
int8_t *out = (int8_t *)dest; // lets us do pointer math easier
|
||||
size_t toCopy=ringBufferUsed;
|
||||
if (toCopy > len) toCopy = len;
|
||||
|
||||
size_t copied=0;
|
||||
len-=toCopy;
|
||||
|
||||
// read to the end of the ring buffer
|
||||
size_t end = ringBufferSize-(ringReadPosition-ringBuffer);
|
||||
|
||||
size_t read1 = MIN(end, toCopy);
|
||||
memcpy(out, ringReadPosition, read1);
|
||||
#if defined(__ARM_ARCH_7A__)
|
||||
__asm__ __volatile__ ("dmb" : : : "memory");
|
||||
#endif
|
||||
copied+=read1;
|
||||
ringReadPosition+=read1;
|
||||
if (ringReadPosition == ringBuffer + ringBufferSize)
|
||||
ringReadPosition=ringBuffer;
|
||||
|
||||
// update positions
|
||||
nx_atomic_sub(read1, &ringBufferUsed);
|
||||
toCopy-=read1;
|
||||
out = (int8_t *)out+read1;
|
||||
|
||||
// see if we still have more to read after wrapping around
|
||||
if (toCopy)
|
||||
{
|
||||
memcpy(out, ringReadPosition, toCopy);
|
||||
#if defined(__ARM_ARCH_7A__)
|
||||
__asm__ __volatile__ ("dmb" : : : "memory");
|
||||
#endif
|
||||
copied+=toCopy;
|
||||
ringReadPosition+=toCopy;
|
||||
nx_atomic_sub(toCopy, &ringBufferUsed);
|
||||
if (ringReadPosition == ringBuffer + ringBufferSize)
|
||||
ringReadPosition=ringBuffer;
|
||||
}
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
size_t LockFreeRingBuffer::advance_to(size_t position)
|
||||
{
|
||||
intptr_t bytes_to_flush = (intptr_t)(position - (size_t)ringReadPosition);
|
||||
if (bytes_to_flush < 0)
|
||||
bytes_to_flush += ringBufferSize;
|
||||
return advance(bytes_to_flush);
|
||||
}
|
||||
|
||||
size_t LockFreeRingBuffer::at(size_t offset, void *dest, size_t len) const
|
||||
{
|
||||
size_t toCopy=ringBufferUsed;
|
||||
|
||||
// make a local copy of this so we don't blow the original
|
||||
char *ringReadPosition = this->ringReadPosition;
|
||||
|
||||
/* --- do a "dummy read" to deal with the offset request --- */
|
||||
size_t dummy_end = ringBufferSize-(ringReadPosition-ringBuffer);
|
||||
|
||||
offset = MIN(toCopy, offset);
|
||||
size_t read0 = MIN(dummy_end, offset);
|
||||
ringReadPosition+=read0;
|
||||
if (ringReadPosition == ringBuffer + ringBufferSize)
|
||||
ringReadPosition=ringBuffer;
|
||||
|
||||
// update positions
|
||||
toCopy-=read0;
|
||||
offset-=read0;
|
||||
|
||||
// do second-half read (wraparound)
|
||||
if (offset)
|
||||
{
|
||||
ringReadPosition+=offset;
|
||||
toCopy-=offset;
|
||||
}
|
||||
|
||||
// dummy read done
|
||||
|
||||
/* --- set up destination buffer and copy size --- */
|
||||
int8_t *out = (int8_t *)dest; // lets us do pointer math easier
|
||||
|
||||
if (toCopy > len) toCopy=len;
|
||||
size_t copied=0;
|
||||
|
||||
/* --- read to the end of the ring buffer --- */
|
||||
size_t end = ringBufferSize-(ringReadPosition-ringBuffer);
|
||||
|
||||
size_t read1 = MIN(end, toCopy);
|
||||
memcpy(out, ringReadPosition, read1);
|
||||
copied+=read1;
|
||||
ringReadPosition+=read1;
|
||||
if (ringReadPosition == ringBuffer + ringBufferSize)
|
||||
ringReadPosition=ringBuffer;
|
||||
|
||||
// update positions
|
||||
toCopy-=read1;
|
||||
out = (int8_t *)out+read1;
|
||||
|
||||
/* --- see if we still have more to read after wrapping around --- */
|
||||
if (toCopy)
|
||||
{
|
||||
memcpy(out, ringReadPosition, toCopy);
|
||||
copied+=toCopy;
|
||||
ringReadPosition+=toCopy;
|
||||
}
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
size_t LockFreeRingBuffer::peek(void *dest, size_t len) const
|
||||
{
|
||||
int8_t *out = (int8_t *)dest; // lets us do pointer math easier
|
||||
|
||||
size_t toCopy=ringBufferUsed;
|
||||
|
||||
if (toCopy > len) toCopy=len;
|
||||
size_t copied=0;
|
||||
|
||||
// make a local copy of this so we don't blow the original
|
||||
char *ringReadPosition = this->ringReadPosition;
|
||||
|
||||
// read to the end of the ring buffer
|
||||
size_t end = ringBufferSize-(ringReadPosition-ringBuffer);
|
||||
|
||||
size_t read1 = MIN(end, toCopy);
|
||||
memcpy(out, ringReadPosition, read1);
|
||||
copied+=read1;
|
||||
ringReadPosition+=read1;
|
||||
if (ringReadPosition == ringBuffer + ringBufferSize)
|
||||
ringReadPosition=ringBuffer;
|
||||
|
||||
// update positions
|
||||
toCopy-=read1;
|
||||
out = (int8_t *)out+read1;
|
||||
|
||||
// see if we still have more to read after wrapping around
|
||||
if (toCopy)
|
||||
{
|
||||
memcpy(out, ringReadPosition, toCopy);
|
||||
copied+=toCopy;
|
||||
ringReadPosition+=toCopy;
|
||||
}
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
size_t LockFreeRingBuffer::advance(size_t len)
|
||||
{
|
||||
#if defined(__ARM_ARCH_7A__)
|
||||
__asm__ __volatile__ ("dmb" : : : "memory");
|
||||
#endif
|
||||
size_t toCopy=ringBufferUsed;
|
||||
|
||||
if (toCopy>len) toCopy=len;
|
||||
size_t copied=0;
|
||||
len-=toCopy;
|
||||
|
||||
// read to the end of the ring buffer
|
||||
size_t end = ringBufferSize-(ringReadPosition-ringBuffer);
|
||||
|
||||
size_t read1 = MIN(end, toCopy);
|
||||
copied+=read1;
|
||||
ringReadPosition+=read1;
|
||||
if (ringReadPosition == ringBuffer + ringBufferSize)
|
||||
ringReadPosition=ringBuffer;
|
||||
|
||||
// update positions
|
||||
toCopy-=read1;
|
||||
nx_atomic_sub(read1, &ringBufferUsed);
|
||||
|
||||
// see if we still have more to read after wrapping around
|
||||
if (toCopy)
|
||||
{
|
||||
copied+=toCopy;
|
||||
ringReadPosition+=toCopy;
|
||||
nx_atomic_sub(toCopy, &ringBufferUsed);
|
||||
|
||||
if (ringReadPosition == ringBuffer + ringBufferSize)
|
||||
ringReadPosition=ringBuffer;
|
||||
}
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
size_t LockFreeRingBuffer::avail() const
|
||||
{
|
||||
return ringBufferSize - ringBufferUsed;
|
||||
}
|
||||
|
||||
size_t LockFreeRingBuffer::write(const void *buffer, size_t bytes)
|
||||
{
|
||||
size_t used=ringBufferUsed;
|
||||
|
||||
size_t avail = ringBufferSize - used;
|
||||
bytes = MIN(avail, bytes);
|
||||
|
||||
// write to the end of the ring buffer
|
||||
size_t end = ringBufferSize-(ringWritePosition-ringBuffer);
|
||||
size_t copied=0;
|
||||
size_t write1 = MIN(end, bytes);
|
||||
memcpy(ringWritePosition, buffer, write1);
|
||||
#if defined(__ARM_ARCH_7A__)
|
||||
__asm__ __volatile__ ("dmb" : : : "memory");
|
||||
#endif
|
||||
copied+=write1;
|
||||
ringWritePosition+=write1;
|
||||
if (ringWritePosition == ringBuffer + ringBufferSize)
|
||||
ringWritePosition=ringBuffer;
|
||||
|
||||
|
||||
// update positions
|
||||
nx_atomic_add(write1, &ringBufferUsed);
|
||||
bytes-=write1;
|
||||
buffer = (const int8_t *)buffer+write1;
|
||||
|
||||
// see if we still have more to write after wrapping around
|
||||
if (bytes)
|
||||
{
|
||||
memcpy(ringWritePosition, buffer, bytes);
|
||||
#if defined(__ARM_ARCH_7A__)
|
||||
__asm__ __volatile__ ("dmb" : : : "memory");
|
||||
#endif
|
||||
copied+=bytes;
|
||||
ringWritePosition+=bytes;
|
||||
nx_atomic_add(bytes, &ringBufferUsed);
|
||||
if (ringWritePosition == ringBuffer + ringBufferSize)
|
||||
ringWritePosition=ringBuffer;
|
||||
}
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
size_t LockFreeRingBuffer::update(size_t bytes)
|
||||
{
|
||||
size_t used=ringBufferUsed;
|
||||
|
||||
size_t avail = ringBufferSize - used;
|
||||
bytes = MIN(avail, bytes);
|
||||
|
||||
// write to the end of the ring buffer
|
||||
size_t end = ringBufferSize-(ringWritePosition-ringBuffer);
|
||||
size_t copied=0;
|
||||
size_t write1 = MIN(end, bytes);
|
||||
#if defined(__ARM_ARCH_7A__)
|
||||
__asm__ __volatile__ ("dmb" : : : "memory");
|
||||
#endif
|
||||
copied+=write1;
|
||||
ringWritePosition+=write1;
|
||||
if (ringWritePosition == ringBuffer + ringBufferSize)
|
||||
ringWritePosition=ringBuffer;
|
||||
|
||||
// update positions
|
||||
nx_atomic_add(write1, &ringBufferUsed);
|
||||
bytes-=write1;
|
||||
|
||||
// see if we still have more to write after wrapping around
|
||||
if (bytes)
|
||||
{
|
||||
/* no need for memory barrier here, we havn't written anything in the interim */
|
||||
copied+=bytes;
|
||||
ringWritePosition+=bytes;
|
||||
nx_atomic_add(bytes, &ringBufferUsed);
|
||||
if (ringWritePosition == ringBuffer + ringBufferSize)
|
||||
ringWritePosition=ringBuffer;
|
||||
}
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
|
||||
void LockFreeRingBuffer::get_write_buffer(size_t bytes, void **buffer, size_t *bytes_available)
|
||||
{
|
||||
size_t used=ringBufferUsed;
|
||||
|
||||
size_t avail = ringBufferSize - used;
|
||||
bytes = MIN(avail, bytes);
|
||||
|
||||
// can only write to the end of the ring buffer
|
||||
size_t end = ringBufferSize-(ringWritePosition-ringBuffer);
|
||||
*bytes_available = MIN(end, bytes);
|
||||
*buffer = ringWritePosition;
|
||||
}
|
||||
|
||||
void LockFreeRingBuffer::get_read_buffer(size_t bytes, const void **buffer, size_t *bytes_available)
|
||||
{
|
||||
size_t toCopy=ringBufferUsed;
|
||||
|
||||
if (toCopy > bytes) toCopy=bytes;
|
||||
|
||||
// read to the end of the ring buffer
|
||||
size_t end = ringBufferSize-(ringReadPosition-ringBuffer);
|
||||
|
||||
*bytes_available = MIN(end, toCopy);
|
||||
*buffer = ringReadPosition;
|
||||
}
|
||||
|
||||
size_t LockFreeRingBuffer::size() const
|
||||
{
|
||||
return ringBufferUsed;
|
||||
}
|
||||
|
||||
void LockFreeRingBuffer::clear()
|
||||
{
|
||||
nx_atomic_write(0, &ringBufferUsed);
|
||||
ringWritePosition=ringBuffer;
|
||||
ringReadPosition=ringBuffer;
|
||||
}
|
||||
|
||||
size_t LockFreeRingBuffer::write_position() const
|
||||
{
|
||||
return (size_t)ringWritePosition;
|
||||
}
|
||||
|
||||
size_t LockFreeRingBuffer::read_position() const
|
||||
{
|
||||
return (size_t)ringReadPosition;
|
||||
}
|
52
Src/replicant/nu/LockFreeRingBuffer.h
Normal file
52
Src/replicant/nu/LockFreeRingBuffer.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Created by Ben Allison on 11/10/07.
|
||||
* Copyright 2007-2011 Nullsoft, Inc. All rights reserved.
|
||||
*
|
||||
* Ring Buffer class
|
||||
* Thread safety:
|
||||
* This class can be used from exactly two simultaneous threads without locking
|
||||
* as long as one thread only writes and the other thread only reads
|
||||
* the writer thread may call empty(), avail(), size(), write(), fill(
|
||||
* the reader thread my call empty(), avail(), size(), read(), peek(), advance()
|
||||
*
|
||||
* two (or more) readers or two (or more) writers requires external locking
|
||||
*
|
||||
* Reset(), reserve(), clear(), LockBuffer(), UnlockBuffer() are not thread-safe
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stddef.h>
|
||||
#include "foundation/types.h"
|
||||
|
||||
class LockFreeRingBuffer
|
||||
{
|
||||
public:
|
||||
LockFreeRingBuffer();
|
||||
~LockFreeRingBuffer();
|
||||
void Reset();
|
||||
int expand(size_t bytes); // like reserve, but only expands upward. non-destructive. returns an NError
|
||||
bool reserve(size_t bytes);
|
||||
bool empty() const;
|
||||
size_t avail() const; // how much available for writing
|
||||
size_t size() const; // how much available for reading
|
||||
void clear();
|
||||
size_t read(void *dest, size_t len); // returns amount actually read
|
||||
size_t advance(size_t len); // same as read() but doesn't write the data any where.
|
||||
size_t peek(void *dest, size_t len) const; // same as read() but doesn't advance the read pointer
|
||||
size_t write(const void *src, size_t len);
|
||||
size_t update(size_t len); // same as write() but doesn't write the data, usually used after a get_buffer call
|
||||
size_t at(size_t offset, void *dest, size_t len) const; // peeks() from offset. returns bytes read
|
||||
size_t write_position() const; // returns an integer representing a write position
|
||||
size_t read_position() const; // returns an integer representing the read position
|
||||
size_t advance_to(size_t position); // moves the read pointer to a position previously returned from write_position(). returns bytes advanced
|
||||
void get_write_buffer(size_t bytes, void **buffer, size_t *bytes_available); /* returns a pointer that you can write data to, you MUST call update() when you are done */
|
||||
void get_read_buffer(size_t bytes, const void **buffer, size_t *bytes_available); /* returns a pointer that you can read data from, call advance() when you are done */
|
||||
|
||||
|
||||
private:
|
||||
volatile size_t ringBufferUsed;
|
||||
size_t ringBufferSize;
|
||||
char *ringBuffer;
|
||||
char *ringWritePosition;
|
||||
char *ringReadPosition;
|
||||
};
|
16
Src/replicant/nu/MessageLoop.h
Normal file
16
Src/replicant/nu/MessageLoop.h
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#if defined(__ANDROID__) && defined(__ARM_ARCH_7A__)
|
||||
#include "android-armv7/MessageLoop.h"
|
||||
#elif defined(__ANDROID__) && (defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5TE__))
|
||||
#include "android-arm/MessageLoop.h"
|
||||
#elif defined(__ANDROID__) && defined(__i386__)
|
||||
#include "android-x86/MessageLoop.h"
|
||||
#elif defined(_WIN32)
|
||||
#include "win/MessageLoop.h"
|
||||
#elif defined(__linux__)
|
||||
#include "linux/MessageLoop.h"
|
||||
#elif defined(__APPLE__)
|
||||
#include "osx/MessageLoop.h"
|
||||
#else
|
||||
#error port me!
|
||||
#endif
|
15
Src/replicant/nu/Pairs.h
Normal file
15
Src/replicant/nu/Pairs.h
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
namespace nu
|
||||
{
|
||||
|
||||
template <class A, class B>
|
||||
class Pair
|
||||
{
|
||||
public:
|
||||
Pair() {}
|
||||
Pair(const A &_a, const B &_b) : first(_a), second(_b) {}
|
||||
|
||||
A first;
|
||||
B second;
|
||||
};
|
||||
}
|
117
Src/replicant/nu/ProgressTracker.cpp
Normal file
117
Src/replicant/nu/ProgressTracker.cpp
Normal file
@ -0,0 +1,117 @@
|
||||
#include "ProgressTracker.h"
|
||||
#include <stdio.h>
|
||||
/* Helper class for managing valid chunks in non-sequential scenarios
|
||||
e.g. progressive downloading */
|
||||
|
||||
ProgressTracker::ProgressTracker()
|
||||
{
|
||||
current_position=0;
|
||||
current_chunk=0;
|
||||
chunks[0]=0;
|
||||
chunks[null_position]=null_position;
|
||||
};
|
||||
|
||||
void ProgressTracker::Write(uint64_t bytes_written)
|
||||
{
|
||||
nu::AutoLock list_lock(list_guard);
|
||||
ChunkList::iterator next, itr = chunks.find(current_chunk);
|
||||
current_position += bytes_written;
|
||||
if (itr->second < current_position)
|
||||
itr->second = current_position;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
next = itr;
|
||||
next++;
|
||||
if (next != chunks.end() && (next->first <= itr->second))
|
||||
{
|
||||
itr->second = next->second;
|
||||
chunks.erase(next);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool ProgressTracker::Valid(uint64_t requested_position, uint64_t requested_end, uint64_t *available)
|
||||
{
|
||||
nu::AutoLock list_lock(list_guard);
|
||||
for (ChunkList::iterator itr=chunks.begin();itr!=chunks.end();itr++)
|
||||
{
|
||||
if (requested_position >= itr->first)
|
||||
{
|
||||
if (requested_position < itr->second)
|
||||
{
|
||||
if (available)
|
||||
*available = itr->second - requested_position;
|
||||
|
||||
if (requested_end <= itr->second)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
if (available)
|
||||
*available = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ProgressTracker::Seek(uint64_t requested_position, uint64_t requested_end, uint64_t *new_start, uint64_t *new_end)
|
||||
{
|
||||
nu::AutoLock list_lock(list_guard);
|
||||
uint64_t last_good_start=0;
|
||||
ChunkList::iterator itr;
|
||||
for (itr=chunks.begin();itr!=chunks.end();itr++)
|
||||
{
|
||||
if (requested_position >= itr->first)
|
||||
{
|
||||
current_chunk = itr->first;
|
||||
if (requested_position <= itr->second)
|
||||
{
|
||||
ChunkList::iterator next = itr;
|
||||
next++;
|
||||
*new_end = next->first;
|
||||
|
||||
*new_start = current_position = itr->second;
|
||||
|
||||
if (requested_end <= itr->second)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
last_good_start = itr->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (last_good_start > requested_position)
|
||||
*new_start = current_position = last_good_start;
|
||||
else
|
||||
{
|
||||
|
||||
*new_start = current_chunk = current_position = requested_position;
|
||||
chunks[current_chunk] = current_chunk;
|
||||
}
|
||||
*new_end = null_position;
|
||||
return false;
|
||||
}
|
||||
|
||||
void ProgressTracker::Dump()
|
||||
{
|
||||
ChunkList::iterator itr;
|
||||
for (itr=chunks.begin();itr!=chunks.end();itr++)
|
||||
{
|
||||
printf("%llu - %llu\n", itr->first, itr->second);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
const uint64_t ProgressTracker::null_position = (uint64_t)-1;
|
24
Src/replicant/nu/ProgressTracker.h
Normal file
24
Src/replicant/nu/ProgressTracker.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include "foundation/types.h"
|
||||
#include <map>
|
||||
#include "nu/AutoLock.h"
|
||||
|
||||
/* Helper class for managing valid chunks in non-sequential scenarios
|
||||
e.g. progressive downloading */
|
||||
|
||||
class ProgressTracker
|
||||
{
|
||||
public:
|
||||
static const uint64_t null_position;
|
||||
ProgressTracker();
|
||||
void Write(uint64_t bytes_written);
|
||||
bool Valid(uint64_t requested_position, uint64_t requested_end, uint64_t *available=0);
|
||||
bool Seek(uint64_t requested_position, uint64_t requested_end, uint64_t *new_start, uint64_t *new_end);
|
||||
void Dump();
|
||||
|
||||
typedef std::map<uint64_t, uint64_t> ChunkList;
|
||||
ChunkList chunks;
|
||||
uint64_t current_chunk;
|
||||
uint64_t current_position;
|
||||
nu::LockGuard list_guard;
|
||||
};
|
464
Src/replicant/nu/RingBuffer.cpp
Normal file
464
Src/replicant/nu/RingBuffer.cpp
Normal file
@ -0,0 +1,464 @@
|
||||
/*
|
||||
* RingBuffer.cpp
|
||||
* Lock-free ring buffer data structure.
|
||||
* One thread can be consumer and one can be producer
|
||||
*
|
||||
* Created by Ben Allison on 11/10/07.
|
||||
* Copyright 2007 Nullsoft, Inc. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "RingBuffer.h"
|
||||
#include "foundation/error.h"
|
||||
#include "foundation/types.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define MIN(a,b) ((a<b)?(a):(b))
|
||||
|
||||
|
||||
RingBuffer::RingBuffer()
|
||||
{
|
||||
ringBuffer = 0;
|
||||
ringBufferSize = 0;
|
||||
ringBufferUsed = 0;
|
||||
ringWritePosition = 0;
|
||||
ringReadPosition = 0;
|
||||
}
|
||||
|
||||
RingBuffer::~RingBuffer()
|
||||
{
|
||||
if ( ringBuffer )
|
||||
free( ringBuffer );
|
||||
|
||||
ringBuffer = 0;
|
||||
}
|
||||
|
||||
void RingBuffer::Reset()
|
||||
{
|
||||
if ( ringBuffer )
|
||||
free( ringBuffer );
|
||||
|
||||
ringBuffer = 0;
|
||||
}
|
||||
|
||||
bool RingBuffer::reserve( size_t bytes )
|
||||
{
|
||||
Reset();
|
||||
|
||||
ringBufferSize = bytes;
|
||||
|
||||
ringBuffer = (char *)calloc( ringBufferSize, sizeof( char ) );
|
||||
if ( !ringBuffer )
|
||||
return false;
|
||||
|
||||
clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int RingBuffer::expand( size_t bytes )
|
||||
{
|
||||
if ( bytes > ringBufferSize )
|
||||
{
|
||||
char *new_buffer = (char *)realloc( ringBuffer, bytes );
|
||||
if ( !new_buffer )
|
||||
return NErr_OutOfMemory;
|
||||
|
||||
size_t write_offset = ringReadPosition - ringBuffer;
|
||||
size_t read_offset = ringWritePosition - ringBuffer;
|
||||
|
||||
/* update write pointer for the new buffer */
|
||||
ringWritePosition = new_buffer + write_offset;
|
||||
|
||||
if ( write_offset > read_offset || !ringBufferUsed ) /* ringBufferUsed will resolve the ambiguity when ringWritePosition == ringReadPosition */
|
||||
{
|
||||
/* the ring buffer looks like [ RXXXW ], so we don't need to move anything.
|
||||
Just update the read pointer */
|
||||
|
||||
ringReadPosition = new_buffer + write_offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* [XXW RXX] needs to become [XXW RXX] */
|
||||
size_t end_bytes = ringBufferSize - read_offset; // number of bytes that we need to relocate (the RXX portion)
|
||||
char *new_read_pointer = &new_buffer[ bytes - end_bytes ];
|
||||
|
||||
memmove( new_read_pointer, ringReadPosition, end_bytes );
|
||||
|
||||
ringReadPosition = new_read_pointer; /* update read pointer */
|
||||
}
|
||||
|
||||
ringBufferSize = bytes;
|
||||
ringBuffer = new_buffer;
|
||||
|
||||
return NErr_Success;
|
||||
}
|
||||
else
|
||||
return NErr_NoAction;
|
||||
}
|
||||
|
||||
bool RingBuffer::empty() const
|
||||
{
|
||||
return ( ringBufferUsed == 0 );
|
||||
}
|
||||
|
||||
size_t RingBuffer::read( void *dest, size_t len )
|
||||
{
|
||||
int8_t *out = (int8_t *)dest; // lets us do pointer math easier
|
||||
size_t toCopy = MIN( ringBufferUsed, len );
|
||||
size_t copied = 0;
|
||||
|
||||
len -= toCopy;
|
||||
|
||||
// read to the end of the ring buffer
|
||||
size_t end = ringBufferSize - ( ringReadPosition - ringBuffer );
|
||||
size_t read1 = MIN( end, toCopy );
|
||||
|
||||
memcpy( out, ringReadPosition, read1 );
|
||||
|
||||
copied += read1;
|
||||
ringReadPosition += read1;
|
||||
|
||||
if ( ringReadPosition == ringBuffer + ringBufferSize )
|
||||
ringReadPosition = ringBuffer;
|
||||
|
||||
// update positions
|
||||
ringBufferUsed -= read1;
|
||||
toCopy -= read1;
|
||||
out = (int8_t *)out + read1;
|
||||
|
||||
// see if we still have more to read after wrapping around
|
||||
if ( toCopy )
|
||||
{
|
||||
memcpy( out, ringReadPosition, toCopy );
|
||||
|
||||
copied += toCopy;
|
||||
ringReadPosition += toCopy;
|
||||
ringBufferUsed -= toCopy;
|
||||
|
||||
if ( ringReadPosition == ringBuffer + ringBufferSize )
|
||||
ringReadPosition = ringBuffer;
|
||||
}
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
size_t RingBuffer::at( size_t offset, void *dest, size_t len ) const
|
||||
{
|
||||
size_t toCopy = ringBufferUsed;
|
||||
|
||||
// make a local copy of this so we don't blow the original
|
||||
char *ringReadPosition = this->ringReadPosition;
|
||||
|
||||
/* --- do a "dummy read" to deal with the offset request --- */
|
||||
size_t dummy_end = ringBufferSize - ( ringReadPosition - ringBuffer );
|
||||
|
||||
offset = MIN( toCopy, offset );
|
||||
size_t read0 = MIN( dummy_end, offset );
|
||||
ringReadPosition += read0;
|
||||
|
||||
if ( ringReadPosition == ringBuffer + ringBufferSize )
|
||||
ringReadPosition = ringBuffer;
|
||||
|
||||
// update positions
|
||||
toCopy -= read0;
|
||||
offset -= read0;
|
||||
|
||||
// do second-half read (wraparound)
|
||||
if ( offset )
|
||||
{
|
||||
ringReadPosition += offset;
|
||||
toCopy -= offset;
|
||||
}
|
||||
|
||||
// dummy read done
|
||||
|
||||
/* --- set up destination buffer and copy size --- */
|
||||
int8_t *out = (int8_t *)dest; // lets us do pointer math easier
|
||||
|
||||
if ( toCopy > len )
|
||||
toCopy = len;
|
||||
|
||||
size_t copied = 0;
|
||||
|
||||
/* --- read to the end of the ring buffer --- */
|
||||
size_t end = ringBufferSize - ( ringReadPosition - ringBuffer );
|
||||
size_t read1 = MIN( end, toCopy );
|
||||
|
||||
memcpy( out, ringReadPosition, read1 );
|
||||
|
||||
copied += read1;
|
||||
ringReadPosition += read1;
|
||||
|
||||
if ( ringReadPosition == ringBuffer + ringBufferSize )
|
||||
ringReadPosition = ringBuffer;
|
||||
|
||||
// update positions
|
||||
toCopy -= read1;
|
||||
out = (int8_t *)out + read1;
|
||||
|
||||
/* --- see if we still have more to read after wrapping around --- */
|
||||
if ( toCopy )
|
||||
{
|
||||
memcpy( out, ringReadPosition, toCopy );
|
||||
|
||||
copied += toCopy;
|
||||
ringReadPosition += toCopy;
|
||||
}
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
size_t RingBuffer::peek( void *dest, size_t len ) const
|
||||
{
|
||||
int8_t *out = (int8_t *)dest; // lets us do pointer math easier
|
||||
|
||||
size_t toCopy = MIN( ringBufferUsed, len );
|
||||
size_t copied = 0;
|
||||
|
||||
// make a local copy of this so we don't blow the original
|
||||
char *ringReadPosition = this->ringReadPosition;
|
||||
|
||||
// read to the end of the ring buffer
|
||||
size_t end = ringBufferSize - ( ringReadPosition - ringBuffer );
|
||||
size_t read1 = MIN( end, toCopy );
|
||||
|
||||
memcpy( out, ringReadPosition, read1 );
|
||||
|
||||
copied += read1;
|
||||
ringReadPosition += read1;
|
||||
|
||||
if ( ringReadPosition == ringBuffer + ringBufferSize )
|
||||
ringReadPosition = ringBuffer;
|
||||
|
||||
// update positions
|
||||
toCopy -= read1;
|
||||
out = (int8_t *)out + read1;
|
||||
|
||||
// see if we still have more to read after wrapping around
|
||||
if ( toCopy )
|
||||
{
|
||||
memcpy( out, ringReadPosition, toCopy );
|
||||
|
||||
copied += toCopy;
|
||||
ringReadPosition += toCopy;
|
||||
}
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
size_t RingBuffer::advance( size_t len )
|
||||
{
|
||||
size_t toCopy = MIN( ringBufferUsed, len );
|
||||
size_t copied = 0;
|
||||
|
||||
len -= toCopy;
|
||||
|
||||
// read to the end of the ring buffer
|
||||
size_t end = ringBufferSize - ( ringReadPosition - ringBuffer );
|
||||
size_t read1 = MIN( end, toCopy );
|
||||
|
||||
copied += read1;
|
||||
ringReadPosition += read1;
|
||||
|
||||
if ( ringReadPosition == ringBuffer + ringBufferSize )
|
||||
ringReadPosition = ringBuffer;
|
||||
|
||||
// update positions
|
||||
toCopy -= read1;
|
||||
ringBufferUsed -= read1;
|
||||
|
||||
// see if we still have more to read after wrapping around
|
||||
if ( toCopy )
|
||||
{
|
||||
copied += toCopy;
|
||||
ringReadPosition += toCopy;
|
||||
ringBufferUsed -= toCopy;
|
||||
|
||||
if ( ringReadPosition == ringBuffer + ringBufferSize )
|
||||
ringReadPosition = ringBuffer;
|
||||
}
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
size_t RingBuffer::avail() const
|
||||
{
|
||||
return ringBufferSize - ringBufferUsed;
|
||||
}
|
||||
|
||||
size_t RingBuffer::write( const void *buffer, size_t bytes )
|
||||
{
|
||||
size_t used = ringBufferUsed;
|
||||
size_t avail = ringBufferSize - used;
|
||||
|
||||
bytes = MIN( avail, bytes );
|
||||
|
||||
// write to the end of the ring buffer
|
||||
size_t end = ringBufferSize - ( ringWritePosition - ringBuffer );
|
||||
size_t copied = 0;
|
||||
size_t write1 = MIN( end, bytes );
|
||||
|
||||
memcpy( ringWritePosition, buffer, write1 );
|
||||
|
||||
copied += write1;
|
||||
ringWritePosition += write1;
|
||||
|
||||
if ( ringWritePosition == ringBuffer + ringBufferSize )
|
||||
ringWritePosition = ringBuffer;
|
||||
|
||||
// update positions
|
||||
ringBufferUsed += write1;
|
||||
bytes -= write1;
|
||||
buffer = (const int8_t *)buffer + write1;
|
||||
|
||||
// see if we still have more to write after wrapping around
|
||||
if ( bytes )
|
||||
{
|
||||
memcpy( ringWritePosition, buffer, bytes );
|
||||
|
||||
copied += bytes;
|
||||
ringWritePosition += bytes;
|
||||
ringBufferUsed += bytes;
|
||||
|
||||
if ( ringWritePosition == ringBuffer + ringBufferSize )
|
||||
ringWritePosition = ringBuffer;
|
||||
}
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
size_t RingBuffer::drain( Drainer *drainer, size_t max_bytes )
|
||||
{
|
||||
// read to the end of the ring buffer
|
||||
size_t used = ringBufferUsed;
|
||||
size_t bytes = used;
|
||||
|
||||
bytes = MIN( bytes, max_bytes );
|
||||
|
||||
size_t copied = 0;
|
||||
size_t end = ringBufferSize - ( ringReadPosition - ringBuffer );
|
||||
size_t drain1 = MIN( end, bytes );
|
||||
|
||||
if ( !drain1 )
|
||||
return 0;
|
||||
|
||||
size_t read1 = drainer->Write( ringReadPosition, drain1 );
|
||||
if ( read1 == 0 )
|
||||
return 0;
|
||||
|
||||
copied += read1;
|
||||
ringReadPosition += read1;
|
||||
if ( ringReadPosition == ringBuffer + ringBufferSize )
|
||||
ringReadPosition = ringBuffer;
|
||||
|
||||
// update positions
|
||||
ringBufferUsed -= read1;
|
||||
bytes -= read1;
|
||||
|
||||
// see if we still have more to read after wrapping around
|
||||
if ( drain1 == read1 && bytes )
|
||||
{
|
||||
size_t read2 = drainer->Write( ringReadPosition, bytes );
|
||||
|
||||
copied += read2;
|
||||
ringReadPosition += read2;
|
||||
ringBufferUsed -= read2;
|
||||
|
||||
if ( ringReadPosition == ringBuffer + ringBufferSize )
|
||||
ringReadPosition = ringBuffer;
|
||||
}
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
size_t RingBuffer::fill( Filler *filler, size_t max_bytes )
|
||||
{
|
||||
// write to the end of the ring buffer
|
||||
size_t used = ringBufferUsed;
|
||||
size_t bytes = ringBufferSize - used;
|
||||
|
||||
bytes = MIN( bytes, max_bytes );
|
||||
|
||||
size_t copied = 0;
|
||||
size_t end = ringBufferSize - ( ringWritePosition - ringBuffer );
|
||||
size_t fill1 = MIN( end, bytes );
|
||||
|
||||
if ( !fill1 )
|
||||
return 0;
|
||||
|
||||
size_t write1 = filler->Read( ringWritePosition, fill1 );
|
||||
if ( write1 == 0 )
|
||||
return 0;
|
||||
|
||||
copied += write1;
|
||||
ringWritePosition += write1;
|
||||
|
||||
if ( ringWritePosition == ringBuffer + ringBufferSize )
|
||||
ringWritePosition = ringBuffer;
|
||||
|
||||
// update positions
|
||||
ringBufferUsed += write1;
|
||||
bytes -= write1;
|
||||
|
||||
// see if we still have more to write after wrapping around
|
||||
if ( fill1 == write1 && bytes )
|
||||
{
|
||||
size_t write2 = filler->Read( ringWritePosition, bytes );
|
||||
|
||||
copied += write2;
|
||||
ringWritePosition += write2;
|
||||
ringBufferUsed += write2;
|
||||
|
||||
if ( ringWritePosition == ringBuffer + ringBufferSize )
|
||||
ringWritePosition = ringBuffer;
|
||||
}
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
size_t RingBuffer::size() const
|
||||
{
|
||||
return ringBufferUsed;
|
||||
}
|
||||
|
||||
void RingBuffer::clear()
|
||||
{
|
||||
ringBufferUsed = 0;
|
||||
ringWritePosition = ringBuffer;
|
||||
ringReadPosition = ringBuffer;
|
||||
}
|
||||
|
||||
void *RingBuffer::LockBuffer()
|
||||
{
|
||||
return ringBuffer;
|
||||
}
|
||||
|
||||
void RingBuffer::UnlockBuffer( size_t written )
|
||||
{
|
||||
ringWritePosition = ringBuffer + written;
|
||||
ringBufferUsed = written;
|
||||
}
|
||||
|
||||
size_t RingBuffer::write_position() const
|
||||
{
|
||||
return (size_t)ringWritePosition;
|
||||
}
|
||||
|
||||
size_t RingBuffer::read_position() const
|
||||
{
|
||||
return (size_t)ringReadPosition;
|
||||
}
|
||||
|
||||
void RingBuffer::get_read_buffer( size_t bytes, const void **buffer, size_t *bytes_available ) const
|
||||
{
|
||||
size_t toCopy = MIN( ringBufferUsed, bytes );
|
||||
|
||||
// read to the end of the ring buffer
|
||||
size_t end = ringBufferSize - ( ringReadPosition - ringBuffer );
|
||||
|
||||
*bytes_available = MIN( end, toCopy );
|
||||
*buffer = ringReadPosition;
|
||||
}
|
61
Src/replicant/nu/RingBuffer.h
Normal file
61
Src/replicant/nu/RingBuffer.h
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Not Thread Safe !
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stddef.h>
|
||||
|
||||
class Filler
|
||||
{
|
||||
public:
|
||||
virtual size_t Read(void *dest, size_t len)=0;
|
||||
};
|
||||
|
||||
class Drainer
|
||||
{
|
||||
public:
|
||||
virtual size_t Write(const void *dest, size_t len)=0;
|
||||
};
|
||||
|
||||
class RingBuffer
|
||||
{
|
||||
public:
|
||||
RingBuffer();
|
||||
~RingBuffer();
|
||||
|
||||
void Reset();
|
||||
bool reserve( size_t bytes ); // destructive.
|
||||
int expand( size_t bytes ); // like reserve, but only expands upward. non-destructive. returns an NError
|
||||
bool empty() const;
|
||||
size_t avail() const; // how much available for writing
|
||||
size_t size() const; // how much available for reading
|
||||
void clear();
|
||||
size_t read( void *dest, size_t len ); // returns amount actually read
|
||||
size_t advance( size_t len ); // same as read() but doesn't write the data any where.
|
||||
size_t peek( void *dest, size_t len ) const; // same as read() but doesn't advance the read pointer
|
||||
size_t write( const void *src, size_t len );
|
||||
size_t fill( Filler *filler, size_t max_bytes );
|
||||
size_t drain( Drainer *drainer, size_t max_bytes );
|
||||
size_t at( size_t offset, void *dest, size_t len ) const; // peeks() from offset. returns bytes read
|
||||
|
||||
size_t write_position() const; // returns an integer representing a write position
|
||||
size_t read_position() const; // returns an integer representing the read position
|
||||
|
||||
void get_read_buffer( size_t bytes, const void **buffer, size_t *bytes_available ) const; /* returns a pointer that you can read data from, call advance() when you are done */
|
||||
/* DO NOT USING THIS UNLESS YOU KNOW WHAT YOU'RE DOING
|
||||
you should only use it when the ring buffer is empty
|
||||
1) call clear() beforehand - very important!
|
||||
2) call LockBuffer(), it'll give you a buffer
|
||||
3) call UnlockBufer() with how much you've written
|
||||
4) you catch the man
|
||||
*/
|
||||
void *LockBuffer();
|
||||
void UnlockBuffer( size_t written );
|
||||
|
||||
private:
|
||||
volatile size_t ringBufferUsed;
|
||||
size_t ringBufferSize;
|
||||
char *ringBuffer;
|
||||
char *ringWritePosition;
|
||||
char *ringReadPosition;
|
||||
};
|
51
Src/replicant/nu/SafeSize.h
Normal file
51
Src/replicant/nu/SafeSize.h
Normal file
@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
class SafeSize
|
||||
{
|
||||
public:
|
||||
SafeSize()
|
||||
{
|
||||
value = 0;
|
||||
overflow = false;
|
||||
}
|
||||
|
||||
void Add(size_t add)
|
||||
{
|
||||
if (!overflow)
|
||||
{
|
||||
value += add;
|
||||
if (value < add)
|
||||
overflow=true;
|
||||
}
|
||||
}
|
||||
|
||||
void AddN(size_t size, size_t count)
|
||||
{
|
||||
if (!overflow)
|
||||
{
|
||||
size_t total = size * count;
|
||||
if (total < size)
|
||||
{
|
||||
overflow = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Add(total);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Overflowed() const
|
||||
{
|
||||
return overflow;
|
||||
}
|
||||
|
||||
operator size_t ()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t value;
|
||||
bool overflow;
|
||||
};
|
16
Src/replicant/nu/ThreadLoop.h
Normal file
16
Src/replicant/nu/ThreadLoop.h
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#if defined(__ANDROID__) && defined(__ARM_ARCH_7A__)
|
||||
#include "android-armv7/ThreadLoop.h"
|
||||
#elif defined(__ANDROID__) && (defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5TE__))
|
||||
#include "android-arm/ThreadLoop.h"
|
||||
#elif defined(__ANDROID__) && defined(__i386__)
|
||||
#include "android-x86/ThreadLoop.h"
|
||||
#elif defined(_WIN32)
|
||||
#include "win/ThreadLoop.h"
|
||||
#elif defined(__linux__)
|
||||
#include "linux/ThreadLoop.h"
|
||||
#elif defined(__APPLE__)
|
||||
#include "osx/ThreadLoop.h"
|
||||
#else
|
||||
#error port me!
|
||||
#endif
|
25
Src/replicant/nu/lfitem.c
Normal file
25
Src/replicant/nu/lfitem.c
Normal file
@ -0,0 +1,25 @@
|
||||
#include "nu/lfitem.h"
|
||||
#include "foundation/atomics.h"
|
||||
|
||||
void lfitem_init(lfitem_t item)
|
||||
{
|
||||
item->item=0;
|
||||
}
|
||||
|
||||
void *lfitem_get(lfitem_t item)
|
||||
{
|
||||
return nx_atomic_swap_pointer(0, &(item->item));
|
||||
}
|
||||
|
||||
void *lfitem_set(lfitem_t item, const void *value)
|
||||
{
|
||||
return nx_atomic_swap_pointer((void *)value, &(item->item));
|
||||
}
|
||||
|
||||
void *lfitem_set_if_zero(lfitem_t item, void *value)
|
||||
{
|
||||
if (nx_atomic_cmpxchg_pointer(0, value, &(item->item)) == 0)
|
||||
return 0;
|
||||
else
|
||||
return (void *)value;
|
||||
}
|
23
Src/replicant/nu/lfitem.h
Normal file
23
Src/replicant/nu/lfitem.h
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
/* This data structure holds one item, and returns you the old item when you replace it
|
||||
it's sort of a "stack of 1" */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct lfitem_struct_t
|
||||
{
|
||||
void * volatile item;
|
||||
} lfitem_value_t, *lfitem_t;
|
||||
|
||||
|
||||
void lfitem_init(lfitem_t item);
|
||||
void *lfitem_get(lfitem_t item);
|
||||
void *lfitem_set(lfitem_t item, const void *value);
|
||||
void *lfitem_set_if_zero(lfitem_t item, void *value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
50
Src/replicant/nu/lfmpscq.c
Normal file
50
Src/replicant/nu/lfmpscq.c
Normal file
@ -0,0 +1,50 @@
|
||||
#include "lfmpscq.h"
|
||||
#include "foundation/atomics.h"
|
||||
|
||||
void mpscq_init(mpscq_t* self)
|
||||
{
|
||||
self->head = &self->stub;
|
||||
self->tail = &self->stub;
|
||||
self->stub.Next = 0;
|
||||
}
|
||||
|
||||
int mpscq_push(mpscq_t *self, queue_node_t *n)
|
||||
{
|
||||
queue_node_t *prev;
|
||||
n->Next = 0;
|
||||
prev = nx_atomic_swap_pointer(n, (void * volatile *)&self->head);
|
||||
//(*)
|
||||
prev->Next = n;
|
||||
return prev!=&self->stub;
|
||||
}
|
||||
|
||||
queue_node_t *mpscq_pop(mpscq_t *self)
|
||||
{
|
||||
queue_node_t* tail = self->tail;
|
||||
queue_node_t* next = tail->Next;
|
||||
queue_node_t* head;
|
||||
if (tail == &self->stub)
|
||||
{
|
||||
if (0 == next)
|
||||
return 0;
|
||||
self->tail = next;
|
||||
tail = next;
|
||||
next = next->Next;
|
||||
}
|
||||
if (next)
|
||||
{
|
||||
self->tail = next;
|
||||
return tail;
|
||||
}
|
||||
head = self->head;
|
||||
if (tail != head)
|
||||
return (queue_node_t *)1;
|
||||
mpscq_push(self, &self->stub);
|
||||
next = tail->Next;
|
||||
if (next)
|
||||
{
|
||||
self->tail = next;
|
||||
return tail;
|
||||
}
|
||||
return 0;
|
||||
}
|
23
Src/replicant/nu/lfmpscq.h
Normal file
23
Src/replicant/nu/lfmpscq.h
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include "queue_node.h"
|
||||
|
||||
/* lock free unbounded multiple producer, single consumer queue */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct mpscq_struct_t
|
||||
{
|
||||
queue_node_t * volatile head;
|
||||
queue_node_t *tail;
|
||||
queue_node_t stub;
|
||||
} mpscq_t;
|
||||
|
||||
#define MPSCQ_STATIC_INIT(self) {&self.stub, &self.stub, {0}}
|
||||
|
||||
void mpscq_init(mpscq_t* self);
|
||||
int mpscq_push(mpscq_t *self, queue_node_t *n);
|
||||
queue_node_t *mpscq_pop(mpscq_t *self); /* returns (queue_node_t *)1 if the queue is busy */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
158
Src/replicant/nu/lfringbuffer.c
Normal file
158
Src/replicant/nu/lfringbuffer.c
Normal file
@ -0,0 +1,158 @@
|
||||
#include "lfringbuffer.h"
|
||||
#include "foundation/error.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct LFRingBufferHeader
|
||||
{
|
||||
volatile size_t used; /* number of bytes written, in elements */
|
||||
size_t size; /* in elements */
|
||||
size_t write_position;
|
||||
size_t read_position;
|
||||
} LFRingBufferHeader;
|
||||
|
||||
typedef struct LFRingBuffer
|
||||
{
|
||||
LFRingBufferHeader header;
|
||||
float data[1];
|
||||
} LFRingBuffer;
|
||||
|
||||
/* create a ring buffer with the desired number of elements */
|
||||
int lfringbuffer_create(lfringbuffer_t *out_ring_buffer, size_t number_of_elements)
|
||||
{
|
||||
LFRingBuffer *ring_buffer = (LFRingBuffer *)malloc(sizeof(LFRingBufferHeader) + sizeof(float) * number_of_elements);
|
||||
if (!ring_buffer)
|
||||
return NErr_OutOfMemory;
|
||||
|
||||
ring_buffer->header.used = 0;
|
||||
ring_buffer->header.size = number_of_elements;
|
||||
ring_buffer->header.write_position = 0;
|
||||
ring_buffer->header.read_position = 0;
|
||||
*out_ring_buffer = (lfringbuffer_t)ring_buffer;
|
||||
return NErr_Success;
|
||||
}
|
||||
|
||||
int lfringbuffer_destroy(lfringbuffer_t ring_buffer)
|
||||
{
|
||||
free(ring_buffer);
|
||||
return NErr_Success;
|
||||
}
|
||||
|
||||
/* ----- Read functions ----- */
|
||||
/* get how many elements can currently be read */
|
||||
size_t lfringbuffer_read_available(lfringbuffer_t rb)
|
||||
{
|
||||
LFRingBuffer *ring_buffer = (LFRingBuffer *)rb;
|
||||
return ring_buffer->header.used;
|
||||
}
|
||||
|
||||
/* retrieve a pointer that can be read from.
|
||||
you might have to call this twice, because of ring buffer wraparound
|
||||
call lfringbuffer_read_update() when you are done */
|
||||
int lfringbuffer_read_get(lfringbuffer_t rb, size_t elements_requested, const float **out_buffer, size_t *elements_available)
|
||||
{
|
||||
LFRingBuffer *ring_buffer = (LFRingBuffer *)rb;
|
||||
size_t end, to_copy;
|
||||
int ret = NErr_Success;
|
||||
|
||||
/* can only read how many bytes we have available */
|
||||
to_copy=ring_buffer->header.used;
|
||||
if (to_copy > elements_requested)
|
||||
{
|
||||
to_copy = elements_requested;
|
||||
ret = NErr_Underrun; /* signal that there was a buffer underrun when reading */
|
||||
}
|
||||
|
||||
/* can only read until the end of the buffer */
|
||||
end = ring_buffer->header.size-ring_buffer->header.read_position;
|
||||
if (to_copy > end)
|
||||
{
|
||||
to_copy = end;
|
||||
ret = NErr_TryAgain; /* signal that they need to call again to get the next part of the buffer */
|
||||
}
|
||||
|
||||
*out_buffer = ring_buffer->data + ring_buffer->header.read_position;
|
||||
*elements_available = to_copy;
|
||||
|
||||
ring_buffer->header.read_position += to_copy;
|
||||
if (ring_buffer->header.read_position == ring_buffer->header.size)
|
||||
ring_buffer->header.read_position=0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* call to acknowledge that you have read the bytes */
|
||||
void lfringbuffer_read_update(lfringbuffer_t rb, size_t elements_read)
|
||||
{
|
||||
LFRingBuffer *ring_buffer = (LFRingBuffer *)rb;
|
||||
#if defined(__ARM_ARCH_7A__)
|
||||
__asm__ __volatile__ ("dmb" : : : "memory");
|
||||
#endif
|
||||
nx_atomic_sub(elements_read, &ring_buffer->header.used);
|
||||
}
|
||||
static void lfringbuffer_read_set_position(lfringbuffer_t rb, size_t position)
|
||||
{
|
||||
LFRingBuffer *ring_buffer = (LFRingBuffer *)rb;
|
||||
intptr_t bytes_to_flush = (intptr_t)(position - (size_t)ring_buffer->header.read_position);
|
||||
if (bytes_to_flush < 0)
|
||||
bytes_to_flush += ring_buffer->header.size;
|
||||
lfringbuffer_read_update(rb, bytes_to_flush);
|
||||
}
|
||||
|
||||
/* ----- Write functions ----- */
|
||||
/* get how many elements can currently be written */
|
||||
size_t lfringbuffer_write_available(lfringbuffer_t rb)
|
||||
{
|
||||
LFRingBuffer *ring_buffer = (LFRingBuffer *)rb;
|
||||
return ring_buffer->header.size - ring_buffer->header.used;
|
||||
}
|
||||
|
||||
/* retrieve a pointer that can be written to.
|
||||
you might have to call this twice, because of ring buffer wraparound
|
||||
call lfringbuffer_write_update() when you are done */
|
||||
int lfringbuffer_write_get(lfringbuffer_t rb, size_t elements_requested, float **out_buffer, size_t *elements_available)
|
||||
{
|
||||
LFRingBuffer *ring_buffer = (LFRingBuffer *)rb;
|
||||
size_t end, to_copy;
|
||||
int ret = NErr_Success;
|
||||
|
||||
/* can only write how many bytes we have available */
|
||||
to_copy=ring_buffer->header.size - ring_buffer->header.used;
|
||||
if (to_copy > elements_requested)
|
||||
{
|
||||
to_copy = elements_requested;
|
||||
ret = NErr_Underrun; /* signal that there was a buffer underrun when reading */
|
||||
}
|
||||
|
||||
/* can only read until the end of the buffer */
|
||||
end = ring_buffer->header.size-ring_buffer->header.write_position;
|
||||
if (to_copy > end)
|
||||
{
|
||||
to_copy = end;
|
||||
ret = NErr_TryAgain; /* signal that they need to call again to get the next part of the buffer */
|
||||
}
|
||||
|
||||
*out_buffer = ring_buffer->data + ring_buffer->header.write_position;
|
||||
*elements_available = to_copy;
|
||||
|
||||
ring_buffer->header.write_position += to_copy;
|
||||
if (ring_buffer->header.write_position == ring_buffer->header.size)
|
||||
ring_buffer->header.write_position=0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* call to acknowledge that you have written the bytes */
|
||||
void lfringbuffer_write_update(lfringbuffer_t rb, size_t elements_read)
|
||||
{
|
||||
LFRingBuffer *ring_buffer = (LFRingBuffer *)rb;
|
||||
#if defined(__ARM_ARCH_7A__)
|
||||
__asm__ __volatile__ ("dmb" : : : "memory");
|
||||
#endif
|
||||
nx_atomic_add(elements_read, &ring_buffer->header.used);
|
||||
}
|
||||
|
||||
size_t lfringbuffer_write_get_position(lfringbuffer_t rb)
|
||||
{
|
||||
LFRingBuffer *ring_buffer = (LFRingBuffer *)rb;
|
||||
return ring_buffer->header.write_position;
|
||||
}
|
46
Src/replicant/nu/lfringbuffer.h
Normal file
46
Src/replicant/nu/lfringbuffer.h
Normal file
@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
#include "foundation/types.h"
|
||||
/* lock free floating point ring buffer
|
||||
generic implementation
|
||||
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct lfringbuffer_s { } *lfringbuffer_t;
|
||||
|
||||
/* create a ring buffer with the desired number of elements */
|
||||
int lfringbuffer_create(lfringbuffer_t *out_ring_buffer, size_t number_of_elements);
|
||||
int lfringbuffer_destroy(lfringbuffer_t ring_buffer);
|
||||
|
||||
/* ----- Read functions ----- */
|
||||
/* get how many elements can currently be read */
|
||||
size_t lfringbuffer_read_available(lfringbuffer_t ring_buffer);
|
||||
|
||||
/* retrieve a pointer that can be read from.
|
||||
you might have to call this twice, because of ring buffer wraparound
|
||||
call lfringbuffer_read_update() when you are done */
|
||||
int lfringbuffer_read_get(lfringbuffer_t ring_buffer, size_t elements_requested, const float **out_buffer, size_t *elements_available);
|
||||
|
||||
/* call to acknowledge that you have read the bytes */
|
||||
void lfringbuffer_read_update(lfringbuffer_t ring_buffer, size_t elements_read);
|
||||
|
||||
/* ----- Write functions ----- */
|
||||
/* get how many elements can currently be written */
|
||||
size_t lfringbuffer_write_available(lfringbuffer_t ring_buffer);
|
||||
|
||||
/* retrieve a pointer that can be written to.
|
||||
you might have to call this twice, because of ring buffer wraparound
|
||||
call lfringbuffer_write_update() when you are done */
|
||||
int lfringbuffer_write_get(lfringbuffer_t ring_buffer, size_t elements_requested, float **out_buffer, size_t *elements_available);
|
||||
|
||||
/* call to acknowledge that you have written the bytes */
|
||||
void lfringbuffer_write_update(lfringbuffer_t ring_buffer, size_t elements_read);
|
||||
|
||||
size_t lfringbuffer_write_get_position(lfringbuffer_t ring_buffer);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
71
Src/replicant/nu/nodelist.c
Normal file
71
Src/replicant/nu/nodelist.c
Normal file
@ -0,0 +1,71 @@
|
||||
#include "nodelist.h"
|
||||
|
||||
void nodelist_init(nodelist_t nodelist)
|
||||
{
|
||||
nodelist->head=0;
|
||||
nodelist->tail=0;
|
||||
}
|
||||
|
||||
void nodelist_push_back(nodelist_t nodelist, queue_node_t *item)
|
||||
{
|
||||
if (!nodelist->head)
|
||||
{
|
||||
nodelist->head=item;
|
||||
nodelist->tail=item;
|
||||
}
|
||||
else
|
||||
{
|
||||
nodelist->tail->Next=item;
|
||||
nodelist->tail=item;
|
||||
}
|
||||
item->Next = 0;
|
||||
}
|
||||
|
||||
void nodelist_push_front(nodelist_t nodelist, queue_node_t *item)
|
||||
{
|
||||
if (!nodelist->head)
|
||||
{
|
||||
nodelist->head=item;
|
||||
nodelist->tail=item;
|
||||
item->Next=0;
|
||||
}
|
||||
else
|
||||
{
|
||||
item->Next = nodelist->head;
|
||||
nodelist->head = item;
|
||||
}
|
||||
}
|
||||
|
||||
queue_node_t *nodelist_pop_front(nodelist_t nodelist)
|
||||
{
|
||||
queue_node_t *ret;
|
||||
if (!nodelist->head)
|
||||
return 0;
|
||||
|
||||
ret = nodelist->head;
|
||||
nodelist->head = nodelist->head->Next;
|
||||
ret->Next = 0; // so we don't confuse anyone
|
||||
|
||||
if (ret == nodelist->tail)
|
||||
{
|
||||
nodelist->tail = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void nodelist_push_back_list(nodelist_t nodelist, queue_node_t *item)
|
||||
{
|
||||
if (!nodelist->head)
|
||||
{
|
||||
nodelist->head=item;
|
||||
}
|
||||
else
|
||||
{
|
||||
nodelist->tail->Next=item;
|
||||
}
|
||||
|
||||
while (item->Next)
|
||||
item = item->Next;
|
||||
|
||||
nodelist->tail = item;
|
||||
}
|
24
Src/replicant/nu/nodelist.h
Normal file
24
Src/replicant/nu/nodelist.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include "queue_node.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// non-thread-safe list of queue_node_t objects with _head and _tail
|
||||
typedef struct nodelist_s
|
||||
{
|
||||
queue_node_t *head;
|
||||
queue_node_t *tail;
|
||||
} nodelist_s, *nodelist_t;
|
||||
|
||||
void nodelist_init(nodelist_t nodelist);
|
||||
void nodelist_push_back(nodelist_t nodelist, queue_node_t *item);
|
||||
void nodelist_push_front(nodelist_t nodelist, queue_node_t *item);
|
||||
queue_node_t *nodelist_pop_front(nodelist_t nodelist);
|
||||
// pushes an item onto the list, but treat it as a whole list rather than a single item
|
||||
void nodelist_push_back_list(nodelist_t nodelist, queue_node_t *item);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
51
Src/replicant/nu/ns_wc.h
Normal file
51
Src/replicant/nu/ns_wc.h
Normal file
@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
__inline int MultiByteToWideCharSZ(
|
||||
UINT CodePage, // code page
|
||||
DWORD dwFlags, // character-type options
|
||||
LPCSTR lpMultiByteStr, // string to map
|
||||
int cbMultiByte, // number of bytes in string
|
||||
LPWSTR lpWideCharStr, // wide-character buffer
|
||||
int cchWideChar // size of buffer
|
||||
)
|
||||
{
|
||||
int converted=0;
|
||||
if (cchWideChar == 0)
|
||||
return MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, lpWideCharStr, cchWideChar);
|
||||
converted = MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, lpWideCharStr, cchWideChar-1);
|
||||
if (!converted)
|
||||
return 0;
|
||||
lpWideCharStr[converted]=0;
|
||||
return converted+1;
|
||||
}
|
||||
|
||||
__inline int WideCharToMultiByteSZ(
|
||||
UINT CodePage, // code page
|
||||
DWORD dwFlags, // performance and mapping flags
|
||||
LPCWSTR lpWideCharStr, // wide-character string
|
||||
int cchWideChar, // number of chars in string
|
||||
LPSTR lpMultiByteStr, // buffer for new string
|
||||
int cbMultiByte, // size of buffer
|
||||
LPCSTR lpDefaultChar, // default for unmappable chars
|
||||
LPBOOL lpUsedDefaultChar // set when default char used
|
||||
)
|
||||
{
|
||||
int converted=0;
|
||||
if (cbMultiByte == 0)
|
||||
return WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, lpMultiByteStr, cbMultiByte, lpDefaultChar, lpUsedDefaultChar);
|
||||
converted= WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, lpMultiByteStr, cbMultiByte-1, lpDefaultChar, lpUsedDefaultChar);
|
||||
if (!converted)
|
||||
return converted;
|
||||
lpMultiByteStr[converted]=0;
|
||||
return converted+1;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
28
Src/replicant/nu/nu.sln
Normal file
28
Src/replicant/nu/nu.sln
Normal file
@ -0,0 +1,28 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29509.3
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nu", "nu.vcxproj", "{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Debug|x64 = Debug|x64
|
||||
Release|Win32 = Release|Win32
|
||||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|x64.Build.0 = Debug|x64
|
||||
{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.Build.0 = Release|Win32
|
||||
{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.ActiveCfg = Release|x64
|
||||
{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
267
Src/replicant/nu/nu.vcxproj
Normal file
267
Src/replicant/nu/nu.vcxproj
Normal file
@ -0,0 +1,267 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}</ProjectGuid>
|
||||
<RootNamespace>nu</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
|
||||
<IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
|
||||
<IncludePath>$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
|
||||
<IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
|
||||
<IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
|
||||
<IncludePath>$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
|
||||
<IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Vcpkg">
|
||||
<VcpkgEnableManifest>false</VcpkgEnableManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<VcpkgInstalledDir>
|
||||
</VcpkgInstalledDir>
|
||||
<VcpkgUseStatic>false</VcpkgUseStatic>
|
||||
<VcpkgConfiguration>Debug</VcpkgConfiguration>
|
||||
<VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<VcpkgInstalledDir>
|
||||
</VcpkgInstalledDir>
|
||||
<VcpkgUseStatic>false</VcpkgUseStatic>
|
||||
<VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<VcpkgInstalledDir>
|
||||
</VcpkgInstalledDir>
|
||||
<VcpkgUseStatic>false</VcpkgUseStatic>
|
||||
<VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
|
||||
<VcpkgConfiguration>Debug</VcpkgConfiguration>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<VcpkgInstalledDir>
|
||||
</VcpkgInstalledDir>
|
||||
<VcpkgUseStatic>false</VcpkgUseStatic>
|
||||
<VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>..</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Lib>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>..</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Lib>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<Optimization>MinSpace</Optimization>
|
||||
<FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
|
||||
<PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..</AdditionalIncludeDirectories>
|
||||
<StringPooling>true</StringPooling>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>None</DebugInformationFormat>
|
||||
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Lib>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<Optimization>MinSpace</Optimization>
|
||||
<FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
|
||||
<PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..</AdditionalIncludeDirectories>
|
||||
<StringPooling>true</StringPooling>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>None</DebugInformationFormat>
|
||||
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Lib>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="BitReader.cpp" />
|
||||
<ClCompile Include="ByteReader.c" />
|
||||
<ClCompile Include="ByteWriter.c" />
|
||||
<ClCompile Include="lfitem.c" />
|
||||
<ClCompile Include="lfmpscq.c" />
|
||||
<ClCompile Include="LockFreeFIFO.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="LockFreeRingBuffer.cpp" />
|
||||
<ClCompile Include="nodelist.c" />
|
||||
<ClCompile Include="ProgressTracker.cpp" />
|
||||
<ClCompile Include="RingBuffer.cpp" />
|
||||
<ClCompile Include="win-amd64\ThreadLoop.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="win-x86\LockFreeLIFO.c">
|
||||
<AssemblerOutput Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AssemblyAndSourceCode</AssemblerOutput>
|
||||
<AssemblerOutput Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AssemblyAndSourceCode</AssemblerOutput>
|
||||
<AssemblerListingLocation Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(ProjectDir)x86_Release\lifo32.asm</AssemblerListingLocation>
|
||||
<AssemblerListingLocation Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectDir)x64_Release\lifo32.asm</AssemblerListingLocation>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="win\MessageLoop.cpp" />
|
||||
<ClCompile Include="win\ThreadLoop.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="BitReader.h" />
|
||||
<ClInclude Include="ByteWriter.h" />
|
||||
<ClInclude Include="lfmpscq.h" />
|
||||
<ClInclude Include="LockFreeFIFO.h" />
|
||||
<ClInclude Include="LockFreeItem.h" />
|
||||
<ClInclude Include="LockFreeLIFO.h" />
|
||||
<ClInclude Include="LockFreeRingBuffer.h" />
|
||||
<ClInclude Include="nodelist.h" />
|
||||
<ClInclude Include="ProgressTracker.h" />
|
||||
<ClInclude Include="queue_node.h" />
|
||||
<ClInclude Include="win-amd64\LockFreeLIFO.h" />
|
||||
<ClInclude Include="win-amd64\ThreadLoop.h" />
|
||||
<ClInclude Include="win-x86\LockFreeLIFO.h" />
|
||||
<ClInclude Include="win\ThreadLoop.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuild Include="win-x86\LockFreeLIFO.asm">
|
||||
<FileType>Document</FileType>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">ml /c /nologo /Fo"$(ProjectDir)x86_Debug\%(Filename)-$(PlatformShortName).obj" /Zi %(FullPath)</Command>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">ml /c /nologo /Fo"$(ProjectDir)x64_Debug\%(Filename)-$(PlatformShortName).obj" /Zi %(FullPath)</Command>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectDir)x86_Debug\%(Filename)-$(PlatformShortName).obj</Outputs>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectDir)x64_Debug\%(Filename)-$(PlatformShortName).obj</Outputs>
|
||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(FullPath)</AdditionalInputs>
|
||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(FullPath)</AdditionalInputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">ml /c /nologo /Fo"$(ProjectDir)x86_Release\%(Filename)-$(PlatformShortName).obj" /Zi %(FullPath)</Command>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">ml /c /nologo /Fo"$(ProjectDir)x64_Release\%(Filename)-$(PlatformShortName).obj" /Zi %(FullPath)</Command>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(ProjectDir)x86_Release\%(Filename)-$(PlatformShortName).obj</Outputs>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectDir)x64_Release\%(Filename)-$(PlatformShortName).obj</Outputs>
|
||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(FullPath)</AdditionalInputs>
|
||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(FullPath)</AdditionalInputs>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</CustomBuild>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
107
Src/replicant/nu/nu.vcxproj.filters
Normal file
107
Src/replicant/nu/nu.vcxproj.filters
Normal file
@ -0,0 +1,107 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="BitReader.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ByteReader.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ByteWriter.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="lfitem.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="lfmpscq.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="LockFreeFIFO.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="win-x86\LockFreeLIFO.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="LockFreeRingBuffer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="win\MessageLoop.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="nodelist.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ProgressTracker.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="RingBuffer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="win-amd64\ThreadLoop.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="win\ThreadLoop.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="win-amd64\ThreadLoop.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="win\ThreadLoop.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="queue_node.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ProgressTracker.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="nodelist.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="LockFreeRingBuffer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="LockFreeLIFO.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="win-x86\LockFreeLIFO.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="win-amd64\LockFreeLIFO.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="LockFreeItem.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="LockFreeFIFO.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="lfmpscq.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ByteWriter.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="BitReader.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{600469f6-bb91-4a1c-9603-9036bf14452f}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Ressource Files">
|
||||
<UniqueIdentifier>{c7911d31-e53d-45f9-b477-140f1e534e28}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{5c814c31-6040-46c7-9e53-e472177be0f1}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuild Include="win-x86\LockFreeLIFO.asm">
|
||||
<Filter>Source Files</Filter>
|
||||
</CustomBuild>
|
||||
</ItemGroup>
|
||||
</Project>
|
18
Src/replicant/nu/precomp.h
Normal file
18
Src/replicant/nu/precomp.h
Normal file
@ -0,0 +1,18 @@
|
||||
//
|
||||
// precomp.h
|
||||
// nu
|
||||
//
|
||||
|
||||
#include "foundation/error.h"
|
||||
#include "foundation/types.h"
|
||||
#include "foundation/atomics.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#endif //WIN32
|
31
Src/replicant/nu/queue_node.h
Normal file
31
Src/replicant/nu/queue_node.h
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <Windows.h>
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
typedef SLIST_ENTRY queue_node_t;
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct queue_node_struct_t
|
||||
{
|
||||
struct queue_node_struct_t *Next;
|
||||
} queue_node_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
4497
Src/replicant/nu/strsafe.h
Normal file
4497
Src/replicant/nu/strsafe.h
Normal file
File diff suppressed because it is too large
Load Diff
649
Src/replicant/nu/utf.c
Normal file
649
Src/replicant/nu/utf.c
Normal file
@ -0,0 +1,649 @@
|
||||
#include "utf.h"
|
||||
|
||||
#include "ByteReader.h"
|
||||
#include "ByteWriter.h"
|
||||
#include "foundation/error.h"
|
||||
#include <string.h>
|
||||
|
||||
static const uint8_t mask_tab[6]={0x80,0xE0,0xF0,0xF8,0xFC,0xFE};
|
||||
|
||||
static const uint8_t val_tab[6]={0,0xC0,0xE0,0xF0,0xF8,0xFC};
|
||||
|
||||
// returns the number of utf-16 words required to store a given codepoint
|
||||
static size_t ucs4_to_utf16_count(uint32_t codepoint)
|
||||
{
|
||||
if (codepoint >= 0x110000)
|
||||
return 0; // out of bounds
|
||||
|
||||
if (codepoint >= 0x10000)
|
||||
return 2;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int utf16LE_to_ucs4_character(bytereader_t const byte_reader, uint32_t *codepoint)
|
||||
{
|
||||
uint16_t lead;
|
||||
|
||||
lead = bytereader_read_u16_le(byte_reader);
|
||||
if (lead < 0xD800 || lead >= 0xE000)
|
||||
{
|
||||
*codepoint = lead;
|
||||
return NErr_Success;
|
||||
}
|
||||
|
||||
if (lead < 0xDC00)
|
||||
{
|
||||
if (bytereader_size(byte_reader) >= 2)
|
||||
{
|
||||
uint16_t trail = bytereader_read_u16_le(byte_reader);
|
||||
if (trail >= 0xDC00 && trail < 0xE000)
|
||||
{
|
||||
*codepoint = 0x10000 + ((lead - 0xD800) << 10) + (trail - 0xDC00);
|
||||
return NErr_Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NErr_Error; // invalid
|
||||
}
|
||||
|
||||
static int utf16BE_to_ucs4_character(bytereader_t const byte_reader, uint32_t *codepoint)
|
||||
{
|
||||
uint16_t lead;
|
||||
|
||||
lead = bytereader_read_u16_be(byte_reader);
|
||||
if (lead < 0xD800 || lead >= 0xE000)
|
||||
{
|
||||
*codepoint = lead;
|
||||
return NErr_Success;
|
||||
}
|
||||
|
||||
if (lead < 0xDC00)
|
||||
{
|
||||
if (bytereader_size(byte_reader) >= 2)
|
||||
{
|
||||
uint16_t trail = bytereader_read_u16_be(byte_reader);
|
||||
if (trail >= 0xDC00 && trail < 0xE000)
|
||||
{
|
||||
*codepoint = 0x10000 + ((lead - 0xD800) << 10) + (trail - 0xDC00);
|
||||
return NErr_Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NErr_Error; // invalid
|
||||
}
|
||||
|
||||
static size_t utf8_to_ucs4_character(const char *utf8, size_t len, uint32_t *codepoint)
|
||||
{
|
||||
uint32_t res=0;
|
||||
size_t n;
|
||||
size_t cnt=0;
|
||||
while(1)
|
||||
{
|
||||
if ((*utf8&mask_tab[cnt])==val_tab[cnt]) break;
|
||||
if (++cnt==6) return 0;
|
||||
}
|
||||
cnt++;
|
||||
|
||||
|
||||
if (cnt==2 && !(*utf8&0x1E))
|
||||
return 0;
|
||||
|
||||
if (cnt==1)
|
||||
res=*utf8;
|
||||
else
|
||||
res=(0xFF>>(cnt+1))&*utf8;
|
||||
|
||||
if (cnt > len)
|
||||
return 0;
|
||||
|
||||
for (n=1;n<cnt;n++)
|
||||
{
|
||||
if ((utf8[n]&0xC0) != 0x80)
|
||||
return 0;
|
||||
if (!res && n==2 && !((utf8[n]&0x7F) >> (7 - cnt)))
|
||||
return 0;
|
||||
|
||||
res=(res<<6)|(utf8[n]&0x3F);
|
||||
}
|
||||
|
||||
if (codepoint)
|
||||
*codepoint=res;
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
// returns the number of utf-8 bytes required to store a given codepoint
|
||||
static size_t ucs4_to_utf8_count(uint32_t codepoint)
|
||||
{
|
||||
if (codepoint < 0x80)
|
||||
return 1;
|
||||
else if (codepoint < 0x800)
|
||||
return 2;
|
||||
else if (codepoint < 0x10000)
|
||||
return 3;
|
||||
else if (codepoint < 0x200000)
|
||||
return 4;
|
||||
else if (codepoint < 0x4000000)
|
||||
return 5;
|
||||
else if (codepoint <= 0x7FFFFFFF)
|
||||
return 6;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t ucs4_to_utf8_character(char *target, uint32_t codepoint, size_t max)
|
||||
{
|
||||
size_t count = ucs4_to_utf8_count(codepoint);
|
||||
|
||||
if (!count)
|
||||
return 0;
|
||||
|
||||
if (count>max) return 0;
|
||||
|
||||
if (target == 0)
|
||||
return count;
|
||||
|
||||
switch (count)
|
||||
{
|
||||
case 6:
|
||||
target[5] = 0x80 | (codepoint & 0x3F);
|
||||
codepoint = codepoint >> 6;
|
||||
codepoint |= 0x4000000;
|
||||
case 5:
|
||||
target[4] = 0x80 | (codepoint & 0x3F);
|
||||
codepoint = codepoint >> 6;
|
||||
codepoint |= 0x200000;
|
||||
case 4:
|
||||
target[3] = 0x80 | (codepoint & 0x3F);
|
||||
codepoint = codepoint >> 6;
|
||||
codepoint |= 0x10000;
|
||||
case 3:
|
||||
target[2] = 0x80 | (codepoint & 0x3F);
|
||||
codepoint = codepoint >> 6;
|
||||
codepoint |= 0x800;
|
||||
case 2:
|
||||
target[1] = 0x80 | (codepoint & 0x3F);
|
||||
codepoint = codepoint >> 6;
|
||||
codepoint |= 0xC0;
|
||||
case 1:
|
||||
target[0] = codepoint;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static size_t ucs4_to_utf16LE_character(bytewriter_t byte_writer, uint32_t codepoint)
|
||||
{
|
||||
if (codepoint >= 0x110000)
|
||||
return 0;
|
||||
|
||||
if (codepoint >= 0x10000)
|
||||
{
|
||||
if (bytewriter_size(byte_writer) < 4)
|
||||
return 0;
|
||||
|
||||
bytewriter_write_u16_le(byte_writer, ((codepoint - 0x10000) >> 10) + 0xD800); // high surrogate
|
||||
bytewriter_write_u16_le(byte_writer, ((codepoint - 0x10000) & 0x3FF) + 0xDC00); // low surrogate
|
||||
return 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
bytewriter_write_u16_le(byte_writer, codepoint);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static size_t ucs4_to_utf16BE_character(bytewriter_t byte_writer, uint32_t codepoint)
|
||||
{
|
||||
if (codepoint >= 0x110000)
|
||||
return 0;
|
||||
|
||||
if (codepoint >= 0x10000)
|
||||
{
|
||||
if (bytewriter_size(byte_writer) < 4)
|
||||
return 0;
|
||||
|
||||
bytewriter_write_u16_be(byte_writer, ((codepoint - 0x10000) >> 10) + 0xD800); // high surrogate
|
||||
bytewriter_write_u16_be(byte_writer, ((codepoint - 0x10000) & 0x3FF) + 0xDC00); // low surrogate
|
||||
return 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
bytewriter_write_u16_be(byte_writer, codepoint);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
size_t utf16LE_to_utf8(const uint16_t *src, size_t source_len, char *dst, size_t out_len)
|
||||
{
|
||||
uint32_t codepoint;
|
||||
size_t position=0;
|
||||
size_t characters_processed;
|
||||
bytereader_s byte_reader;
|
||||
bytereader_init(&byte_reader, src, source_len*2);
|
||||
|
||||
if (!dst) // they just want the size
|
||||
{
|
||||
while (bytereader_size(&byte_reader))
|
||||
{
|
||||
if (utf16LE_to_ucs4_character(&byte_reader, &codepoint) != NErr_Success)
|
||||
break;
|
||||
|
||||
characters_processed = ucs4_to_utf8_count(codepoint);
|
||||
if (!characters_processed)
|
||||
break;
|
||||
|
||||
position+=characters_processed;
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
while(bytereader_size(&byte_reader) && position<out_len)
|
||||
{
|
||||
if (utf16LE_to_ucs4_character(&byte_reader, &codepoint) != NErr_Success)
|
||||
break;
|
||||
|
||||
characters_processed=ucs4_to_utf8_character(&dst[position], codepoint, out_len-position);
|
||||
if (!characters_processed)
|
||||
break;
|
||||
position+=characters_processed;
|
||||
}
|
||||
if (position<out_len)
|
||||
dst[position]=0;
|
||||
return position;
|
||||
}
|
||||
|
||||
size_t utf16BE_to_utf8(const uint16_t *src, size_t source_len, char *dst, size_t out_len)
|
||||
{
|
||||
uint32_t codepoint;
|
||||
size_t position=0;
|
||||
size_t characters_processed;
|
||||
bytereader_s byte_reader;
|
||||
bytereader_init(&byte_reader, src, source_len*2);
|
||||
|
||||
if (!dst) // they just want the size
|
||||
{
|
||||
while (bytereader_size(&byte_reader))
|
||||
{
|
||||
if (utf16BE_to_ucs4_character(&byte_reader, &codepoint) != NErr_Success)
|
||||
break;
|
||||
|
||||
characters_processed = ucs4_to_utf8_count(codepoint);
|
||||
if (!characters_processed)
|
||||
break;
|
||||
|
||||
position+=characters_processed;
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
while(bytereader_size(&byte_reader) && position<out_len)
|
||||
{
|
||||
if (utf16BE_to_ucs4_character(&byte_reader, &codepoint) != NErr_Success)
|
||||
break;
|
||||
|
||||
characters_processed=ucs4_to_utf8_character(&dst[position], codepoint, out_len-position);
|
||||
if (!characters_processed)
|
||||
break;
|
||||
position+=characters_processed;
|
||||
}
|
||||
if (position<out_len)
|
||||
dst[position]=0;
|
||||
return position;
|
||||
}
|
||||
|
||||
|
||||
size_t ucs4_to_utf8(const uint32_t *src, size_t source_len, char *dst, size_t out_len)
|
||||
{
|
||||
uint32_t codepoint;
|
||||
size_t position=0;
|
||||
size_t characters_processed;
|
||||
bytereader_s byte_reader;
|
||||
bytereader_init(&byte_reader, src, source_len*4);
|
||||
|
||||
if (!dst) // they just want the size
|
||||
{
|
||||
while (bytereader_size(&byte_reader) > 3)
|
||||
{
|
||||
codepoint = bytereader_read_u32_le(&byte_reader);
|
||||
|
||||
characters_processed = ucs4_to_utf8_count(codepoint);
|
||||
if (!characters_processed)
|
||||
break;
|
||||
|
||||
position+=characters_processed;
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
while(bytereader_size(&byte_reader) > 3 && position<out_len)
|
||||
{
|
||||
codepoint = bytereader_read_u32_le(&byte_reader);
|
||||
|
||||
characters_processed=ucs4_to_utf8_character(&dst[position], codepoint, out_len-position);
|
||||
if (!characters_processed)
|
||||
break;
|
||||
position+=characters_processed;
|
||||
}
|
||||
if (position<out_len)
|
||||
dst[position]=0;
|
||||
return position;
|
||||
}
|
||||
|
||||
size_t utf8_to_utf16LE(const char *src, size_t source_len, uint16_t *dst, size_t out_len)
|
||||
{
|
||||
uint32_t codepoint;
|
||||
size_t characters_processed;
|
||||
bytewriter_s byte_writer;
|
||||
|
||||
if (!dst) // they just want the size
|
||||
{
|
||||
size_t position=0;
|
||||
while (source_len)
|
||||
{
|
||||
characters_processed = utf8_to_ucs4_character(src, source_len, &codepoint);
|
||||
if (codepoint == 0xFFFD)
|
||||
break;
|
||||
|
||||
source_len -= characters_processed;
|
||||
src += characters_processed;
|
||||
|
||||
characters_processed = ucs4_to_utf16_count(codepoint);
|
||||
if (!characters_processed)
|
||||
break;
|
||||
|
||||
position+=characters_processed;
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
|
||||
bytewriter_init(&byte_writer, dst, out_len*2);
|
||||
while(source_len && bytewriter_size(&byte_writer))
|
||||
{
|
||||
characters_processed = utf8_to_ucs4_character(src, source_len, &codepoint);
|
||||
if (codepoint == 0xFFFD)
|
||||
break;
|
||||
|
||||
source_len -= characters_processed;
|
||||
src += characters_processed;
|
||||
|
||||
characters_processed=ucs4_to_utf16LE_character(&byte_writer, codepoint);
|
||||
if (!characters_processed)
|
||||
break;
|
||||
}
|
||||
if (bytewriter_size(&byte_writer))
|
||||
bytewriter_write_u16_le(&byte_writer, 0);
|
||||
return out_len - bytewriter_size(&byte_writer)/2;
|
||||
}
|
||||
|
||||
size_t utf8_to_utf16BE(const char *src, size_t source_len, uint16_t *dst, size_t out_len)
|
||||
{
|
||||
uint32_t codepoint;
|
||||
size_t characters_processed;
|
||||
bytewriter_s byte_writer;
|
||||
|
||||
if (!dst) // they just want the size
|
||||
{
|
||||
size_t position=0;
|
||||
while (source_len)
|
||||
{
|
||||
characters_processed = utf8_to_ucs4_character(src, source_len, &codepoint);
|
||||
if (codepoint == 0xFFFD)
|
||||
break;
|
||||
|
||||
source_len -= characters_processed;
|
||||
src += characters_processed;
|
||||
|
||||
characters_processed = ucs4_to_utf16_count(codepoint);
|
||||
if (!characters_processed)
|
||||
break;
|
||||
|
||||
position+=characters_processed;
|
||||
}
|
||||
return position;
|
||||
}
|
||||
bytewriter_init(&byte_writer, dst, out_len*2);
|
||||
while(source_len && bytewriter_size(&byte_writer))
|
||||
{
|
||||
characters_processed = utf8_to_ucs4_character(src, source_len, &codepoint);
|
||||
if (codepoint == 0xFFFD)
|
||||
break;
|
||||
|
||||
source_len -= characters_processed;
|
||||
src += characters_processed;
|
||||
|
||||
characters_processed=ucs4_to_utf16BE_character(&byte_writer, codepoint);
|
||||
if (!characters_processed)
|
||||
break;
|
||||
|
||||
}
|
||||
if (bytewriter_size(&byte_writer))
|
||||
bytewriter_write_u16_be(&byte_writer, 0);
|
||||
|
||||
return out_len - bytewriter_size(&byte_writer)/2;
|
||||
|
||||
}
|
||||
|
||||
size_t utf8_to_ISO_8859_1(const char *src, size_t source_len, char *dst, size_t out_len)
|
||||
{
|
||||
uint32_t codepoint;
|
||||
size_t position=0;
|
||||
size_t characters_processed;
|
||||
|
||||
if (!dst) // they just want the size
|
||||
{
|
||||
while (source_len)
|
||||
{
|
||||
characters_processed = utf8_to_ucs4_character(src, source_len, &codepoint);
|
||||
if (codepoint == 0xFFFD)
|
||||
break;
|
||||
|
||||
source_len -= characters_processed;
|
||||
src += characters_processed;
|
||||
position++;
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
while(source_len && position<out_len)
|
||||
{
|
||||
characters_processed = utf8_to_ucs4_character(src, source_len, &codepoint);
|
||||
if (codepoint == 0xFFFD)
|
||||
break;
|
||||
|
||||
source_len -= characters_processed;
|
||||
src += characters_processed;
|
||||
|
||||
if (codepoint < 256)
|
||||
dst[position++] = codepoint;
|
||||
else
|
||||
dst[position++] = '?';
|
||||
}
|
||||
if (position<out_len)
|
||||
dst[position]=0;
|
||||
return position;
|
||||
}
|
||||
|
||||
size_t ISO_8859_1_to_utf8(const char *src, size_t source_len, char *dst, size_t out_len)
|
||||
{
|
||||
uint32_t codepoint;
|
||||
size_t position=0;
|
||||
size_t characters_processed;
|
||||
|
||||
if (!dst) // they just want the size
|
||||
{
|
||||
while (source_len)
|
||||
{
|
||||
codepoint = *src++;
|
||||
source_len--;
|
||||
|
||||
characters_processed = ucs4_to_utf8_count(codepoint);
|
||||
if (!characters_processed)
|
||||
break;
|
||||
|
||||
position+=characters_processed;
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
while(source_len && position<out_len)
|
||||
{
|
||||
codepoint = *src++;
|
||||
|
||||
source_len--;
|
||||
|
||||
characters_processed=ucs4_to_utf8_character(&dst[position], codepoint, out_len-position);
|
||||
if (!characters_processed)
|
||||
break;
|
||||
|
||||
position+=characters_processed;
|
||||
}
|
||||
if (position<out_len)
|
||||
dst[position]=0;
|
||||
return position;
|
||||
}
|
||||
|
||||
size_t utf8_to_ucs4(const char *src, size_t source_len, uint32_t *dst, size_t out_len)
|
||||
{
|
||||
uint32_t codepoint;
|
||||
size_t characters_processed;
|
||||
bytewriter_s byte_writer;
|
||||
|
||||
if (!dst) // they just want the size
|
||||
{
|
||||
size_t position=0;
|
||||
while (source_len)
|
||||
{
|
||||
characters_processed = utf8_to_ucs4_character(src, source_len, &codepoint);
|
||||
if (codepoint == 0xFFFD)
|
||||
break;
|
||||
|
||||
source_len -= characters_processed;
|
||||
src += characters_processed;
|
||||
|
||||
characters_processed = 1;
|
||||
|
||||
position+=characters_processed;
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
bytewriter_init(&byte_writer, dst, out_len*4);
|
||||
while(source_len && bytewriter_size(&byte_writer))
|
||||
{
|
||||
characters_processed = utf8_to_ucs4_character(src, source_len, &codepoint);
|
||||
if (codepoint == 0xFFFD)
|
||||
break;
|
||||
|
||||
source_len -= characters_processed;
|
||||
src += characters_processed;
|
||||
|
||||
bytewriter_write_u32_le(&byte_writer, codepoint);
|
||||
}
|
||||
if (bytewriter_size(&byte_writer))
|
||||
bytewriter_write_u32_le(&byte_writer, 0);
|
||||
return out_len - bytewriter_size(&byte_writer)/4;
|
||||
}
|
||||
|
||||
size_t ASCII_to_utf8(const char *src, size_t source_len, char *dst, size_t out_len)
|
||||
{
|
||||
uint32_t codepoint;
|
||||
size_t position=0;
|
||||
size_t characters_processed;
|
||||
|
||||
if (!dst) // they just want the size
|
||||
{
|
||||
while (source_len)
|
||||
{
|
||||
codepoint = *src++;
|
||||
source_len--;
|
||||
|
||||
characters_processed = ucs4_to_utf8_count(codepoint);
|
||||
if (!characters_processed)
|
||||
break;
|
||||
|
||||
position+=characters_processed;
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
while(source_len && position<out_len)
|
||||
{
|
||||
codepoint = *src++;
|
||||
|
||||
source_len--;
|
||||
|
||||
characters_processed=ucs4_to_utf8_character(&dst[position], codepoint, out_len-position);
|
||||
if (!characters_processed)
|
||||
break;
|
||||
|
||||
position+=characters_processed;
|
||||
}
|
||||
if (position<out_len)
|
||||
dst[position]=0;
|
||||
return position;
|
||||
}
|
||||
|
||||
size_t utf8_to_ASCII(const char *src, size_t source_len, char *dst, size_t out_len)
|
||||
{
|
||||
uint32_t codepoint;
|
||||
size_t position=0;
|
||||
size_t characters_processed;
|
||||
|
||||
if (!dst) // they just want the size
|
||||
{
|
||||
while (source_len)
|
||||
{
|
||||
characters_processed = utf8_to_ucs4_character(src, source_len, &codepoint);
|
||||
if (codepoint == 0xFFFD)
|
||||
break;
|
||||
|
||||
source_len -= characters_processed;
|
||||
src += characters_processed;
|
||||
position++;
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
while(source_len && position<out_len)
|
||||
{
|
||||
characters_processed = utf8_to_ucs4_character(src, source_len, &codepoint);
|
||||
if (codepoint == 0xFFFD)
|
||||
break;
|
||||
|
||||
source_len -= characters_processed;
|
||||
src += characters_processed;
|
||||
|
||||
if (codepoint < 128)
|
||||
dst[position++] = codepoint;
|
||||
else
|
||||
dst[position++] = '?';
|
||||
}
|
||||
if (position<out_len)
|
||||
dst[position]=0;
|
||||
return position;
|
||||
}
|
||||
|
||||
size_t utf8_strnlen(const char *src, size_t source_len, size_t codepoints)
|
||||
{
|
||||
uint32_t codepoint = 0;
|
||||
size_t position=0;
|
||||
size_t i=0;
|
||||
|
||||
for (i=0;i<codepoints && *src;i++)
|
||||
{
|
||||
size_t characters_processed = utf8_to_ucs4_character(src, source_len, &codepoint);
|
||||
if (codepoint == 0xFFFD)
|
||||
break;
|
||||
|
||||
source_len -= characters_processed;
|
||||
src += characters_processed;
|
||||
position+=characters_processed;
|
||||
}
|
||||
return position;
|
||||
|
||||
}
|
26
Src/replicant/nu/utf.h
Normal file
26
Src/replicant/nu/utf.h
Normal file
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include "foundation/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* to utf8 */
|
||||
size_t utf16LE_to_utf8(const uint16_t *src, size_t source_len, char *dst, size_t out_len);
|
||||
size_t utf16BE_to_utf8(const uint16_t *src, size_t source_len, char *dst, size_t out_len);
|
||||
size_t ISO_8859_1_to_utf8(const char *src, size_t source_len, char *dst, size_t out_len);
|
||||
size_t ASCII_to_utf8(const char *src, size_t source_len, char *dst, size_t out_len);
|
||||
size_t ucs4_to_utf8(const uint32_t *src, size_t source_len, char *dst, size_t out_len);
|
||||
|
||||
/* from utf8 */
|
||||
size_t utf8_to_utf16LE(const char *src, size_t source_len, uint16_t *dst, size_t out_len);
|
||||
size_t utf8_to_ISO_8859_1(const char *src, size_t source_len, char *dst, size_t out_len);
|
||||
size_t utf8_to_utf16BE(const char *src, size_t source_len, uint16_t *dst, size_t out_len);
|
||||
size_t utf8_to_ASCII(const char *src, size_t source_len, char *dst, size_t out_len);
|
||||
size_t utf8_to_ucs4(const char *src, size_t source_len, uint32_t *dst, size_t out_len);
|
||||
|
||||
/* returns the number of bytes required to make the specified number of codepoints exactly fit */
|
||||
size_t utf8_strnlen(const char *src, size_t source_len, size_t codepoints);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
34
Src/replicant/nu/win-amd64/LockFreeLIFO.h
Normal file
34
Src/replicant/nu/win-amd64/LockFreeLIFO.h
Normal file
@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "nu/queue_node.h"
|
||||
#include <windows.h>
|
||||
#include <malloc.h>
|
||||
|
||||
/* lock free stack object
|
||||
multiple threads can push and pop without locking
|
||||
note that order is not guaranteed. that is, if Thread 1 calls Insert before Thread 2, Thread 2's item might still make it in before.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define NU_LIFO_INLINE inline
|
||||
extern "C" {
|
||||
#else
|
||||
#define NX_ATOMIC_INLINE
|
||||
#endif
|
||||
|
||||
typedef SLIST_HEADER lifo_t;
|
||||
|
||||
/* use this to allocate an object that will go into this */
|
||||
NU_LIFO_INLINE static queue_node_t *lifo_malloc(size_t bytes) { return (queue_node_t *)_aligned_malloc(bytes, MEMORY_ALLOCATION_ALIGNMENT); }
|
||||
NU_LIFO_INLINE static void lifo_free(queue_node_t *ptr) { _aligned_free(ptr); }
|
||||
|
||||
NU_LIFO_INLINE static lifo_t *lifo_create() { return (lifo_t *)_aligned_malloc(sizeof(SLIST_HEADER), MEMORY_ALLOCATION_ALIGNMENT); }
|
||||
NU_LIFO_INLINE static void lifo_destroy(lifo_t *lifo) { _aligned_free(lifo); }
|
||||
NU_LIFO_INLINE static void lifo_init(lifo_t *lifo) { InitializeSListHead(lifo); }
|
||||
NU_LIFO_INLINE static void lifo_push(lifo_t *lifo, queue_node_t *cl) { InterlockedPushEntrySList(lifo, cl); }
|
||||
NU_LIFO_INLINE static queue_node_t *lifo_pop(lifo_t *lifo) { return InterlockedPopEntrySList(lifo); }
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
70
Src/replicant/nu/win-amd64/ThreadLoop.cpp
Normal file
70
Src/replicant/nu/win-amd64/ThreadLoop.cpp
Normal file
@ -0,0 +1,70 @@
|
||||
#include "ThreadLoop.h"
|
||||
|
||||
lifo_t ThreadLoop::procedure_cache = {0,};
|
||||
lifo_t ThreadLoop::cache_bases= {0,};
|
||||
|
||||
#define PROCEDURE_CACHE_SEED 64
|
||||
ThreadLoop::ThreadLoop()
|
||||
{
|
||||
mpscq_init(&procedure_queue);
|
||||
procedure_notification = CreateSemaphore(0, 0, LONG_MAX, 0);
|
||||
kill_switch = CreateEvent(0, TRUE, FALSE, 0);
|
||||
}
|
||||
|
||||
void ThreadLoop::RefillCache()
|
||||
{
|
||||
threadloop_node_t *cache_seed = (threadloop_node_t *)lifo_malloc(PROCEDURE_CACHE_SEED*sizeof(threadloop_node_t));
|
||||
if (cache_seed)
|
||||
{
|
||||
memset(cache_seed, 0, PROCEDURE_CACHE_SEED*sizeof(threadloop_node_t));
|
||||
for (int i=0;i<PROCEDURE_CACHE_SEED;i++)
|
||||
{
|
||||
lifo_push(&procedure_cache, &cache_seed[i]);
|
||||
}
|
||||
lifo_push(&cache_bases, cache_seed);
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadLoop::Run()
|
||||
{
|
||||
HANDLE events[] = {kill_switch, procedure_notification};
|
||||
while (WaitForMultipleObjects(2, events, FALSE, INFINITE) == WAIT_OBJECT_0 + 1)
|
||||
{
|
||||
threadloop_node_t *apc;
|
||||
for (;;)
|
||||
{
|
||||
apc = (threadloop_node_t *)mpscq_pop(&procedure_queue);
|
||||
if (!apc)
|
||||
{
|
||||
Sleep(0);
|
||||
continue;
|
||||
}
|
||||
apc->func(apc->param1, apc->param2, apc->real_value);
|
||||
}
|
||||
lifo_push(&procedure_cache, apc);
|
||||
}
|
||||
}
|
||||
|
||||
threadloop_node_t *ThreadLoop::GetAPC()
|
||||
{
|
||||
threadloop_node_t *apc = 0;
|
||||
|
||||
do
|
||||
{
|
||||
apc = (threadloop_node_t *)lifo_pop(&procedure_cache);
|
||||
if (!apc)
|
||||
RefillCache();
|
||||
} while (!apc);
|
||||
return apc;
|
||||
}
|
||||
|
||||
void ThreadLoop::Schedule(threadloop_node_t *apc)
|
||||
{
|
||||
mpscq_push(&procedure_queue, apc);
|
||||
ReleaseSemaphore(procedure_notification, 1, 0);
|
||||
}
|
||||
|
||||
void ThreadLoop::Kill()
|
||||
{
|
||||
SetEvent(kill_switch);
|
||||
}
|
38
Src/replicant/nu/win-amd64/ThreadLoop.h
Normal file
38
Src/replicant/nu/win-amd64/ThreadLoop.h
Normal file
@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include "nu/lfmpscq.h"
|
||||
#include "nu/LockFreeLIFO.h"
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
struct threadloop_node_t : public queue_node_t
|
||||
{
|
||||
void (*func)(void *param1, void *param2, double real_value);
|
||||
|
||||
void *param1;
|
||||
void *param2;
|
||||
double real_value;
|
||||
};
|
||||
|
||||
class ThreadLoop
|
||||
{
|
||||
public:
|
||||
ThreadLoop();
|
||||
threadloop_node_t *GetAPC(); // returns a node for you to fill out
|
||||
void Schedule(threadloop_node_t *apc);
|
||||
void Run();
|
||||
void Kill();
|
||||
private:
|
||||
void RefillCache();
|
||||
|
||||
HANDLE procedure_notification;
|
||||
HANDLE kill_switch;
|
||||
mpscq_t procedure_queue;
|
||||
|
||||
/* Memory cache to be able to run APCs without having the memory manager lock
|
||||
we'll allocate 100 at a time (#defined by PROCEDURE_CACHE_SEED)
|
||||
and allocate new ones only if the cache is empty (which unfortunately will lock)
|
||||
cache_bases holds the pointers we've allocated (to free on destruction of this object)
|
||||
and procedure_cache holds the individual pointers */
|
||||
static lifo_t procedure_cache;
|
||||
static lifo_t cache_bases;
|
||||
};
|
50
Src/replicant/nu/win-x86/LockFreeLIFO.asm
Normal file
50
Src/replicant/nu/win-x86/LockFreeLIFO.asm
Normal file
@ -0,0 +1,50 @@
|
||||
.686
|
||||
.model FLAT
|
||||
|
||||
PUBLIC _lifo_push
|
||||
_TEXT SEGMENT
|
||||
lifo = 4 ; size = 4
|
||||
entry = 8 ; size = 4
|
||||
_lifo_push PROC
|
||||
mov ecx, DWORD PTR 4[esp] ; ecx holds lifo
|
||||
mov edx, DWORD PTR 8[esp] ; edx holds the new entry
|
||||
again:
|
||||
mov eax, DWORD PTR [ecx] ; eax holds the old head
|
||||
mov DWORD PTR[edx], eax ; new node's 'next' is set to the old head
|
||||
lock cmpxchg DWORD PTR [ecx], edx
|
||||
jnz again
|
||||
ret 0
|
||||
_lifo_push ENDP
|
||||
|
||||
PUBLIC _lifo_pop
|
||||
_TEXT SEGMENT
|
||||
lifo = 4 ; size = 4
|
||||
_lifo_pop PROC
|
||||
push esi
|
||||
push ebx
|
||||
mov esi, DWORD PTR 12[esp] ; esi holds lifo
|
||||
again:
|
||||
; if re-ordered loads become an issue, we could use cmpxchg8b to read in (after zeroing ebx/ecx) or maybe use movq
|
||||
mov edx, DWORD PTR [esi+4] ; counter
|
||||
; or we could put an LFENCE here
|
||||
mov eax, DWORD PTR [esi] ; pointer
|
||||
test eax, eax
|
||||
jz bail
|
||||
|
||||
mov ecx, edx ; counter
|
||||
mov ebx, DWORD PTR [eax] ; pointer->next
|
||||
inc ecx
|
||||
|
||||
lock cmpxchg8b QWORD PTR [esi]
|
||||
jnz again
|
||||
|
||||
bail:
|
||||
pop ebx
|
||||
pop esi
|
||||
ret 0
|
||||
_lifo_pop ENDP
|
||||
|
||||
_TEXT ENDS
|
||||
|
||||
END
|
||||
|
48
Src/replicant/nu/win-x86/LockFreeLIFO.c
Normal file
48
Src/replicant/nu/win-x86/LockFreeLIFO.c
Normal file
@ -0,0 +1,48 @@
|
||||
#include "LockFreeLIFO.h"
|
||||
#include "foundation/atomics.h"
|
||||
|
||||
/* win32 implementation */
|
||||
|
||||
void lifo_init(lifo_t *lifo)
|
||||
{
|
||||
lifo->head = 0;
|
||||
lifo->aba = 0;
|
||||
}
|
||||
|
||||
#if 0 // defined in LockFreeLIFO.asm
|
||||
void lifo_push(lifo_t *lifo, queue_node_t *cl)
|
||||
{
|
||||
queue_node_t *new_head = cl;
|
||||
queue_node_t *old_head = 0;
|
||||
do
|
||||
{
|
||||
old_head = (queue_node_t *)lifo->head;
|
||||
new_head->Next = old_head;
|
||||
} while (!nx_atomic_cmpxchg_pointer(old_head, new_head, (void * volatile *)&lifo->head));
|
||||
}
|
||||
|
||||
queue_node_t *lifo_pop(lifo_t *lifo)
|
||||
{
|
||||
lifo_t old_head, new_head;
|
||||
do
|
||||
{
|
||||
old_head = *lifo;
|
||||
if (!old_head.head)
|
||||
return 0;
|
||||
|
||||
new_head.head = old_head.head->Next;
|
||||
new_head.aba = old_head.aba+1;
|
||||
} while (!nx_atomic_cmpxchg2(*(int64_t *)&old_head, *(int64_t *)&new_head, (volatile int64_t *)&lifo->head));
|
||||
return (queue_node_t *)old_head.head;
|
||||
}
|
||||
#endif
|
||||
|
||||
queue_node_t *lifo_malloc(size_t bytes)
|
||||
{
|
||||
return _aligned_malloc(bytes, MEMORY_ALLOCATION_ALIGNMENT);
|
||||
}
|
||||
|
||||
void lifo_free(queue_node_t *ptr)
|
||||
{
|
||||
_aligned_free(ptr);
|
||||
}
|
31
Src/replicant/nu/win-x86/LockFreeLIFO.h
Normal file
31
Src/replicant/nu/win-x86/LockFreeLIFO.h
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
#include "foundation/types.h"
|
||||
#include "nu/queue_node.h"
|
||||
#include "foundation/align.h"
|
||||
/* lock free stack object
|
||||
multiple threads can push and pop without locking
|
||||
note that order is not guaranteed. that is, if Thread 1 calls Insert before Thread 2, Thread 2's item might still make it in before.
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef NALIGN(8) struct lifo_struct_t
|
||||
{
|
||||
volatile queue_node_t *head;
|
||||
uint32_t aba;
|
||||
} lifo_t;
|
||||
|
||||
/* use this to allocate an object that will go into this */
|
||||
queue_node_t *lifo_malloc(size_t bytes);
|
||||
void lifo_free(queue_node_t *ptr);
|
||||
|
||||
void lifo_init(lifo_t *lifo);
|
||||
void lifo_push(lifo_t *lifo, queue_node_t *cl);
|
||||
queue_node_t *lifo_pop(lifo_t *lifo);
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
84
Src/replicant/nu/win-x86/ThreadLoop.cpp
Normal file
84
Src/replicant/nu/win-x86/ThreadLoop.cpp
Normal file
@ -0,0 +1,84 @@
|
||||
#include "ThreadLoop.h"
|
||||
|
||||
lifo_t ThreadLoop::procedure_cache = {0,};
|
||||
lifo_t ThreadLoop::cache_bases= {0,};
|
||||
|
||||
#define PROCEDURE_CACHE_SEED 64
|
||||
ThreadLoop::ThreadLoop()
|
||||
{
|
||||
mpscq_init(&procedure_queue);
|
||||
procedure_notification = CreateSemaphore(0, 0, LONG_MAX, 0);
|
||||
kill_switch = CreateEvent(0, TRUE, FALSE, 0);
|
||||
}
|
||||
|
||||
void ThreadLoop::RefillCache()
|
||||
{
|
||||
threadloop_node_t *cache_seed = (threadloop_node_t *)malloc(PROCEDURE_CACHE_SEED*sizeof(threadloop_node_t));
|
||||
|
||||
if (cache_seed)
|
||||
{
|
||||
int i=PROCEDURE_CACHE_SEED;
|
||||
while (--i)
|
||||
{
|
||||
lifo_push(&procedure_cache, (queue_node_t *)&cache_seed[i]);
|
||||
}
|
||||
lifo_push(&cache_bases, (queue_node_t *)cache_seed);
|
||||
}
|
||||
else
|
||||
{
|
||||
Sleep(0); // yield and hope that someone else pops something off soon
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ThreadLoop::Run()
|
||||
{
|
||||
HANDLE events[] = {kill_switch, procedure_notification};
|
||||
while (WaitForMultipleObjects(2, events, FALSE, INFINITE) == WAIT_OBJECT_0 + 1)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
threadloop_node_t *apc = (threadloop_node_t *)mpscq_pop(&procedure_queue);
|
||||
if (apc == (threadloop_node_t *)1) /* special return value that indicates a busy list */
|
||||
{
|
||||
Sleep(0); // yield so that the thread that got pre-empted during push can finish
|
||||
}
|
||||
else
|
||||
{
|
||||
if (apc)
|
||||
{
|
||||
apc->func(apc->param1, apc->param2, apc->real_value);
|
||||
lifo_push(&procedure_cache, apc);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
threadloop_node_t *ThreadLoop::GetAPC()
|
||||
{
|
||||
threadloop_node_t *apc = 0;
|
||||
|
||||
do
|
||||
{
|
||||
apc = (threadloop_node_t *)lifo_pop(&procedure_cache);
|
||||
if (!apc)
|
||||
RefillCache();
|
||||
} while (!apc);
|
||||
return apc;
|
||||
}
|
||||
|
||||
void ThreadLoop::Schedule(threadloop_node_t *apc)
|
||||
{
|
||||
if (mpscq_push(&procedure_queue, apc) == 0)
|
||||
ReleaseSemaphore(procedure_notification, 1, 0);
|
||||
}
|
||||
|
||||
void ThreadLoop::Kill()
|
||||
{
|
||||
SetEvent(kill_switch);
|
||||
}
|
37
Src/replicant/nu/win-x86/ThreadLoop.h
Normal file
37
Src/replicant/nu/win-x86/ThreadLoop.h
Normal file
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#include "nu/lfmpscq.h"
|
||||
#include "nu/LockFreeLIFO.h"
|
||||
#include <windows.h>
|
||||
|
||||
struct threadloop_node_t : public queue_node_t
|
||||
{
|
||||
void (*func)(void *param1, void *param2, double real_value);
|
||||
|
||||
void *param1;
|
||||
void *param2;
|
||||
double real_value;
|
||||
};
|
||||
|
||||
class ThreadLoop
|
||||
{
|
||||
public:
|
||||
ThreadLoop();
|
||||
threadloop_node_t *GetAPC(); // returns a node for you to fill out
|
||||
void Schedule(threadloop_node_t *apc);
|
||||
void Run();
|
||||
void Kill();
|
||||
private:
|
||||
void RefillCache();
|
||||
|
||||
HANDLE procedure_notification;
|
||||
HANDLE kill_switch;
|
||||
mpscq_t procedure_queue;
|
||||
|
||||
/* Memory cache to be able to run APCs without having the memory manager lock
|
||||
we'll allocate 100 at a time (#defined by PROCEDURE_CACHE_SEED)
|
||||
and allocate new ones only if the cache is empty (which unfortunately will lock)
|
||||
cache_bases holds the pointers we've allocated (to free on destruction of this object)
|
||||
and procedure_cache holds the individual pointers */
|
||||
static lifo_t procedure_cache;
|
||||
static lifo_t cache_bases;
|
||||
};
|
121
Src/replicant/nu/win/MessageLoop.cpp
Normal file
121
Src/replicant/nu/win/MessageLoop.cpp
Normal file
@ -0,0 +1,121 @@
|
||||
#include "MessageLoop.h"
|
||||
#include <assert.h>
|
||||
|
||||
lifo_t nu::MessageLoop::message_cache = {0,};
|
||||
lifo_t nu::MessageLoop::cache_bases= {0,};
|
||||
|
||||
#define MESSAAGE_CACHE_SEED 64
|
||||
|
||||
typedef uint8_t message_data_t[64]; // ensure all messages are this size
|
||||
|
||||
nu::MessageLoop::MessageLoop()
|
||||
{
|
||||
mpscq_init(&message_queue);
|
||||
message_notification = CreateEvent(0, FALSE, FALSE, 0);
|
||||
}
|
||||
|
||||
nu::MessageLoop::~MessageLoop()
|
||||
{
|
||||
CloseHandle(message_notification);
|
||||
}
|
||||
|
||||
void nu::MessageLoop::RefillCache()
|
||||
{
|
||||
message_data_t *cache_seed = (message_data_t *)_aligned_malloc(MESSAAGE_CACHE_SEED*sizeof(message_data_t), 64);
|
||||
|
||||
if (cache_seed)
|
||||
{
|
||||
int i=MESSAAGE_CACHE_SEED;
|
||||
while (--i)
|
||||
{
|
||||
lifo_push(&message_cache, (queue_node_t *)&cache_seed[i]);
|
||||
}
|
||||
lifo_push(&cache_bases, (queue_node_t *)cache_seed);
|
||||
}
|
||||
else
|
||||
{
|
||||
Sleep(0); // yield and hope that someone else pops something off soon
|
||||
}
|
||||
}
|
||||
|
||||
nu::message_node_t *nu::MessageLoop::AllocateMessage()
|
||||
{
|
||||
message_node_t *apc = 0;
|
||||
|
||||
do
|
||||
{
|
||||
apc = (message_node_t *)lifo_pop(&message_cache);
|
||||
if (!apc)
|
||||
RefillCache();
|
||||
} while (!apc);
|
||||
return apc;
|
||||
}
|
||||
|
||||
void nu::MessageLoop::PostMessage(nu::message_node_t *message)
|
||||
{
|
||||
if (mpscq_push(&message_queue, message) == 0)
|
||||
SetEvent(message_notification);
|
||||
}
|
||||
|
||||
void nu::MessageLoop::FreeMessage(nu::message_node_t *message)
|
||||
{
|
||||
lifo_push(&message_cache, message);
|
||||
}
|
||||
|
||||
nu::message_node_t *nu::MessageLoop::GetMessage()
|
||||
{
|
||||
message_node_t *message = PeekMessage();
|
||||
if (message)
|
||||
{
|
||||
return message;
|
||||
}
|
||||
|
||||
while (WaitForSingleObject(message_notification, INFINITE) == WAIT_OBJECT_0)
|
||||
{
|
||||
message = PeekMessage();
|
||||
if (message)
|
||||
{
|
||||
return message;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
nu::message_node_t *nu::MessageLoop::PeekMessage()
|
||||
{
|
||||
for (;;) // loop because we need to handle 'busy' from the queue
|
||||
{
|
||||
message_node_t *message = (message_node_t *)mpscq_pop(&message_queue);
|
||||
if (message == (message_node_t *)1) /* special return value that indicates a busy list */
|
||||
{
|
||||
// benski> although it's tempting to return 0 here, doing so will mess up the Event logic
|
||||
Sleep(0); // yield so that the thread that got pre-empted during push can finish
|
||||
}
|
||||
else
|
||||
{
|
||||
if (message)
|
||||
{
|
||||
return message;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nu::message_node_t *nu::MessageLoop::PeekMessage(unsigned int milliseconds)
|
||||
{
|
||||
message_node_t *message = PeekMessage();
|
||||
if (message)
|
||||
return message;
|
||||
|
||||
if (WaitForSingleObject(message_notification, milliseconds) == WAIT_OBJECT_0)
|
||||
{
|
||||
message = PeekMessage();
|
||||
if (message)
|
||||
return message;
|
||||
}
|
||||
return 0;
|
||||
}
|
45
Src/replicant/nu/win/MessageLoop.h
Normal file
45
Src/replicant/nu/win/MessageLoop.h
Normal file
@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
#include "foundation/types.h"
|
||||
#include "nu/lfmpscq.h"
|
||||
#include "nu/LockFreeLIFO.h"
|
||||
#include <windows.h>
|
||||
|
||||
namespace nu
|
||||
{
|
||||
|
||||
/* you can inherit from message_node_t (or combine inside a struct)
|
||||
but make sure that your message isn't > 64 bytes */
|
||||
struct message_node_t : public queue_node_t
|
||||
{
|
||||
uint32_t message;
|
||||
};
|
||||
|
||||
class MessageLoop
|
||||
{
|
||||
public:
|
||||
MessageLoop();
|
||||
~MessageLoop();
|
||||
/* API for Message senders */
|
||||
message_node_t *AllocateMessage(); // returns a message for you to fill out
|
||||
void PostMessage(message_node_t *message);
|
||||
|
||||
/* API for Message receivers */
|
||||
void FreeMessage(message_node_t *message);
|
||||
message_node_t *GetMessage(); // waits forever
|
||||
message_node_t *PeekMessage();
|
||||
message_node_t *PeekMessage(unsigned int milliseconds);
|
||||
private:
|
||||
void RefillCache();
|
||||
|
||||
HANDLE message_notification;
|
||||
mpscq_t message_queue;
|
||||
|
||||
/* Memory cache to be able to run APCs without having the memory manager lock
|
||||
we'll allocate 100 at a time (#defined by MESSAGE_CACHE_SEED)
|
||||
and allocate new ones only if the cache is empty (which unfortunately will lock)
|
||||
cache_bases holds the pointers we've allocated (to free on destruction of this object)
|
||||
and message_cache holds the individual pointers */
|
||||
static lifo_t message_cache;
|
||||
static lifo_t cache_bases;
|
||||
};
|
||||
}
|
146
Src/replicant/nu/win/ThreadLoop.cpp
Normal file
146
Src/replicant/nu/win/ThreadLoop.cpp
Normal file
@ -0,0 +1,146 @@
|
||||
#include "ThreadLoop.h"
|
||||
#include <limits.h>
|
||||
|
||||
lifo_t ThreadLoop::procedure_cache = {0,};
|
||||
lifo_t ThreadLoop::cache_bases= {0,};
|
||||
|
||||
#define PROCEDURE_CACHE_SEED 64
|
||||
ThreadLoop::ThreadLoop()
|
||||
{
|
||||
mpscq_init(&procedure_queue);
|
||||
procedure_notification = CreateSemaphoreW(0, 0, LONG_MAX, 0);
|
||||
kill_switch = CreateEvent(0, TRUE, FALSE, 0);
|
||||
}
|
||||
|
||||
ThreadLoop::~ThreadLoop()
|
||||
{
|
||||
CloseHandle(procedure_notification);
|
||||
CloseHandle(kill_switch);
|
||||
}
|
||||
|
||||
void ThreadLoop::RefillCache()
|
||||
{
|
||||
threadloop_node_t *cache_seed = (threadloop_node_t *)malloc(PROCEDURE_CACHE_SEED*sizeof(threadloop_node_t));
|
||||
|
||||
if (cache_seed)
|
||||
{
|
||||
int i=PROCEDURE_CACHE_SEED;
|
||||
while (--i)
|
||||
{
|
||||
lifo_push(&procedure_cache, (queue_node_t *)&cache_seed[i]);
|
||||
}
|
||||
lifo_push(&cache_bases, (queue_node_t *)cache_seed);
|
||||
}
|
||||
else
|
||||
{
|
||||
Sleep(0); // yield and hope that someone else pops something off soon
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadLoop::Run()
|
||||
{
|
||||
HANDLE events[] = {kill_switch, procedure_notification};
|
||||
while (WaitForMultipleObjects(2, events, FALSE, INFINITE) == WAIT_OBJECT_0 + 1)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
threadloop_node_t *apc = (threadloop_node_t *)mpscq_pop(&procedure_queue);
|
||||
if (apc == (threadloop_node_t *)1) /* special return value that indicates a busy list */
|
||||
{
|
||||
Sleep(0); // yield so that the thread that got pre-empted during push can finish
|
||||
}
|
||||
else
|
||||
{
|
||||
if (apc)
|
||||
{
|
||||
apc->func(apc->param1, apc->param2, apc->real_value);
|
||||
lifo_push(&procedure_cache, apc);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadLoop::Step(unsigned int milliseconds)
|
||||
{
|
||||
HANDLE events[] = {kill_switch, procedure_notification};
|
||||
if (WaitForMultipleObjects(2, events, FALSE, milliseconds) == WAIT_OBJECT_0 + 1)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
threadloop_node_t *apc = (threadloop_node_t *)mpscq_pop(&procedure_queue);
|
||||
if (apc == (threadloop_node_t *)1) /* special return value that indicates a busy list */
|
||||
{
|
||||
Sleep(0); // yield so that the thread that got pre-empted during push can finish
|
||||
}
|
||||
else
|
||||
{
|
||||
if (apc)
|
||||
{
|
||||
apc->func(apc->param1, apc->param2, apc->real_value);
|
||||
lifo_push(&procedure_cache, apc);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadLoop::Step()
|
||||
{
|
||||
HANDLE events[] = {kill_switch, procedure_notification};
|
||||
if (WaitForMultipleObjects(2, events, FALSE, INFINITE) == WAIT_OBJECT_0 + 1)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
threadloop_node_t *apc = (threadloop_node_t *)mpscq_pop(&procedure_queue);
|
||||
if (apc == (threadloop_node_t *)1) /* special return value that indicates a busy list */
|
||||
{
|
||||
Sleep(0); // yield so that the thread that got pre-empted during push can finish
|
||||
}
|
||||
else
|
||||
{
|
||||
if (apc)
|
||||
{
|
||||
apc->func(apc->param1, apc->param2, apc->real_value);
|
||||
lifo_push(&procedure_cache, apc);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
threadloop_node_t *ThreadLoop::GetAPC()
|
||||
{
|
||||
threadloop_node_t *apc = 0;
|
||||
|
||||
do
|
||||
{
|
||||
apc = (threadloop_node_t *)lifo_pop(&procedure_cache);
|
||||
if (!apc)
|
||||
RefillCache();
|
||||
} while (!apc);
|
||||
return apc;
|
||||
}
|
||||
|
||||
void ThreadLoop::Schedule(threadloop_node_t *apc)
|
||||
{
|
||||
if (mpscq_push(&procedure_queue, apc) == 0)
|
||||
ReleaseSemaphore(procedure_notification, 1, 0);
|
||||
}
|
||||
|
||||
void ThreadLoop::Kill()
|
||||
{
|
||||
SetEvent(kill_switch);
|
||||
}
|
40
Src/replicant/nu/win/ThreadLoop.h
Normal file
40
Src/replicant/nu/win/ThreadLoop.h
Normal file
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
#include "nu/lfmpscq.h"
|
||||
#include "nu/LockFreeLIFO.h"
|
||||
#include <windows.h>
|
||||
|
||||
struct threadloop_node_t : public queue_node_t
|
||||
{
|
||||
void (*func)(void *param1, void *param2, double real_value);
|
||||
|
||||
void *param1;
|
||||
void *param2;
|
||||
double real_value;
|
||||
};
|
||||
|
||||
class ThreadLoop
|
||||
{
|
||||
public:
|
||||
ThreadLoop();
|
||||
~ThreadLoop();
|
||||
threadloop_node_t *GetAPC(); // returns a node for you to fill out
|
||||
void Schedule(threadloop_node_t *apc);
|
||||
void Step();
|
||||
void Step(unsigned int milliseconds);
|
||||
void Run();
|
||||
void Kill();
|
||||
private:
|
||||
void RefillCache();
|
||||
|
||||
HANDLE procedure_notification;
|
||||
HANDLE kill_switch;
|
||||
mpscq_t procedure_queue;
|
||||
|
||||
/* Memory cache to be able to run APCs without having the memory manager lock
|
||||
we'll allocate 100 at a time (#defined by PROCEDURE_CACHE_SEED)
|
||||
and allocate new ones only if the cache is empty (which unfortunately will lock)
|
||||
cache_bases holds the pointers we've allocated (to free on destruction of this object)
|
||||
and procedure_cache holds the individual pointers */
|
||||
static lifo_t procedure_cache;
|
||||
static lifo_t cache_bases;
|
||||
};
|
85
Src/replicant/nu/x86/ByteWriter.h
Normal file
85
Src/replicant/nu/x86/ByteWriter.h
Normal file
@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
#include "foundation/types.h"
|
||||
#include "foundation/endian.h"
|
||||
/* this is separated out to processor-specific types for a few reasons
|
||||
|
||||
1) Unaligned writes are fast/easy on x86, slow on some platforms (ARM, x64), very slow on a few (Itanium) and crash on others (PowerPC)
|
||||
2) ARM is very good at *ptr++, x86 is very good at ptr[offset]
|
||||
3) Endian issues
|
||||
*/
|
||||
typedef struct ByteWriter
|
||||
{
|
||||
uint8_t *data;
|
||||
size_t data_length;
|
||||
size_t offset;
|
||||
} ByteWriter, *nu_bytewriter_t;
|
||||
|
||||
/* --------- Construction & Utility --------- */
|
||||
#define BYTEWRITER_INIT(data, length) { (uint8_t *)data, length, 0 }
|
||||
|
||||
inline static uint32_t bw_bytes_written(nu_bytewriter_t bw)
|
||||
{
|
||||
return bw->offset;
|
||||
}
|
||||
|
||||
/* --------- Little Endian writers --------- */
|
||||
inline static void bytewriter_fourcc_string(nu_bytewriter_t bw, const char *fourcc)
|
||||
{
|
||||
bw->data[bw->offset] = fourcc[0];
|
||||
bw->data[bw->offset+1] = fourcc[1];
|
||||
bw->data[bw->offset+2] = fourcc[2];
|
||||
bw->data[bw->offset+3] = fourcc[3];
|
||||
bw->offset += 4;
|
||||
}
|
||||
|
||||
inline static void bytewriter_uint32_le(nu_bytewriter_t bw, uint32_t value)
|
||||
{
|
||||
*(uint32_t *)(&bw->data[bw->offset]) = value;
|
||||
bw->offset+=4;
|
||||
}
|
||||
|
||||
inline static void bytewriter_uint16_le(nu_bytewriter_t bw, uint16_t value)
|
||||
{
|
||||
*(uint16_t *)(&bw->data[bw->offset]) = value;
|
||||
bw->offset+=2;
|
||||
}
|
||||
|
||||
/* --------- Big Endian writers --------- */
|
||||
inline static void bytewriter_uint32_be(nu_bytewriter_t bw, uint32_t value)
|
||||
{
|
||||
*(uint32_t *)(&bw->data[bw->offset]) = _byteswap_ulong(value);
|
||||
bw->offset+=4;
|
||||
}
|
||||
|
||||
inline static void bytewriter_uint24_be(nu_bytewriter_t bw, uint32_t value)
|
||||
{
|
||||
bw->data[bw->offset] = (uint8_t)(value >> 16) & 0xFF;
|
||||
bw->data[bw->offset+1] = (uint8_t)(value >> 8) & 0xFF;
|
||||
bw->data[bw->offset+2] = (uint8_t)value & 0xFF;
|
||||
bw->offset+=3;
|
||||
}
|
||||
|
||||
inline static void bytewriter_uint16_le(nu_bytewriter_t bw, uint16_t value)
|
||||
{
|
||||
*(uint16_t *)(&bw->data[bw->offset]) = value;
|
||||
bw->offset+=2;
|
||||
}
|
||||
|
||||
/* --------- Neutral Endian writers --------- */
|
||||
inline static void bytewriter_uint32_zero(nu_bytewriter_t bw)
|
||||
{
|
||||
*(uint32_t *)(&bw->data[bw->offset]) = 0;
|
||||
bw->offset+=4;
|
||||
}
|
||||
|
||||
inline static void bytewriter_uint32_nzero(nu_bytewriter_t bw, uint32_t num_zeroes)
|
||||
{
|
||||
memset(bw->data, 0, num_zeroes*4);
|
||||
bw->offset+=num_zeroes*4;
|
||||
}
|
||||
|
||||
inline static void bytewriter_uint8(nu_bytewriter_t bw, uint8_t value)
|
||||
{
|
||||
*(uint8_t *)&bw->data[bw->offset] = value;
|
||||
bw->offset++;
|
||||
}
|
Reference in New Issue
Block a user