Initial community commit

This commit is contained in:
Jef
2024-09-24 14:54:57 +02:00
parent 537bcbc862
commit 20d28e80a5
16810 changed files with 4640254 additions and 2 deletions

View 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
View 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
View 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

View 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

View 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

View 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;
}

View 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;
};

View 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;
}

View 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

View 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);
}

View 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

View 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;
}

View 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

View 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;
};

View 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
}

View 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

View 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;
}

View 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;
};

View 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
View 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;
};
}

View 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;

View 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;
};

View 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;
}

View 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;
};

View 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;
};

View 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
View 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
View 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

View 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;
}

View 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

View 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;
}

View 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

View 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;
}

View 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
View 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
View 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
View 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>

View 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>

View 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

View 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

File diff suppressed because it is too large Load Diff

649
Src/replicant/nu/utf.c Normal file
View 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
View 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

View 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

View 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);
}

View 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;
};

View 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

View 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);
}

View 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

View 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);
}

View 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;
};

View 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;
}

View 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;
};
}

View 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);
}

View 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;
};

View 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++;
}