diff options
author | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
---|---|---|
committer | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
commit | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/replicant/nu | |
parent | 537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff) | |
download | winamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz |
Initial community commit
Diffstat (limited to 'Src/replicant/nu')
56 files changed, 9617 insertions, 0 deletions
diff --git a/Src/replicant/nu/AutoBuffer.h b/Src/replicant/nu/AutoBuffer.h new file mode 100644 index 00000000..a972f327 --- /dev/null +++ b/Src/replicant/nu/AutoBuffer.h @@ -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; +}; diff --git a/Src/replicant/nu/AutoChar.h b/Src/replicant/nu/AutoChar.h new file mode 100644 index 00000000..b89bfa31 --- /dev/null +++ b/Src/replicant/nu/AutoChar.h @@ -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
\ No newline at end of file diff --git a/Src/replicant/nu/AutoLock.h b/Src/replicant/nu/AutoLock.h new file mode 100644 index 00000000..02370cfc --- /dev/null +++ b/Src/replicant/nu/AutoLock.h @@ -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 diff --git a/Src/replicant/nu/AutoWide.h b/Src/replicant/nu/AutoWide.h new file mode 100644 index 00000000..7897aa99 --- /dev/null +++ b/Src/replicant/nu/AutoWide.h @@ -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 diff --git a/Src/replicant/nu/Benchmark.h b/Src/replicant/nu/Benchmark.h new file mode 100644 index 00000000..3fcd3b0b --- /dev/null +++ b/Src/replicant/nu/Benchmark.h @@ -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
\ No newline at end of file diff --git a/Src/replicant/nu/BitReader.cpp b/Src/replicant/nu/BitReader.cpp new file mode 100644 index 00000000..7f66f37d --- /dev/null +++ b/Src/replicant/nu/BitReader.cpp @@ -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; +} diff --git a/Src/replicant/nu/BitReader.h b/Src/replicant/nu/BitReader.h new file mode 100644 index 00000000..8460b30f --- /dev/null +++ b/Src/replicant/nu/BitReader.h @@ -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; +}; diff --git a/Src/replicant/nu/ByteReader.c b/Src/replicant/nu/ByteReader.c new file mode 100644 index 00000000..76266356 --- /dev/null +++ b/Src/replicant/nu/ByteReader.c @@ -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; +} diff --git a/Src/replicant/nu/ByteReader.h b/Src/replicant/nu/ByteReader.h new file mode 100644 index 00000000..31aa5d12 --- /dev/null +++ b/Src/replicant/nu/ByteReader.h @@ -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 diff --git a/Src/replicant/nu/ByteWriter.c b/Src/replicant/nu/ByteWriter.c new file mode 100644 index 00000000..ab375a3f --- /dev/null +++ b/Src/replicant/nu/ByteWriter.c @@ -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); +} diff --git a/Src/replicant/nu/ByteWriter.h b/Src/replicant/nu/ByteWriter.h new file mode 100644 index 00000000..309bf106 --- /dev/null +++ b/Src/replicant/nu/ByteWriter.h @@ -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 diff --git a/Src/replicant/nu/LockFreeFIFO.cpp b/Src/replicant/nu/LockFreeFIFO.cpp new file mode 100644 index 00000000..3731d7cd --- /dev/null +++ b/Src/replicant/nu/LockFreeFIFO.cpp @@ -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; +} diff --git a/Src/replicant/nu/LockFreeFIFO.h b/Src/replicant/nu/LockFreeFIFO.h new file mode 100644 index 00000000..598cfaeb --- /dev/null +++ b/Src/replicant/nu/LockFreeFIFO.h @@ -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
\ No newline at end of file diff --git a/Src/replicant/nu/LockFreeItem.h b/Src/replicant/nu/LockFreeItem.h new file mode 100644 index 00000000..09ccafe3 --- /dev/null +++ b/Src/replicant/nu/LockFreeItem.h @@ -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; +}; diff --git a/Src/replicant/nu/LockFreeLIFO.c b/Src/replicant/nu/LockFreeLIFO.c new file mode 100644 index 00000000..eb9c1eb1 --- /dev/null +++ b/Src/replicant/nu/LockFreeLIFO.c @@ -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 + } diff --git a/Src/replicant/nu/LockFreeLIFO.h b/Src/replicant/nu/LockFreeLIFO.h new file mode 100644 index 00000000..b96f0c43 --- /dev/null +++ b/Src/replicant/nu/LockFreeLIFO.h @@ -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 diff --git a/Src/replicant/nu/LockFreeRingBuffer.cpp b/Src/replicant/nu/LockFreeRingBuffer.cpp new file mode 100644 index 00000000..75edb56c --- /dev/null +++ b/Src/replicant/nu/LockFreeRingBuffer.cpp @@ -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; +} diff --git a/Src/replicant/nu/LockFreeRingBuffer.h b/Src/replicant/nu/LockFreeRingBuffer.h new file mode 100644 index 00000000..36514693 --- /dev/null +++ b/Src/replicant/nu/LockFreeRingBuffer.h @@ -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; +};
\ No newline at end of file diff --git a/Src/replicant/nu/MessageLoop.h b/Src/replicant/nu/MessageLoop.h new file mode 100644 index 00000000..5b7548d6 --- /dev/null +++ b/Src/replicant/nu/MessageLoop.h @@ -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 diff --git a/Src/replicant/nu/Pairs.h b/Src/replicant/nu/Pairs.h new file mode 100644 index 00000000..80438ba6 --- /dev/null +++ b/Src/replicant/nu/Pairs.h @@ -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; + }; +} diff --git a/Src/replicant/nu/ProgressTracker.cpp b/Src/replicant/nu/ProgressTracker.cpp new file mode 100644 index 00000000..e42cada3 --- /dev/null +++ b/Src/replicant/nu/ProgressTracker.cpp @@ -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; diff --git a/Src/replicant/nu/ProgressTracker.h b/Src/replicant/nu/ProgressTracker.h new file mode 100644 index 00000000..83f01802 --- /dev/null +++ b/Src/replicant/nu/ProgressTracker.h @@ -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; +}; diff --git a/Src/replicant/nu/RingBuffer.cpp b/Src/replicant/nu/RingBuffer.cpp new file mode 100644 index 00000000..dfc47ef1 --- /dev/null +++ b/Src/replicant/nu/RingBuffer.cpp @@ -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; +} diff --git a/Src/replicant/nu/RingBuffer.h b/Src/replicant/nu/RingBuffer.h new file mode 100644 index 00000000..bff147d1 --- /dev/null +++ b/Src/replicant/nu/RingBuffer.h @@ -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; +}; diff --git a/Src/replicant/nu/SafeSize.h b/Src/replicant/nu/SafeSize.h new file mode 100644 index 00000000..02c97a07 --- /dev/null +++ b/Src/replicant/nu/SafeSize.h @@ -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; +};
\ No newline at end of file diff --git a/Src/replicant/nu/ThreadLoop.h b/Src/replicant/nu/ThreadLoop.h new file mode 100644 index 00000000..b65b29ff --- /dev/null +++ b/Src/replicant/nu/ThreadLoop.h @@ -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 diff --git a/Src/replicant/nu/lfitem.c b/Src/replicant/nu/lfitem.c new file mode 100644 index 00000000..4ece0251 --- /dev/null +++ b/Src/replicant/nu/lfitem.c @@ -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; +}
\ No newline at end of file diff --git a/Src/replicant/nu/lfitem.h b/Src/replicant/nu/lfitem.h new file mode 100644 index 00000000..7bb8a9d3 --- /dev/null +++ b/Src/replicant/nu/lfitem.h @@ -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 diff --git a/Src/replicant/nu/lfmpscq.c b/Src/replicant/nu/lfmpscq.c new file mode 100644 index 00000000..0f7efa73 --- /dev/null +++ b/Src/replicant/nu/lfmpscq.c @@ -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; +} diff --git a/Src/replicant/nu/lfmpscq.h b/Src/replicant/nu/lfmpscq.h new file mode 100644 index 00000000..c06f3331 --- /dev/null +++ b/Src/replicant/nu/lfmpscq.h @@ -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 diff --git a/Src/replicant/nu/lfringbuffer.c b/Src/replicant/nu/lfringbuffer.c new file mode 100644 index 00000000..4c38575b --- /dev/null +++ b/Src/replicant/nu/lfringbuffer.c @@ -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; +} diff --git a/Src/replicant/nu/lfringbuffer.h b/Src/replicant/nu/lfringbuffer.h new file mode 100644 index 00000000..03b3804b --- /dev/null +++ b/Src/replicant/nu/lfringbuffer.h @@ -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 diff --git a/Src/replicant/nu/nodelist.c b/Src/replicant/nu/nodelist.c new file mode 100644 index 00000000..f1c21ea4 --- /dev/null +++ b/Src/replicant/nu/nodelist.c @@ -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; +} diff --git a/Src/replicant/nu/nodelist.h b/Src/replicant/nu/nodelist.h new file mode 100644 index 00000000..5a7f1ec1 --- /dev/null +++ b/Src/replicant/nu/nodelist.h @@ -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 diff --git a/Src/replicant/nu/ns_wc.h b/Src/replicant/nu/ns_wc.h new file mode 100644 index 00000000..0520f7da --- /dev/null +++ b/Src/replicant/nu/ns_wc.h @@ -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
\ No newline at end of file diff --git a/Src/replicant/nu/nu.sln b/Src/replicant/nu/nu.sln new file mode 100644 index 00000000..f7b3dc6f --- /dev/null +++ b/Src/replicant/nu/nu.sln @@ -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 diff --git a/Src/replicant/nu/nu.vcxproj b/Src/replicant/nu/nu.vcxproj new file mode 100644 index 00000000..25e634f8 --- /dev/null +++ b/Src/replicant/nu/nu.vcxproj @@ -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>
\ No newline at end of file diff --git a/Src/replicant/nu/nu.vcxproj.filters b/Src/replicant/nu/nu.vcxproj.filters new file mode 100644 index 00000000..cdb52bfc --- /dev/null +++ b/Src/replicant/nu/nu.vcxproj.filters @@ -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>
\ No newline at end of file diff --git a/Src/replicant/nu/precomp.h b/Src/replicant/nu/precomp.h new file mode 100644 index 00000000..f19fd91a --- /dev/null +++ b/Src/replicant/nu/precomp.h @@ -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 diff --git a/Src/replicant/nu/queue_node.h b/Src/replicant/nu/queue_node.h new file mode 100644 index 00000000..88478070 --- /dev/null +++ b/Src/replicant/nu/queue_node.h @@ -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 diff --git a/Src/replicant/nu/strsafe.h b/Src/replicant/nu/strsafe.h new file mode 100644 index 00000000..3c94fe40 --- /dev/null +++ b/Src/replicant/nu/strsafe.h @@ -0,0 +1,4497 @@ +/****************************************************************** +* * +* strsafe.h -- This module defines safer C library string * +* routine replacements. These are meant to make C * +* a bit more safe in reference to security and * +* robustness * +* * +* Copyright (c) Microsoft Corp. All rights reserved. * +* Ported to Unix by Ben Allison - Nullsoft, Inc. * +* * +******************************************************************/ +#pragma once +#ifdef _WIN32 +#include <strsafe.h> +#else +#pragma once + +#include <stdio.h> // for _vsnprintf, _vsnwprintf, getc, getwc +#include <string.h> // for memset +#include <stdarg.h> // for va_start, etc. +#include <wchar.h> + + +#include <stdint.h> + +#ifndef _HRESULT_DEFINED +#define _HRESULT_DEFINED +typedef long HRESULT; +#endif // !_HRESULT_DEFINED + +#ifndef SUCCEEDED +#define SUCCEEDED(hr) ((HRESULT)(hr) >= 0) +#endif + +#ifndef FAILED +#define FAILED(hr) ((HRESULT)(hr) < 0) +#endif + +#ifndef S_OK +#define S_OK ((HRESULT)0x00000000L) +#endif + +#ifdef __cplusplus +#define _STRSAFE_EXTERN_C extern "C" +#else +#define _STRSAFE_EXTERN_C extern +#endif + +// If you do not want to use these functions inline (and instead want to link w/ strsafe.lib), then +// #define STRSAFE_LIB before including this header file. +#ifndef _WIN32 +#define __inline inline +#define __stdcall +#endif + +#if defined(STRSAFE_LIB) +#define STRSAFEAPI _STRSAFE_EXTERN_C HRESULT __stdcall +#pragma comment(lib, "./strsafe.lib") +#elif defined(STRSAFE_LIB_IMPL) +#define STRSAFEAPI _STRSAFE_EXTERN_C __declspec(dllexport) HRESULT __stdcall +#else +#define STRSAFEAPI __inline HRESULT __stdcall +#define STRSAFE_INLINE +#endif + +// Some functions always run inline because they use stdin and we want to avoid building multiple +// versions of strsafe lib depending on if you use msvcrt, libcmt, etc. +#define STRSAFE_INLINE_API __inline HRESULT __stdcall + +// The user can request no "Cb" or no "Cch" fuctions, but not both! +#if defined(STRSAFE_NO_CB_FUNCTIONS) && defined(STRSAFE_NO_CCH_FUNCTIONS) +#error cannot specify both STRSAFE_NO_CB_FUNCTIONS and STRSAFE_NO_CCH_FUNCTIONS !! +#endif + +// This should only be defined when we are building strsafe.lib +#ifdef STRSAFE_LIB_IMPL +#define STRSAFE_INLINE +#endif + + +// If both strsafe.h and ntstrsafe.h are included, only use definitions from one. +#ifndef _NTSTRSAFE_H_INCLUDED_ + +#define STRSAFE_MAX_CCH 2147483647 // max # of characters we support (same as INT_MAX) + +// Flags for controling the Ex functions +// +// STRSAFE_FILL_BYTE(0xFF) 0x000000FF // bottom byte specifies fill pattern +#define STRSAFE_IGNORE_NULLS 0x00000100 // treat null as TEXT("") -- don't fault on NULL buffers +#define STRSAFE_FILL_BEHIND_NULL 0x00000200 // fill in extra space behind the null terminator +#define STRSAFE_FILL_ON_FAILURE 0x00000400 // on failure, overwrite pszDest with fill pattern and null terminate it +#define STRSAFE_NULL_ON_FAILURE 0x00000800 // on failure, set *pszDest = TEXT('\0') +#define STRSAFE_NO_TRUNCATION 0x00001000 // instead of returning a truncated result, copy/append nothing to pszDest and null terminate it + +#define STRSAFE_VALID_FLAGS (0x000000FF | STRSAFE_IGNORE_NULLS | STRSAFE_FILL_BEHIND_NULL | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE | STRSAFE_NO_TRUNCATION) + +// helper macro to set the fill character and specify buffer filling +#define STRSAFE_FILL_BYTE(x) ((unsigned long)((x & 0x000000FF) | STRSAFE_FILL_BEHIND_NULL)) +#define STRSAFE_FAILURE_BYTE(x) ((unsigned long)((x & 0x000000FF) | STRSAFE_FILL_ON_FAILURE)) + +#define STRSAFE_GET_FILL_PATTERN(dwFlags) ((int)(dwFlags & 0x000000FF)) + +#endif // _NTSTRSAFE_H_INCLUDED_ + +// STRSAFE error return codes +// +#define STRSAFE_E_INSUFFICIENT_BUFFER ((HRESULT)0x8007007AL) // 0x7A = 122L = ERROR_INSUFFICIENT_BUFFER +#define STRSAFE_E_INVALID_PARAMETER ((HRESULT)0x80070057L) // 0x57 = 87L = ERROR_INVALID_PARAMETER +#define STRSAFE_E_END_OF_FILE ((HRESULT)0x80070026L) // 0x26 = 38L = ERROR_HANDLE_EOF + +// prototypes for the worker functions +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCopyWorkerA(char* pszDest, size_t cchDest, const char* pszSrc); +STRSAFEAPI StringCopyExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags); +STRSAFEAPI StringCopyNWorkerA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchSrc); +STRSAFEAPI StringCopyNExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, size_t cchSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags); +STRSAFEAPI StringCatWorkerA(char* pszDest, size_t cchDest, const char* pszSrc); +STRSAFEAPI StringCatExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags); +STRSAFEAPI StringCatNWorkerA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchMaxAppend); +STRSAFEAPI StringCatNExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, size_t cchMaxAppend, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags); +STRSAFEAPI StringVPrintfWorkerA(char* pszDest, size_t cchDest, const char* pszFormat, va_list argList); +STRSAFEAPI StringVPrintfExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const char* pszFormat, va_list argList); +STRSAFEAPI StringLengthWorkerA(const char* psz, size_t cchMax, size_t* pcch); +#endif // STRSAFE_INLINE + +#ifndef STRSAFE_LIB_IMPL +// these functions are always inline +STRSAFE_INLINE_API StringGetsExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags); +#endif + +#ifdef _NTSTRSAFE_H_INCLUDED_ +#pragma warning(push) +#pragma warning(disable : 4995) +#endif // _NTSTRSAFE_H_INCLUDED_ + + +#ifndef STRSAFE_NO_CCH_FUNCTIONS +/*++ + +STDAPI +StringCchCopy( + OUT LPTSTR pszDest, + IN size_t cchDest, + IN LPCTSTR pszSrc + ); + +Routine Description: + + This routine is a safer version of the C built-in function 'strcpy'. + The size of the destination buffer (in characters) is a parameter and + this function will not write past the end of this buffer and it will + ALWAYS null terminate the destination buffer (unless it is zero length). + + This routine is not a replacement for strncpy. That function will pad the + destination string with extra null termination characters if the count is + greater than the length of the source string, and it will fail to null + terminate the destination string if the source string length is greater + than or equal to the count. You can not blindly use this instead of strncpy: + it is common for code to use it to "patch" strings and you would introduce + errors if the code started null terminating in the middle of the string. + + This function returns a hresult, and not a pointer. It returns + S_OK if the string was copied without truncation and null terminated, + otherwise it will return a failure code. In failure cases as much of + pszSrc will be copied to pszDest as possible, and pszDest will be null + terminated. + +Arguments: + + pszDest - destination string + + cchDest - size of destination buffer in characters. + length must be = (_tcslen(src) + 1) to hold all of the + source including the null terminator + + pszSrc - source string which must be null terminated + +Notes: + Behavior is undefined if source and destination strings overlap. + + pszDest and pszSrc should not be NULL. See StringCchCopyEx if you require + the handling of NULL values. + +Return Value: + + S_OK - if there was source data and it was all copied and the + resultant dest string was null terminated + + failure - you can use the macro HRESULT_CODE() to get a win32 + error code for all hresult failure cases + + STRSAFE_E_INSUFFICIENT_BUFFER / + HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER + - this return value is an indication that the copy + operation failed due to insufficient space. When this + error occurs, the destination buffer is modified to + contain a truncated version of the ideal result and is + null terminated. This is useful for situations where + truncation is ok + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function. + +--*/ + +STRSAFEAPI StringCchCopyA(char* pszDest, size_t cchDest, const char* pszSrc); +#define StringCchCopy StringCchCopyA + +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCchCopyA(char* pszDest, size_t cchDest, const char* pszSrc) +{ + HRESULT hr; + + if (cchDest > STRSAFE_MAX_CCH) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + hr = StringCopyWorkerA(pszDest, cchDest, pszSrc); + } + + return hr; +} + +#endif // STRSAFE_INLINE +#endif // !STRSAFE_NO_CCH_FUNCTIONS + + +#ifndef STRSAFE_NO_CB_FUNCTIONS +/*++ + +STDAPI +StringCbCopy( + OUT LPTSTR pszDest, + IN size_t cbDest, + IN LPCTSTR pszSrc + ); + +Routine Description: + + This routine is a safer version of the C built-in function 'strcpy'. + The size of the destination buffer (in bytes) is a parameter and this + function will not write past the end of this buffer and it will ALWAYS + null terminate the destination buffer (unless it is zero length). + + This routine is not a replacement for strncpy. That function will pad the + destination string with extra null termination characters if the count is + greater than the length of the source string, and it will fail to null + terminate the destination string if the source string length is greater + than or equal to the count. You can not blindly use this instead of strncpy: + it is common for code to use it to "patch" strings and you would introduce + errors if the code started null terminating in the middle of the string. + + This function returns a hresult, and not a pointer. It returns + S_OK if the string was copied without truncation and null terminated, + otherwise it will return a failure code. In failure cases as much of pszSrc + will be copied to pszDest as possible, and pszDest will be null terminated. + +Arguments: + + pszDest - destination string + + cbDest - size of destination buffer in bytes. + length must be = ((_tcslen(src) + 1) * sizeof(TCHAR)) to + hold all of the source including the null terminator + + pszSrc - source string which must be null terminated + +Notes: + Behavior is undefined if source and destination strings overlap. + + pszDest and pszSrc should not be NULL. See StringCbCopyEx if you require + the handling of NULL values. + +Return Value: + + S_OK - if there was source data and it was all copied and the + resultant dest string was null terminated + + failure - you can use the macro HRESULT_CODE() to get a win32 + error code for all hresult failure cases + + STRSAFE_E_INSUFFICIENT_BUFFER / + HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER + - this return value is an indication that the copy + operation failed due to insufficient space. When this + error occurs, the destination buffer is modified to + contain a truncated version of the ideal result and is + null terminated. This is useful for situations where + truncation is ok + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function. + +--*/ + +STRSAFEAPI StringCbCopyA(char* pszDest, size_t cbDest, const char* pszSrc); +#define StringCbCopy StringCbCopyA + +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCbCopyA(char* pszDest, size_t cbDest, const char* pszSrc) +{ + HRESULT hr; + size_t cchDest; + + // convert to count of characters + cchDest = cbDest / sizeof(char); + + if (cchDest > STRSAFE_MAX_CCH) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + hr = StringCopyWorkerA(pszDest, cchDest, pszSrc); + } + + return hr; +} + +#endif // STRSAFE_INLINE +#endif // !STRSAFE_NO_CB_FUNCTIONS + + +#ifndef STRSAFE_NO_CCH_FUNCTIONS +/*++ + +STDAPI +StringCchCopyEx( + OUT LPTSTR pszDest OPTIONAL, + IN size_t cchDest, + IN LPCTSTR pszSrc OPTIONAL, + OUT LPTSTR* ppszDestEnd OPTIONAL, + OUT size_t* pcchRemaining OPTIONAL, + IN DWORD dwFlags + ); + +Routine Description: + + This routine is a safer version of the C built-in function 'strcpy' with + some additional parameters. In addition to functionality provided by + StringCchCopy, this routine also returns a pointer to the end of the + destination string and the number of characters left in the destination string + including the null terminator. The flags parameter allows additional controls. + +Arguments: + + pszDest - destination string + + cchDest - size of destination buffer in characters. + length must be = (_tcslen(pszSrc) + 1) to hold all of + the source including the null terminator + + pszSrc - source string which must be null terminated + + ppszDestEnd - if ppszDestEnd is non-null, the function will return a + pointer to the end of the destination string. If the + function copied any data, the result will point to the + null termination character + + pcchRemaining - if pcchRemaining is non-null, the function will return the + number of characters left in the destination string, + including the null terminator + + dwFlags - controls some details of the string copy: + + STRSAFE_FILL_BEHIND_NULL + if the function succeeds, the low byte of dwFlags will be + used to fill the uninitialize part of destination buffer + behind the null terminator + + STRSAFE_IGNORE_NULLS + treat NULL string pointers like empty strings (TEXT("")). + this flag is useful for emulating functions like lstrcpy + + STRSAFE_FILL_ON_FAILURE + if the function fails, the low byte of dwFlags will be + used to fill all of the destination buffer, and it will + be null terminated. This will overwrite any truncated + string returned when the failure is + STRSAFE_E_INSUFFICIENT_BUFFER + + STRSAFE_NO_TRUNCATION / + STRSAFE_NULL_ON_FAILURE + if the function fails, the destination buffer will be set + to the empty string. This will overwrite any truncated string + returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER. + +Notes: + Behavior is undefined if source and destination strings overlap. + + pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag + is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc + may be NULL. An error may still be returned even though NULLS are ignored + due to insufficient space. + +Return Value: + + S_OK - if there was source data and it was all copied and the + resultant dest string was null terminated + + failure - you can use the macro HRESULT_CODE() to get a win32 + error code for all hresult failure cases + + STRSAFE_E_INSUFFICIENT_BUFFER / + HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER + - this return value is an indication that the copy + operation failed due to insufficient space. When this + error occurs, the destination buffer is modified to + contain a truncated version of the ideal result and is + null terminated. This is useful for situations where + truncation is ok. + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function + +--*/ + +STRSAFEAPI StringCchCopyExA(char* pszDest, size_t cchDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags); +#define StringCchCopyEx StringCchCopyExA + +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCchCopyExA(char* pszDest, size_t cchDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags) +{ + HRESULT hr; + + if (cchDest > STRSAFE_MAX_CCH) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + size_t cbDest; + + // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1 + cbDest = cchDest * sizeof(char); + + hr = StringCopyExWorkerA(pszDest, cchDest, cbDest, pszSrc, ppszDestEnd, pcchRemaining, dwFlags); + } + + return hr; +} + +#endif // STRSAFE_INLINE +#endif // !STRSAFE_NO_CCH_FUNCTIONS + + +#ifndef STRSAFE_NO_CB_FUNCTIONS +/*++ + +STDAPI +StringCbCopyEx( + OUT LPTSTR pszDest OPTIONAL, + IN size_t cbDest, + IN LPCTSTR pszSrc OPTIONAL, + OUT LPTSTR* ppszDestEnd OPTIONAL, + OUT size_t* pcbRemaining OPTIONAL, + IN DWORD dwFlags + ); + +Routine Description: + + This routine is a safer version of the C built-in function 'strcpy' with + some additional parameters. In addition to functionality provided by + StringCbCopy, this routine also returns a pointer to the end of the + destination string and the number of bytes left in the destination string + including the null terminator. The flags parameter allows additional controls. + +Arguments: + + pszDest - destination string + + cbDest - size of destination buffer in bytes. + length must be ((_tcslen(pszSrc) + 1) * sizeof(TCHAR)) to + hold all of the source including the null terminator + + pszSrc - source string which must be null terminated + + ppszDestEnd - if ppszDestEnd is non-null, the function will return a + pointer to the end of the destination string. If the + function copied any data, the result will point to the + null termination character + + pcbRemaining - pcbRemaining is non-null,the function will return the + number of bytes left in the destination string, + including the null terminator + + dwFlags - controls some details of the string copy: + + STRSAFE_FILL_BEHIND_NULL + if the function succeeds, the low byte of dwFlags will be + used to fill the uninitialize part of destination buffer + behind the null terminator + + STRSAFE_IGNORE_NULLS + treat NULL string pointers like empty strings (TEXT("")). + this flag is useful for emulating functions like lstrcpy + + STRSAFE_FILL_ON_FAILURE + if the function fails, the low byte of dwFlags will be + used to fill all of the destination buffer, and it will + be null terminated. This will overwrite any truncated + string returned when the failure is + STRSAFE_E_INSUFFICIENT_BUFFER + + STRSAFE_NO_TRUNCATION / + STRSAFE_NULL_ON_FAILURE + if the function fails, the destination buffer will be set + to the empty string. This will overwrite any truncated string + returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER. + +Notes: + Behavior is undefined if source and destination strings overlap. + + pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag + is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc + may be NULL. An error may still be returned even though NULLS are ignored + due to insufficient space. + +Return Value: + + S_OK - if there was source data and it was all copied and the + resultant dest string was null terminated + + failure - you can use the macro HRESULT_CODE() to get a win32 + error code for all hresult failure cases + + STRSAFE_E_INSUFFICIENT_BUFFER / + HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER + - this return value is an indication that the copy + operation failed due to insufficient space. When this + error occurs, the destination buffer is modified to + contain a truncated version of the ideal result and is + null terminated. This is useful for situations where + truncation is ok. + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function + +--*/ + +STRSAFEAPI StringCbCopyExA(char* pszDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags); +#define StringCbCopyEx StringCbCopyExA + +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCbCopyExA(char* pszDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags) +{ + HRESULT hr; + size_t cchDest; + size_t cchRemaining = 0; + + cchDest = cbDest / sizeof(char); + + if (cchDest > STRSAFE_MAX_CCH) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + hr = StringCopyExWorkerA(pszDest, cchDest, cbDest, pszSrc, ppszDestEnd, &cchRemaining, dwFlags); + } + + if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER)) + { + if (pcbRemaining) + { + // safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1 + *pcbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char)); + } + } + + return hr; +} + +#endif // STRSAFE_INLINE +#endif // !STRSAFE_NO_CB_FUNCTIONS + + +#ifndef STRSAFE_NO_CCH_FUNCTIONS +/*++ + +STDAPI +StringCchCopyN( + OUT LPTSTR pszDest, + IN size_t cchDest, + IN LPCTSTR pszSrc, + IN size_t cchSrc + ); + +Routine Description: + + This routine is a safer version of the C built-in function 'strncpy'. + The size of the destination buffer (in characters) is a parameter and + this function will not write past the end of this buffer and it will + ALWAYS null terminate the destination buffer (unless it is zero length). + + This routine is meant as a replacement for strncpy, but it does behave + differently. This function will not pad the destination buffer with extra + null termination characters if cchSrc is greater than the length of pszSrc. + + This function returns a hresult, and not a pointer. It returns + S_OK if the entire string or the first cchSrc characters were copied + without truncation and the resultant destination string was null terminated, + otherwise it will return a failure code. In failure cases as much of pszSrc + will be copied to pszDest as possible, and pszDest will be null terminated. + +Arguments: + + pszDest - destination string + + cchDest - size of destination buffer in characters. + length must be = (_tcslen(src) + 1) to hold all of the + source including the null terminator + + pszSrc - source string + + cchSrc - maximum number of characters to copy from source string, + not including the null terminator. + +Notes: + Behavior is undefined if source and destination strings overlap. + + pszDest and pszSrc should not be NULL. See StringCchCopyNEx if you require + the handling of NULL values. + +Return Value: + + S_OK - if there was source data and it was all copied and the + resultant dest string was null terminated + + failure - you can use the macro HRESULT_CODE() to get a win32 + error code for all hresult failure cases + + STRSAFE_E_INSUFFICIENT_BUFFER / + HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER + - this return value is an indication that the copy + operation failed due to insufficient space. When this + error occurs, the destination buffer is modified to + contain a truncated version of the ideal result and is + null terminated. This is useful for situations where + truncation is ok + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function. + +--*/ + +STRSAFEAPI StringCchCopyNA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchSrc); +#define StringCchCopyN StringCchCopyNA + +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCchCopyNA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchSrc) +{ + HRESULT hr; + + if ((cchDest > STRSAFE_MAX_CCH) || + (cchSrc > STRSAFE_MAX_CCH)) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + hr = StringCopyNWorkerA(pszDest, cchDest, pszSrc, cchSrc); + } + + return hr; +} + +#endif // STRSAFE_INLINE +#endif // !STRSAFE_NO_CCH_FUNCTIONS + + +#ifndef STRSAFE_NO_CB_FUNCTIONS +/*++ + +STDAPI +StringCbCopyN( + OUT LPTSTR pszDest, + IN size_t cbDest, + IN LPCTSTR pszSrc, + IN size_t cbSrc + ); + +Routine Description: + + This routine is a safer version of the C built-in function 'strncpy'. + The size of the destination buffer (in bytes) is a parameter and this + function will not write past the end of this buffer and it will ALWAYS + null terminate the destination buffer (unless it is zero length). + + This routine is meant as a replacement for strncpy, but it does behave + differently. This function will not pad the destination buffer with extra + null termination characters if cbSrc is greater than the size of pszSrc. + + This function returns a hresult, and not a pointer. It returns + S_OK if the entire string or the first cbSrc characters were + copied without truncation and the resultant destination string was null + terminated, otherwise it will return a failure code. In failure cases as + much of pszSrc will be copied to pszDest as possible, and pszDest will be + null terminated. + +Arguments: + + pszDest - destination string + + cbDest - size of destination buffer in bytes. + length must be = ((_tcslen(src) + 1) * sizeof(TCHAR)) to + hold all of the source including the null terminator + + pszSrc - source string + + cbSrc - maximum number of bytes to copy from source string, + not including the null terminator. + +Notes: + Behavior is undefined if source and destination strings overlap. + + pszDest and pszSrc should not be NULL. See StringCbCopyEx if you require + the handling of NULL values. + +Return Value: + + S_OK - if there was source data and it was all copied and the + resultant dest string was null terminated + + failure - you can use the macro HRESULT_CODE() to get a win32 + error code for all hresult failure cases + + STRSAFE_E_INSUFFICIENT_BUFFER / + HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER + - this return value is an indication that the copy + operation failed due to insufficient space. When this + error occurs, the destination buffer is modified to + contain a truncated version of the ideal result and is + null terminated. This is useful for situations where + truncation is ok + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function. + +--*/ + +STRSAFEAPI StringCbCopyNA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbSrc); +#define StringCbCopyN StringCbCopyNA + +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCbCopyNA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbSrc) +{ + HRESULT hr; + size_t cchDest; + size_t cchSrc; + + // convert to count of characters + cchDest = cbDest / sizeof(char); + cchSrc = cbSrc / sizeof(char); + + if ((cchDest > STRSAFE_MAX_CCH) || + (cchSrc > STRSAFE_MAX_CCH)) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + hr = StringCopyNWorkerA(pszDest, cchDest, pszSrc, cchSrc); + } + + return hr; +} + +#endif // STRSAFE_INLINE +#endif // !STRSAFE_NO_CB_FUNCTIONS + + +#ifndef STRSAFE_NO_CCH_FUNCTIONS +/*++ + +STDAPI +StringCchCopyNEx( + OUT LPTSTR pszDest OPTIONAL, + IN size_t cchDest, + IN LPCTSTR pszSrc OPTIONAL, + IN size_t cchSrc, + OUT LPTSTR* ppszDestEnd OPTIONAL, + OUT size_t* pcchRemaining OPTIONAL, + IN DWORD dwFlags + ); + +Routine Description: + + This routine is a safer version of the C built-in function 'strncpy' with + some additional parameters. In addition to functionality provided by + StringCchCopyN, this routine also returns a pointer to the end of the + destination string and the number of characters left in the destination + string including the null terminator. The flags parameter allows + additional controls. + + This routine is meant as a replacement for strncpy, but it does behave + differently. This function will not pad the destination buffer with extra + null termination characters if cchSrc is greater than the length of pszSrc. + +Arguments: + + pszDest - destination string + + cchDest - size of destination buffer in characters. + length must be = (_tcslen(pszSrc) + 1) to hold all of + the source including the null terminator + + pszSrc - source string + + cchSrc - maximum number of characters to copy from the source + string + + ppszDestEnd - if ppszDestEnd is non-null, the function will return a + pointer to the end of the destination string. If the + function copied any data, the result will point to the + null termination character + + pcchRemaining - if pcchRemaining is non-null, the function will return the + number of characters left in the destination string, + including the null terminator + + dwFlags - controls some details of the string copy: + + STRSAFE_FILL_BEHIND_NULL + if the function succeeds, the low byte of dwFlags will be + used to fill the uninitialize part of destination buffer + behind the null terminator + + STRSAFE_IGNORE_NULLS + treat NULL string pointers like empty strings (TEXT("")). + this flag is useful for emulating functions like lstrcpy + + STRSAFE_FILL_ON_FAILURE + if the function fails, the low byte of dwFlags will be + used to fill all of the destination buffer, and it will + be null terminated. This will overwrite any truncated + string returned when the failure is + STRSAFE_E_INSUFFICIENT_BUFFER + + STRSAFE_NO_TRUNCATION / + STRSAFE_NULL_ON_FAILURE + if the function fails, the destination buffer will be set + to the empty string. This will overwrite any truncated string + returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER. + +Notes: + Behavior is undefined if source and destination strings overlap. + + pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag + is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc + may be NULL. An error may still be returned even though NULLS are ignored + due to insufficient space. + +Return Value: + + S_OK - if there was source data and it was all copied and the + resultant dest string was null terminated + + failure - you can use the macro HRESULT_CODE() to get a win32 + error code for all hresult failure cases + + STRSAFE_E_INSUFFICIENT_BUFFER / + HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER + - this return value is an indication that the copy + operation failed due to insufficient space. When this + error occurs, the destination buffer is modified to + contain a truncated version of the ideal result and is + null terminated. This is useful for situations where + truncation is ok. + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function + +--*/ + +STRSAFEAPI StringCchCopyNExA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags); +#define StringCchCopyNEx StringCchCopyNExA + +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCchCopyNExA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags) +{ + HRESULT hr; + + if ((cchDest > STRSAFE_MAX_CCH) || + (cchSrc > STRSAFE_MAX_CCH)) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + size_t cbDest; + + // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1 + cbDest = cchDest * sizeof(char); + + hr = StringCopyNExWorkerA(pszDest, cchDest, cbDest, pszSrc, cchSrc, ppszDestEnd, pcchRemaining, dwFlags); + } + + return hr; +} + +#endif // STRSAFE_INLINE +#endif // !STRSAFE_NO_CCH_FUNCTIONS + + +#ifndef STRSAFE_NO_CB_FUNCTIONS +/*++ + +STDAPI +StringCbCopyNEx( + OUT LPTSTR pszDest OPTIONAL, + IN size_t cbDest, + IN LPCTSTR pszSrc OPTIONAL, + IN size_t cbSrc, + OUT LPTSTR* ppszDestEnd OPTIONAL, + OUT size_t* pcbRemaining OPTIONAL, + IN DWORD dwFlags + ); + +Routine Description: + + This routine is a safer version of the C built-in function 'strncpy' with + some additional parameters. In addition to functionality provided by + StringCbCopyN, this routine also returns a pointer to the end of the + destination string and the number of bytes left in the destination string + including the null terminator. The flags parameter allows additional controls. + + This routine is meant as a replacement for strncpy, but it does behave + differently. This function will not pad the destination buffer with extra + null termination characters if cbSrc is greater than the size of pszSrc. + +Arguments: + + pszDest - destination string + + cbDest - size of destination buffer in bytes. + length must be ((_tcslen(pszSrc) + 1) * sizeof(TCHAR)) to + hold all of the source including the null terminator + + pszSrc - source string + + cbSrc - maximum number of bytes to copy from source string + + ppszDestEnd - if ppszDestEnd is non-null, the function will return a + pointer to the end of the destination string. If the + function copied any data, the result will point to the + null termination character + + pcbRemaining - pcbRemaining is non-null,the function will return the + number of bytes left in the destination string, + including the null terminator + + dwFlags - controls some details of the string copy: + + STRSAFE_FILL_BEHIND_NULL + if the function succeeds, the low byte of dwFlags will be + used to fill the uninitialize part of destination buffer + behind the null terminator + + STRSAFE_IGNORE_NULLS + treat NULL string pointers like empty strings (TEXT("")). + this flag is useful for emulating functions like lstrcpy + + STRSAFE_FILL_ON_FAILURE + if the function fails, the low byte of dwFlags will be + used to fill all of the destination buffer, and it will + be null terminated. This will overwrite any truncated + string returned when the failure is + STRSAFE_E_INSUFFICIENT_BUFFER + + STRSAFE_NO_TRUNCATION / + STRSAFE_NULL_ON_FAILURE + if the function fails, the destination buffer will be set + to the empty string. This will overwrite any truncated string + returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER. + +Notes: + Behavior is undefined if source and destination strings overlap. + + pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag + is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc + may be NULL. An error may still be returned even though NULLS are ignored + due to insufficient space. + +Return Value: + + S_OK - if there was source data and it was all copied and the + resultant dest string was null terminated + + failure - you can use the macro HRESULT_CODE() to get a win32 + error code for all hresult failure cases + + STRSAFE_E_INSUFFICIENT_BUFFER / + HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER + - this return value is an indication that the copy + operation failed due to insufficient space. When this + error occurs, the destination buffer is modified to + contain a truncated version of the ideal result and is + null terminated. This is useful for situations where + truncation is ok. + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function + +--*/ + +STRSAFEAPI StringCbCopyNExA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbSrc, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags); +#define StringCbCopyNEx StringCbCopyNExA + +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCbCopyNExA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbSrc, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags) +{ + HRESULT hr; + size_t cchDest; + size_t cchSrc; + size_t cchRemaining = 0; + + cchDest = cbDest / sizeof(char); + cchSrc = cbSrc / sizeof(char); + + if ((cchDest > STRSAFE_MAX_CCH) || + (cchSrc > STRSAFE_MAX_CCH)) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + hr = StringCopyNExWorkerA(pszDest, cchDest, cbDest, pszSrc, cchSrc, ppszDestEnd, &cchRemaining, dwFlags); + } + + if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER)) + { + if (pcbRemaining) + { + // safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1 + *pcbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char)); + } + } + + return hr; +} + +#endif // STRSAFE_INLINE +#endif // !STRSAFE_NO_CB_FUNCTIONS + + +#ifndef STRSAFE_NO_CCH_FUNCTIONS +/*++ + +STDAPI +StringCchCat( + IN OUT LPTSTR pszDest, + IN size_t cchDest, + IN LPCTSTR pszSrc + ); + +Routine Description: + + This routine is a safer version of the C built-in function 'strcat'. + The size of the destination buffer (in characters) is a parameter and this + function will not write past the end of this buffer and it will ALWAYS + null terminate the destination buffer (unless it is zero length). + + This function returns a hresult, and not a pointer. It returns + S_OK if the string was concatenated without truncation and null terminated, + otherwise it will return a failure code. In failure cases as much of pszSrc + will be appended to pszDest as possible, and pszDest will be null + terminated. + +Arguments: + + pszDest - destination string which must be null terminated + + cchDest - size of destination buffer in characters. + length must be = (_tcslen(pszDest) + _tcslen(pszSrc) + 1) + to hold all of the combine string plus the null + terminator + + pszSrc - source string which must be null terminated + +Notes: + Behavior is undefined if source and destination strings overlap. + + pszDest and pszSrc should not be NULL. See StringCchCatEx if you require + the handling of NULL values. + +Return Value: + + S_OK - if there was source data and it was all concatenated and + the resultant dest string was null terminated + + failure - you can use the macro HRESULT_CODE() to get a win32 + error code for all hresult failure cases + + STRSAFE_E_INSUFFICIENT_BUFFER / + HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER + - this return value is an indication that the operation + failed due to insufficient space. When this error occurs, + the destination buffer is modified to contain a truncated + version of the ideal result and is null terminated. This + is useful for situations where truncation is ok. + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function + +--*/ + +STRSAFEAPI StringCchCatA(char* pszDest, size_t cchDest, const char* pszSrc); +#define StringCchCat StringCchCatA + +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCchCatA(char* pszDest, size_t cchDest, const char* pszSrc) +{ + HRESULT hr; + + if (cchDest > STRSAFE_MAX_CCH) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + hr = StringCatWorkerA(pszDest, cchDest, pszSrc); + } + + return hr; +} + +#endif // STRSAFE_INLINE +#endif // !STRSAFE_NO_CCH_FUNCTIONS + + +#ifndef STRSAFE_NO_CB_FUNCTIONS +/*++ + +STDAPI +StringCbCat( + IN OUT LPTSTR pszDest, + IN size_t cbDest, + IN LPCTSTR pszSrc + ); + +Routine Description: + + This routine is a safer version of the C built-in function 'strcat'. + The size of the destination buffer (in bytes) is a parameter and this + function will not write past the end of this buffer and it will ALWAYS + null terminate the destination buffer (unless it is zero length). + + This function returns a hresult, and not a pointer. It returns + S_OK if the string was concatenated without truncation and null terminated, + otherwise it will return a failure code. In failure cases as much of pszSrc + will be appended to pszDest as possible, and pszDest will be null + terminated. + +Arguments: + + pszDest - destination string which must be null terminated + + cbDest - size of destination buffer in bytes. + length must be = ((_tcslen(pszDest) + _tcslen(pszSrc) + 1) * sizeof(TCHAR) + to hold all of the combine string plus the null + terminator + + pszSrc - source string which must be null terminated + +Notes: + Behavior is undefined if source and destination strings overlap. + + pszDest and pszSrc should not be NULL. See StringCbCatEx if you require + the handling of NULL values. + +Return Value: + + S_OK - if there was source data and it was all concatenated and + the resultant dest string was null terminated + + failure - you can use the macro HRESULT_CODE() to get a win32 + error code for all hresult failure cases + + STRSAFE_E_INSUFFICIENT_BUFFER / + HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER + - this return value is an indication that the operation + failed due to insufficient space. When this error occurs, + the destination buffer is modified to contain a truncated + version of the ideal result and is null terminated. This + is useful for situations where truncation is ok. + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function + +--*/ + +STRSAFEAPI StringCbCatA(char* pszDest, size_t cbDest, const char* pszSrc); +#define StringCbCat StringCbCatA + +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCbCatA(char* pszDest, size_t cbDest, const char* pszSrc) +{ + HRESULT hr; + size_t cchDest; + + cchDest = cbDest / sizeof(char); + + if (cchDest > STRSAFE_MAX_CCH) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + hr = StringCatWorkerA(pszDest, cchDest, pszSrc); + } + + return hr; +} + +#endif // STRSAFE_INLINE +#endif // !STRSAFE_NO_CB_FUNCTIONS + + +#ifndef STRSAFE_NO_CCH_FUNCTIONS +/*++ + +STDAPI +StringCchCatEx( + IN OUT LPTSTR pszDest OPTIONAL, + IN size_t cchDest, + IN LPCTSTR pszSrc OPTIONAL, + OUT LPTSTR* ppszDestEnd OPTIONAL, + OUT size_t* pcchRemaining OPTIONAL, + IN DWORD dwFlags + ); + +Routine Description: + + This routine is a safer version of the C built-in function 'strcat' with + some additional parameters. In addition to functionality provided by + StringCchCat, this routine also returns a pointer to the end of the + destination string and the number of characters left in the destination string + including the null terminator. The flags parameter allows additional controls. + +Arguments: + + pszDest - destination string which must be null terminated + + cchDest - size of destination buffer in characters + length must be (_tcslen(pszDest) + _tcslen(pszSrc) + 1) + to hold all of the combine string plus the null + terminator. + + pszSrc - source string which must be null terminated + + ppszDestEnd - if ppszDestEnd is non-null, the function will return a + pointer to the end of the destination string. If the + function appended any data, the result will point to the + null termination character + + pcchRemaining - if pcchRemaining is non-null, the function will return the + number of characters left in the destination string, + including the null terminator + + dwFlags - controls some details of the string copy: + + STRSAFE_FILL_BEHIND_NULL + if the function succeeds, the low byte of dwFlags will be + used to fill the uninitialize part of destination buffer + behind the null terminator + + STRSAFE_IGNORE_NULLS + treat NULL string pointers like empty strings (TEXT("")). + this flag is useful for emulating functions like lstrcat + + STRSAFE_FILL_ON_FAILURE + if the function fails, the low byte of dwFlags will be + used to fill all of the destination buffer, and it will + be null terminated. This will overwrite any pre-existing + or truncated string + + STRSAFE_NULL_ON_FAILURE + if the function fails, the destination buffer will be set + to the empty string. This will overwrite any pre-existing or + truncated string + + STRSAFE_NO_TRUNCATION + if the function returns STRSAFE_E_INSUFFICIENT_BUFFER, pszDest + will not contain a truncated string, it will remain unchanged. + +Notes: + Behavior is undefined if source and destination strings overlap. + + pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag + is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc + may be NULL. An error may still be returned even though NULLS are ignored + due to insufficient space. + +Return Value: + + S_OK - if there was source data and it was all concatenated and + the resultant dest string was null terminated + + failure - you can use the macro HRESULT_CODE() to get a win32 + error code for all hresult failure cases + + STRSAFE_E_INSUFFICIENT_BUFFER / + HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER + - this return value is an indication that the operation + failed due to insufficient space. When this error + occurs, the destination buffer is modified to contain + a truncated version of the ideal result and is null + terminated. This is useful for situations where + truncation is ok. + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function + +--*/ + +STRSAFEAPI StringCchCatExA(char* pszDest, size_t cchDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags); +#define StringCchCatEx StringCchCatExA + +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCchCatExA(char* pszDest, size_t cchDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags) +{ + HRESULT hr; + + if (cchDest > STRSAFE_MAX_CCH) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + size_t cbDest; + + // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1 + cbDest = cchDest * sizeof(char); + + hr = StringCatExWorkerA(pszDest, cchDest, cbDest, pszSrc, ppszDestEnd, pcchRemaining, dwFlags); + } + + return hr; +} + +#endif // STRSAFE_INLINE +#endif // !STRSAFE_NO_CCH_FUNCTIONS + + +#ifndef STRSAFE_NO_CB_FUNCTIONS +/*++ + +STDAPI +StringCbCatEx( + IN OUT LPTSTR pszDest OPTIONAL, + IN size_t cbDest, + IN LPCTSTR pszSrc OPTIONAL, + OUT LPTSTR* ppszDestEnd OPTIONAL, + OUT size_t* pcbRemaining OPTIONAL, + IN DWORD dwFlags + ); + +Routine Description: + + This routine is a safer version of the C built-in function 'strcat' with + some additional parameters. In addition to functionality provided by + StringCbCat, this routine also returns a pointer to the end of the + destination string and the number of bytes left in the destination string + including the null terminator. The flags parameter allows additional controls. + +Arguments: + + pszDest - destination string which must be null terminated + + cbDest - size of destination buffer in bytes. + length must be ((_tcslen(pszDest) + _tcslen(pszSrc) + 1) * sizeof(TCHAR) + to hold all of the combine string plus the null + terminator. + + pszSrc - source string which must be null terminated + + ppszDestEnd - if ppszDestEnd is non-null, the function will return a + pointer to the end of the destination string. If the + function appended any data, the result will point to the + null termination character + + pcbRemaining - if pcbRemaining is non-null, the function will return + the number of bytes left in the destination string, + including the null terminator + + dwFlags - controls some details of the string copy: + + STRSAFE_FILL_BEHIND_NULL + if the function succeeds, the low byte of dwFlags will be + used to fill the uninitialize part of destination buffer + behind the null terminator + + STRSAFE_IGNORE_NULLS + treat NULL string pointers like empty strings (TEXT("")). + this flag is useful for emulating functions like lstrcat + + STRSAFE_FILL_ON_FAILURE + if the function fails, the low byte of dwFlags will be + used to fill all of the destination buffer, and it will + be null terminated. This will overwrite any pre-existing + or truncated string + + STRSAFE_NULL_ON_FAILURE + if the function fails, the destination buffer will be set + to the empty string. This will overwrite any pre-existing or + truncated string + + STRSAFE_NO_TRUNCATION + if the function returns STRSAFE_E_INSUFFICIENT_BUFFER, pszDest + will not contain a truncated string, it will remain unchanged. + +Notes: + Behavior is undefined if source and destination strings overlap. + + pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag + is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc + may be NULL. An error may still be returned even though NULLS are ignored + due to insufficient space. + +Return Value: + + S_OK - if there was source data and it was all concatenated + and the resultant dest string was null terminated + + failure - you can use the macro HRESULT_CODE() to get a win32 + error code for all hresult failure cases + + STRSAFE_E_INSUFFICIENT_BUFFER / + HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER + - this return value is an indication that the operation + failed due to insufficient space. When this error + occurs, the destination buffer is modified to contain + a truncated version of the ideal result and is null + terminated. This is useful for situations where + truncation is ok. + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function + +--*/ + +STRSAFEAPI StringCbCatExA(char* pszDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags); +#define StringCbCatEx StringCbCatExA + +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCbCatExA(char* pszDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags) +{ + HRESULT hr; + size_t cchDest; + size_t cchRemaining = 0; + + cchDest = cbDest / sizeof(char); + + if (cchDest > STRSAFE_MAX_CCH) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + hr = StringCatExWorkerA(pszDest, cchDest, cbDest, pszSrc, ppszDestEnd, &cchRemaining, dwFlags); + } + + if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER)) + { + if (pcbRemaining) + { + // safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1 + *pcbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char)); + } + } + + return hr; +} + +#endif // STRSAFE_INLINE +#endif // !STRSAFE_NO_CB_FUNCTIONS + + +#ifndef STRSAFE_NO_CCH_FUNCTIONS +/*++ + +STDAPI +StringCchCatN( + IN OUT LPTSTR pszDest, + IN size_t cchDest, + IN LPCTSTR pszSrc, + IN size_t cchMaxAppend + ); + +Routine Description: + + This routine is a safer version of the C built-in function 'strncat'. + The size of the destination buffer (in characters) is a parameter as well as + the maximum number of characters to append, excluding the null terminator. + This function will not write past the end of the destination buffer and it will + ALWAYS null terminate pszDest (unless it is zero length). + + This function returns a hresult, and not a pointer. It returns + S_OK if all of pszSrc or the first cchMaxAppend characters were appended + to the destination string and it was null terminated, otherwise it will + return a failure code. In failure cases as much of pszSrc will be appended + to pszDest as possible, and pszDest will be null terminated. + +Arguments: + + pszDest - destination string which must be null terminated + + cchDest - size of destination buffer in characters. + length must be (_tcslen(pszDest) + min(cchMaxAppend, _tcslen(pszSrc)) + 1) + to hold all of the combine string plus the null + terminator. + + pszSrc - source string + + cchMaxAppend - maximum number of characters to append + +Notes: + Behavior is undefined if source and destination strings overlap. + + pszDest and pszSrc should not be NULL. See StringCchCatNEx if you require + the handling of NULL values. + +Return Value: + + S_OK - if all of pszSrc or the first cchMaxAppend characters + were concatenated to pszDest and the resultant dest + string was null terminated + + failure - you can use the macro HRESULT_CODE() to get a win32 + error code for all hresult failure cases + + STRSAFE_E_INSUFFICIENT_BUFFER / + HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER + - this return value is an indication that the operation + failed due to insufficient space. When this error + occurs, the destination buffer is modified to contain + a truncated version of the ideal result and is null + terminated. This is useful for situations where + truncation is ok. + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function + +--*/ + +STRSAFEAPI StringCchCatNA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchMaxAppend); +#define StringCchCatN StringCchCatNA + +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCchCatNA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchMaxAppend) +{ + HRESULT hr; + + if (cchDest > STRSAFE_MAX_CCH) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + hr = StringCatNWorkerA(pszDest, cchDest, pszSrc, cchMaxAppend); + } + + return hr; +} + +#endif // STRSAFE_INLINE +#endif // !STRSAFE_NO_CCH_FUNCTIONS + + +#ifndef STRSAFE_NO_CB_FUNCTIONS +/*++ + +STDAPI +StringCbCatN( + IN OUT LPTSTR pszDest, + IN size_t cbDest, + IN LPCTSTR pszSrc, + IN size_t cbMaxAppend + ); + +Routine Description: + + This routine is a safer version of the C built-in function 'strncat'. + The size of the destination buffer (in bytes) is a parameter as well as + the maximum number of bytes to append, excluding the null terminator. + This function will not write past the end of the destination buffer and it will + ALWAYS null terminate pszDest (unless it is zero length). + + This function returns a hresult, and not a pointer. It returns + S_OK if all of pszSrc or the first cbMaxAppend bytes were appended + to the destination string and it was null terminated, otherwise it will + return a failure code. In failure cases as much of pszSrc will be appended + to pszDest as possible, and pszDest will be null terminated. + +Arguments: + + pszDest - destination string which must be null terminated + + cbDest - size of destination buffer in bytes. + length must be ((_tcslen(pszDest) + min(cbMaxAppend / sizeof(TCHAR), _tcslen(pszSrc)) + 1) * sizeof(TCHAR) + to hold all of the combine string plus the null + terminator. + + pszSrc - source string + + cbMaxAppend - maximum number of bytes to append + +Notes: + Behavior is undefined if source and destination strings overlap. + + pszDest and pszSrc should not be NULL. See StringCbCatNEx if you require + the handling of NULL values. + +Return Value: + + S_OK - if all of pszSrc or the first cbMaxAppend bytes were + concatenated to pszDest and the resultant dest string + was null terminated + + failure - you can use the macro HRESULT_CODE() to get a win32 + error code for all hresult failure cases + + STRSAFE_E_INSUFFICIENT_BUFFER / + HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER + - this return value is an indication that the operation + failed due to insufficient space. When this error + occurs, the destination buffer is modified to contain + a truncated version of the ideal result and is null + terminated. This is useful for situations where + truncation is ok. + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function + +--*/ + +STRSAFEAPI StringCbCatNA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbMaxAppend); + +#define StringCbCatN StringCbCatNA + +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCbCatNA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbMaxAppend) +{ + HRESULT hr; + size_t cchDest; + + cchDest = cbDest / sizeof(char); + + if (cchDest > STRSAFE_MAX_CCH) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + size_t cchMaxAppend; + + cchMaxAppend = cbMaxAppend / sizeof(char); + + hr = StringCatNWorkerA(pszDest, cchDest, pszSrc, cchMaxAppend); + } + + return hr; +} + +#endif // STRSAFE_INLINE +#endif // !STRSAFE_NO_CB_FUNCTIONS + + +#ifndef STRSAFE_NO_CCH_FUNCTIONS +/*++ + +STDAPI +StringCchCatNEx( + IN OUT LPTSTR pszDest OPTIONAL, + IN size_t cchDest, + IN LPCTSTR pszSrc OPTIONAL, + IN size_t cchMaxAppend, + OUT LPTSTR* ppszDestEnd OPTIONAL, + OUT size_t* pcchRemaining OPTIONAL, + IN DWORD dwFlags + ); + +Routine Description: + + This routine is a safer version of the C built-in function 'strncat', with + some additional parameters. In addition to functionality provided by + StringCchCatN, this routine also returns a pointer to the end of the + destination string and the number of characters left in the destination string + including the null terminator. The flags parameter allows additional controls. + +Arguments: + + pszDest - destination string which must be null terminated + + cchDest - size of destination buffer in characters. + length must be (_tcslen(pszDest) + min(cchMaxAppend, _tcslen(pszSrc)) + 1) + to hold all of the combine string plus the null + terminator. + + pszSrc - source string + + cchMaxAppend - maximum number of characters to append + + ppszDestEnd - if ppszDestEnd is non-null, the function will return a + pointer to the end of the destination string. If the + function appended any data, the result will point to the + null termination character + + pcchRemaining - if pcchRemaining is non-null, the function will return the + number of characters left in the destination string, + including the null terminator + + dwFlags - controls some details of the string copy: + + STRSAFE_FILL_BEHIND_NULL + if the function succeeds, the low byte of dwFlags will be + used to fill the uninitialize part of destination buffer + behind the null terminator + + STRSAFE_IGNORE_NULLS + treat NULL string pointers like empty strings (TEXT("")) + + STRSAFE_FILL_ON_FAILURE + if the function fails, the low byte of dwFlags will be + used to fill all of the destination buffer, and it will + be null terminated. This will overwrite any pre-existing + or truncated string + + STRSAFE_NULL_ON_FAILURE + if the function fails, the destination buffer will be set + to the empty string. This will overwrite any pre-existing or + truncated string + + STRSAFE_NO_TRUNCATION + if the function returns STRSAFE_E_INSUFFICIENT_BUFFER, pszDest + will not contain a truncated string, it will remain unchanged. + +Notes: + Behavior is undefined if source and destination strings overlap. + + pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag + is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc + may be NULL. An error may still be returned even though NULLS are ignored + due to insufficient space. + +Return Value: + + S_OK - if all of pszSrc or the first cchMaxAppend characters + were concatenated to pszDest and the resultant dest + string was null terminated + + failure - you can use the macro HRESULT_CODE() to get a win32 + error code for all hresult failure cases + + STRSAFE_E_INSUFFICIENT_BUFFER / + HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER + - this return value is an indication that the operation + failed due to insufficient space. When this error + occurs, the destination buffer is modified to contain + a truncated version of the ideal result and is null + terminated. This is useful for situations where + truncation is ok. + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function + +--*/ + +STRSAFEAPI StringCchCatNExA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchMaxAppend, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags); +#define StringCchCatNEx StringCchCatNExA + +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCchCatNExA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchMaxAppend, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags) +{ + HRESULT hr; + + if (cchDest > STRSAFE_MAX_CCH) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + size_t cbDest; + + // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1 + cbDest = cchDest * sizeof(char); + + hr = StringCatNExWorkerA(pszDest, cchDest, cbDest, pszSrc, cchMaxAppend, ppszDestEnd, pcchRemaining, dwFlags); + } + + return hr; +} + + +#endif // STRSAFE_INLINE +#endif // !STRSAFE_NO_CCH_FUNCTIONS + + +#ifndef STRSAFE_NO_CB_FUNCTIONS +/*++ + +STDAPI +StringCbCatNEx( + IN OUT LPTSTR pszDest OPTIONAL, + IN size_t cbDest, + IN LPCTSTR pszSrc OPTIONAL, + IN size_t cbMaxAppend, + OUT LPTSTR* ppszDestEnd OPTIONAL, + OUT size_t* pcchRemaining OPTIONAL, + IN DWORD dwFlags + ); + +Routine Description: + + This routine is a safer version of the C built-in function 'strncat', with + some additional parameters. In addition to functionality provided by + StringCbCatN, this routine also returns a pointer to the end of the + destination string and the number of bytes left in the destination string + including the null terminator. The flags parameter allows additional controls. + +Arguments: + + pszDest - destination string which must be null terminated + + cbDest - size of destination buffer in bytes. + length must be ((_tcslen(pszDest) + min(cbMaxAppend / sizeof(TCHAR), _tcslen(pszSrc)) + 1) * sizeof(TCHAR) + to hold all of the combine string plus the null + terminator. + + pszSrc - source string + + cbMaxAppend - maximum number of bytes to append + + ppszDestEnd - if ppszDestEnd is non-null, the function will return a + pointer to the end of the destination string. If the + function appended any data, the result will point to the + null termination character + + pcbRemaining - if pcbRemaining is non-null, the function will return the + number of bytes left in the destination string, + including the null terminator + + dwFlags - controls some details of the string copy: + + STRSAFE_FILL_BEHIND_NULL + if the function succeeds, the low byte of dwFlags will be + used to fill the uninitialize part of destination buffer + behind the null terminator + + STRSAFE_IGNORE_NULLS + treat NULL string pointers like empty strings (TEXT("")) + + STRSAFE_FILL_ON_FAILURE + if the function fails, the low byte of dwFlags will be + used to fill all of the destination buffer, and it will + be null terminated. This will overwrite any pre-existing + or truncated string + + STRSAFE_NULL_ON_FAILURE + if the function fails, the destination buffer will be set + to the empty string. This will overwrite any pre-existing or + truncated string + + STRSAFE_NO_TRUNCATION + if the function returns STRSAFE_E_INSUFFICIENT_BUFFER, pszDest + will not contain a truncated string, it will remain unchanged. + +Notes: + Behavior is undefined if source and destination strings overlap. + + pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag + is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc + may be NULL. An error may still be returned even though NULLS are ignored + due to insufficient space. + +Return Value: + + S_OK - if all of pszSrc or the first cbMaxAppend bytes were + concatenated to pszDest and the resultant dest string + was null terminated + + failure - you can use the macro HRESULT_CODE() to get a win32 + error code for all hresult failure cases + + STRSAFE_E_INSUFFICIENT_BUFFER / + HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER + - this return value is an indication that the operation + failed due to insufficient space. When this error + occurs, the destination buffer is modified to contain + a truncated version of the ideal result and is null + terminated. This is useful for situations where + truncation is ok. + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function + +--*/ + +STRSAFEAPI StringCbCatNExA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbMaxAppend, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags); +#define StringCbCatNEx StringCbCatNExA + +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCbCatNExA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbMaxAppend, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags) +{ + HRESULT hr; + size_t cchDest; + size_t cchRemaining = 0; + + cchDest = cbDest / sizeof(char); + + if (cchDest > STRSAFE_MAX_CCH) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + size_t cchMaxAppend; + + cchMaxAppend = cbMaxAppend / sizeof(char); + + hr = StringCatNExWorkerA(pszDest, cchDest, cbDest, pszSrc, cchMaxAppend, ppszDestEnd, &cchRemaining, dwFlags); + } + + if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER)) + { + if (pcbRemaining) + { + // safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1 + *pcbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char)); + } + } + + return hr; +} +#endif // STRSAFE_INLINE +#endif // !STRSAFE_NO_CB_FUNCTIONS + + +#ifndef STRSAFE_NO_CCH_FUNCTIONS +/*++ + +STDAPI +StringCchVPrintf( + OUT LPTSTR pszDest, + IN size_t cchDest, + IN LPCTSTR pszFormat, + IN va_list argList + ); + +Routine Description: + + This routine is a safer version of the C built-in function 'vsprintf'. + The size of the destination buffer (in characters) is a parameter and + this function will not write past the end of this buffer and it will + ALWAYS null terminate the destination buffer (unless it is zero length). + + This function returns a hresult, and not a pointer. It returns + S_OK if the string was printed without truncation and null terminated, + otherwise it will return a failure code. In failure cases it will return + a truncated version of the ideal result. + +Arguments: + + pszDest - destination string + + cchDest - size of destination buffer in characters + length must be sufficient to hold the resulting formatted + string, including the null terminator. + + pszFormat - format string which must be null terminated + + argList - va_list from the variable arguments according to the + stdarg.h convention + +Notes: + Behavior is undefined if destination, format strings or any arguments + strings overlap. + + pszDest and pszFormat should not be NULL. See StringCchVPrintfEx if you + require the handling of NULL values. + +Return Value: + + S_OK - if there was sufficient space in the dest buffer for + the resultant string and it was null terminated. + + failure - you can use the macro HRESULT_CODE() to get a win32 + error code for all hresult failure cases + + STRSAFE_E_INSUFFICIENT_BUFFER / + HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER + - this return value is an indication that the print + operation failed due to insufficient space. When this + error occurs, the destination buffer is modified to + contain a truncated version of the ideal result and is + null terminated. This is useful for situations where + truncation is ok. + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function + +--*/ + +STRSAFEAPI StringCchVPrintfA(char* pszDest, size_t cchDest, const char* pszFormat, va_list argList); +#define StringCchVPrintf StringCchVPrintfA + +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCchVPrintfA(char* pszDest, size_t cchDest, const char* pszFormat, va_list argList) +{ + HRESULT hr; + + if (cchDest > STRSAFE_MAX_CCH) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + hr = StringVPrintfWorkerA(pszDest, cchDest, pszFormat, argList); + } + + return hr; +} +#endif // STRSAFE_INLINE +#endif // !STRSAFE_NO_CCH_FUNCTIONS + + +#ifndef STRSAFE_NO_CB_FUNCTIONS +/*++ + +STDAPI +StringCbVPrintf( + OUT LPTSTR pszDest, + IN size_t cbDest, + IN LPCTSTR pszFormat, + IN va_list argList + ); + +Routine Description: + + This routine is a safer version of the C built-in function 'vsprintf'. + The size of the destination buffer (in bytes) is a parameter and + this function will not write past the end of this buffer and it will + ALWAYS null terminate the destination buffer (unless it is zero length). + + This function returns a hresult, and not a pointer. It returns + S_OK if the string was printed without truncation and null terminated, + otherwise it will return a failure code. In failure cases it will return + a truncated version of the ideal result. + +Arguments: + + pszDest - destination string + + cbDest - size of destination buffer in bytes + length must be sufficient to hold the resulting formatted + string, including the null terminator. + + pszFormat - format string which must be null terminated + + argList - va_list from the variable arguments according to the + stdarg.h convention + +Notes: + Behavior is undefined if destination, format strings or any arguments + strings overlap. + + pszDest and pszFormat should not be NULL. See StringCbVPrintfEx if you + require the handling of NULL values. + + +Return Value: + + S_OK - if there was sufficient space in the dest buffer for + the resultant string and it was null terminated. + + failure - you can use the macro HRESULT_CODE() to get a win32 + error code for all hresult failure cases + + STRSAFE_E_INSUFFICIENT_BUFFER / + HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER + - this return value is an indication that the print + operation failed due to insufficient space. When this + error occurs, the destination buffer is modified to + contain a truncated version of the ideal result and is + null terminated. This is useful for situations where + truncation is ok. + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function + +--*/ + +STRSAFEAPI StringCbVPrintfA(char* pszDest, size_t cbDest, const char* pszFormat, va_list argList); +#define StringCbVPrintf StringCbVPrintfA + +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCbVPrintfA(char* pszDest, size_t cbDest, const char* pszFormat, va_list argList) +{ + HRESULT hr; + size_t cchDest; + + cchDest = cbDest / sizeof(char); + + if (cchDest > STRSAFE_MAX_CCH) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + hr = StringVPrintfWorkerA(pszDest, cchDest, pszFormat, argList); + } + + return hr; +} + +#endif // STRSAFE_INLINE +#endif // !STRSAFE_NO_CB_FUNCTIONS + + +#ifndef STRSAFE_NO_CCH_FUNCTIONS +/*++ + +STDAPI +StringCchPrintf( + OUT LPTSTR pszDest, + IN size_t cchDest, + IN LPCTSTR pszFormat, + ... + ); + +Routine Description: + + This routine is a safer version of the C built-in function 'sprintf'. + The size of the destination buffer (in characters) is a parameter and + this function will not write past the end of this buffer and it will + ALWAYS null terminate the destination buffer (unless it is zero length). + + This function returns a hresult, and not a pointer. It returns + S_OK if the string was printed without truncation and null terminated, + otherwise it will return a failure code. In failure cases it will return + a truncated version of the ideal result. + +Arguments: + + pszDest - destination string + + cchDest - size of destination buffer in characters + length must be sufficient to hold the resulting formatted + string, including the null terminator. + + pszFormat - format string which must be null terminated + + ... - additional parameters to be formatted according to + the format string + +Notes: + Behavior is undefined if destination, format strings or any arguments + strings overlap. + + pszDest and pszFormat should not be NULL. See StringCchPrintfEx if you + require the handling of NULL values. + +Return Value: + + S_OK - if there was sufficient space in the dest buffer for + the resultant string and it was null terminated. + + failure - you can use the macro HRESULT_CODE() to get a win32 + error code for all hresult failure cases + + STRSAFE_E_INSUFFICIENT_BUFFER / + HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER + - this return value is an indication that the print + operation failed due to insufficient space. When this + error occurs, the destination buffer is modified to + contain a truncated version of the ideal result and is + null terminated. This is useful for situations where + truncation is ok. + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function + +--*/ + +STRSAFEAPI StringCchPrintfA(char* pszDest, size_t cchDest, const char* pszFormat, ...); +#define StringCchPrintf StringCchPrintfA + +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCchPrintfA(char* pszDest, size_t cchDest, const char* pszFormat, ...) +{ + HRESULT hr; + + if (cchDest > STRSAFE_MAX_CCH) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + va_list argList; + + va_start(argList, pszFormat); + + hr = StringVPrintfWorkerA(pszDest, cchDest, pszFormat, argList); + + va_end(argList); + } + + return hr; +} + +#endif // STRSAFE_INLINE +#endif // !STRSAFE_NO_CCH_FUNCTIONS + + +#ifndef STRSAFE_NO_CB_FUNCTIONS +/*++ + +STDAPI +StringCbPrintf( + OUT LPTSTR pszDest, + IN size_t cbDest, + IN LPCTSTR pszFormat, + ... + ); + +Routine Description: + + This routine is a safer version of the C built-in function 'sprintf'. + The size of the destination buffer (in bytes) is a parameter and + this function will not write past the end of this buffer and it will + ALWAYS null terminate the destination buffer (unless it is zero length). + + This function returns a hresult, and not a pointer. It returns + S_OK if the string was printed without truncation and null terminated, + otherwise it will return a failure code. In failure cases it will return + a truncated version of the ideal result. + +Arguments: + + pszDest - destination string + + cbDest - size of destination buffer in bytes + length must be sufficient to hold the resulting formatted + string, including the null terminator. + + pszFormat - format string which must be null terminated + + ... - additional parameters to be formatted according to + the format string + +Notes: + Behavior is undefined if destination, format strings or any arguments + strings overlap. + + pszDest and pszFormat should not be NULL. See StringCbPrintfEx if you + require the handling of NULL values. + + +Return Value: + + S_OK - if there was sufficient space in the dest buffer for + the resultant string and it was null terminated. + + failure - you can use the macro HRESULT_CODE() to get a win32 + error code for all hresult failure cases + + STRSAFE_E_INSUFFICIENT_BUFFER / + HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER + - this return value is an indication that the print + operation failed due to insufficient space. When this + error occurs, the destination buffer is modified to + contain a truncated version of the ideal result and is + null terminated. This is useful for situations where + truncation is ok. + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function + +--*/ + +STRSAFEAPI StringCbPrintfA(char* pszDest, size_t cbDest, const char* pszFormat, ...); +#define StringCbPrintf StringCbPrintfA + +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCbPrintfA(char* pszDest, size_t cbDest, const char* pszFormat, ...) +{ + HRESULT hr; + size_t cchDest; + + cchDest = cbDest / sizeof(char); + + if (cchDest > STRSAFE_MAX_CCH) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + va_list argList; + + va_start(argList, pszFormat); + + hr = StringVPrintfWorkerA(pszDest, cchDest, pszFormat, argList); + + va_end(argList); + } + + return hr; +} + +#endif // STRSAFE_INLINE +#endif // !STRSAFE_NO_CB_FUNCTIONS + + +#ifndef STRSAFE_NO_CCH_FUNCTIONS +/*++ + +STDAPI +StringCchPrintfEx( + OUT LPTSTR pszDest OPTIONAL, + IN size_t cchDest, + OUT LPTSTR* ppszDestEnd OPTIONAL, + OUT size_t* pcchRemaining OPTIONAL, + IN DWORD dwFlags, + IN LPCTSTR pszFormat OPTIONAL, + ... + ); + +Routine Description: + + This routine is a safer version of the C built-in function 'sprintf' with + some additional parameters. In addition to functionality provided by + StringCchPrintf, this routine also returns a pointer to the end of the + destination string and the number of characters left in the destination string + including the null terminator. The flags parameter allows additional controls. + +Arguments: + + pszDest - destination string + + cchDest - size of destination buffer in characters. + length must be sufficient to contain the resulting + formatted string plus the null terminator. + + ppszDestEnd - if ppszDestEnd is non-null, the function will return a + pointer to the end of the destination string. If the + function printed any data, the result will point to the + null termination character + + pcchRemaining - if pcchRemaining is non-null, the function will return + the number of characters left in the destination string, + including the null terminator + + dwFlags - controls some details of the string copy: + + STRSAFE_FILL_BEHIND_NULL + if the function succeeds, the low byte of dwFlags will be + used to fill the uninitialize part of destination buffer + behind the null terminator + + STRSAFE_IGNORE_NULLS + treat NULL string pointers like empty strings (TEXT("")) + + STRSAFE_FILL_ON_FAILURE + if the function fails, the low byte of dwFlags will be + used to fill all of the destination buffer, and it will + be null terminated. This will overwrite any truncated + string returned when the failure is + STRSAFE_E_INSUFFICIENT_BUFFER + + STRSAFE_NO_TRUNCATION / + STRSAFE_NULL_ON_FAILURE + if the function fails, the destination buffer will be set + to the empty string. This will overwrite any truncated string + returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER. + + pszFormat - format string which must be null terminated + + ... - additional parameters to be formatted according to + the format string + +Notes: + Behavior is undefined if destination, format strings or any arguments + strings overlap. + + pszDest and pszFormat should not be NULL unless the STRSAFE_IGNORE_NULLS + flag is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and + pszFormat may be NULL. An error may still be returned even though NULLS + are ignored due to insufficient space. + +Return Value: + + S_OK - if there was source data and it was all concatenated and + the resultant dest string was null terminated + + failure - you can use the macro HRESULT_CODE() to get a win32 + error code for all hresult failure cases + + STRSAFE_E_INSUFFICIENT_BUFFER / + HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER + - this return value is an indication that the print + operation failed due to insufficient space. When this + error occurs, the destination buffer is modified to + contain a truncated version of the ideal result and is + null terminated. This is useful for situations where + truncation is ok. + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function + +--*/ + +STRSAFEAPI StringCchPrintfExA(char* pszDest, size_t cchDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const char* pszFormat, ...); +#define StringCchPrintfEx StringCchPrintfExA + +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCchPrintfExA(char* pszDest, size_t cchDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const char* pszFormat, ...) +{ + HRESULT hr; + + if (cchDest > STRSAFE_MAX_CCH) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + size_t cbDest; + va_list argList; + + // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1 + cbDest = cchDest * sizeof(char); + va_start(argList, pszFormat); + + hr = StringVPrintfExWorkerA(pszDest, cchDest, cbDest, ppszDestEnd, pcchRemaining, dwFlags, pszFormat, argList); + + va_end(argList); + } + + return hr; +} + +#endif // STRSAFE_INLINE +#endif // !STRSAFE_NO_CCH_FUNCTIONS + + +#ifndef STRSAFE_NO_CB_FUNCTIONS +/*++ + +STDAPI +StringCbPrintfEx( + OUT LPTSTR pszDest OPTIONAL, + IN size_t cbDest, + OUT LPTSTR* ppszDestEnd OPTIONAL, + OUT size_t* pcbRemaining OPTIONAL, + IN DWORD dwFlags, + IN LPCTSTR pszFormat OPTIONAL, + ... + ); + +Routine Description: + + This routine is a safer version of the C built-in function 'sprintf' with + some additional parameters. In addition to functionality provided by + StringCbPrintf, this routine also returns a pointer to the end of the + destination string and the number of bytes left in the destination string + including the null terminator. The flags parameter allows additional controls. + +Arguments: + + pszDest - destination string + + cbDest - size of destination buffer in bytes. + length must be sufficient to contain the resulting + formatted string plus the null terminator. + + ppszDestEnd - if ppszDestEnd is non-null, the function will return a + pointer to the end of the destination string. If the + function printed any data, the result will point to the + null termination character + + pcbRemaining - if pcbRemaining is non-null, the function will return + the number of bytes left in the destination string, + including the null terminator + + dwFlags - controls some details of the string copy: + + STRSAFE_FILL_BEHIND_NULL + if the function succeeds, the low byte of dwFlags will be + used to fill the uninitialize part of destination buffer + behind the null terminator + + STRSAFE_IGNORE_NULLS + treat NULL string pointers like empty strings (TEXT("")) + + STRSAFE_FILL_ON_FAILURE + if the function fails, the low byte of dwFlags will be + used to fill all of the destination buffer, and it will + be null terminated. This will overwrite any truncated + string returned when the failure is + STRSAFE_E_INSUFFICIENT_BUFFER + + STRSAFE_NO_TRUNCATION / + STRSAFE_NULL_ON_FAILURE + if the function fails, the destination buffer will be set + to the empty string. This will overwrite any truncated string + returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER. + + pszFormat - format string which must be null terminated + + ... - additional parameters to be formatted according to + the format string + +Notes: + Behavior is undefined if destination, format strings or any arguments + strings overlap. + + pszDest and pszFormat should not be NULL unless the STRSAFE_IGNORE_NULLS + flag is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and + pszFormat may be NULL. An error may still be returned even though NULLS + are ignored due to insufficient space. + +Return Value: + + S_OK - if there was source data and it was all concatenated and + the resultant dest string was null terminated + + failure - you can use the macro HRESULT_CODE() to get a win32 + error code for all hresult failure cases + + STRSAFE_E_INSUFFICIENT_BUFFER / + HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER + - this return value is an indication that the print + operation failed due to insufficient space. When this + error occurs, the destination buffer is modified to + contain a truncated version of the ideal result and is + null terminated. This is useful for situations where + truncation is ok. + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function + +--*/ + +STRSAFEAPI StringCbPrintfExA(char* pszDest, size_t cbDest, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags, const char* pszFormat, ...); +#define StringCbPrintfEx StringCbPrintfExA + +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCbPrintfExA(char* pszDest, size_t cbDest, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags, const char* pszFormat, ...) +{ + HRESULT hr; + size_t cchDest; + size_t cchRemaining = 0; + + cchDest = cbDest / sizeof(char); + + if (cchDest > STRSAFE_MAX_CCH) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + va_list argList; + + va_start(argList, pszFormat); + + hr = StringVPrintfExWorkerA(pszDest, cchDest, cbDest, ppszDestEnd, &cchRemaining, dwFlags, pszFormat, argList); + + va_end(argList); + } + + if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER)) + { + if (pcbRemaining) + { + // safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1 + *pcbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char)); + } + } + + return hr; +} + +#endif // STRSAFE_INLINE +#endif // !STRSAFE_NO_CB_FUNCTIONS + + +#ifndef STRSAFE_NO_CCH_FUNCTIONS +/*++ + +STDAPI +StringCchVPrintfEx( + OUT LPTSTR pszDest OPTIONAL, + IN size_t cchDest, + OUT LPTSTR* ppszDestEnd OPTIONAL, + OUT size_t* pcchRemaining OPTIONAL, + IN DWORD dwFlags, + IN LPCTSTR pszFormat OPTIONAL, + IN va_list argList + ); + +Routine Description: + + This routine is a safer version of the C built-in function 'vsprintf' with + some additional parameters. In addition to functionality provided by + StringCchVPrintf, this routine also returns a pointer to the end of the + destination string and the number of characters left in the destination string + including the null terminator. The flags parameter allows additional controls. + +Arguments: + + pszDest - destination string + + cchDest - size of destination buffer in characters. + length must be sufficient to contain the resulting + formatted string plus the null terminator. + + ppszDestEnd - if ppszDestEnd is non-null, the function will return a + pointer to the end of the destination string. If the + function printed any data, the result will point to the + null termination character + + pcchRemaining - if pcchRemaining is non-null, the function will return + the number of characters left in the destination string, + including the null terminator + + dwFlags - controls some details of the string copy: + + STRSAFE_FILL_BEHIND_NULL + if the function succeeds, the low byte of dwFlags will be + used to fill the uninitialize part of destination buffer + behind the null terminator + + STRSAFE_IGNORE_NULLS + treat NULL string pointers like empty strings (TEXT("")) + + STRSAFE_FILL_ON_FAILURE + if the function fails, the low byte of dwFlags will be + used to fill all of the destination buffer, and it will + be null terminated. This will overwrite any truncated + string returned when the failure is + STRSAFE_E_INSUFFICIENT_BUFFER + + STRSAFE_NO_TRUNCATION / + STRSAFE_NULL_ON_FAILURE + if the function fails, the destination buffer will be set + to the empty string. This will overwrite any truncated string + returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER. + + pszFormat - format string which must be null terminated + + argList - va_list from the variable arguments according to the + stdarg.h convention + +Notes: + Behavior is undefined if destination, format strings or any arguments + strings overlap. + + pszDest and pszFormat should not be NULL unless the STRSAFE_IGNORE_NULLS + flag is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and + pszFormat may be NULL. An error may still be returned even though NULLS + are ignored due to insufficient space. + +Return Value: + + S_OK - if there was source data and it was all concatenated and + the resultant dest string was null terminated + + failure - you can use the macro HRESULT_CODE() to get a win32 + error code for all hresult failure cases + + STRSAFE_E_INSUFFICIENT_BUFFER / + HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER + - this return value is an indication that the print + operation failed due to insufficient space. When this + error occurs, the destination buffer is modified to + contain a truncated version of the ideal result and is + null terminated. This is useful for situations where + truncation is ok. + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function + +--*/ + +STRSAFEAPI StringCchVPrintfExA(char* pszDest, size_t cchDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const char* pszFormat, va_list argList); +#define StringCchVPrintfEx StringCchVPrintfExA + +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCchVPrintfExA(char* pszDest, size_t cchDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const char* pszFormat, va_list argList) +{ + HRESULT hr; + + if (cchDest > STRSAFE_MAX_CCH) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + size_t cbDest; + + // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1 + cbDest = cchDest * sizeof(char); + + hr = StringVPrintfExWorkerA(pszDest, cchDest, cbDest, ppszDestEnd, pcchRemaining, dwFlags, pszFormat, argList); + } + + return hr; +} +#endif // STRSAFE_INLINE +#endif // !STRSAFE_NO_CCH_FUNCTIONS + + +#ifndef STRSAFE_NO_CB_FUNCTIONS +/*++ + +STDAPI +StringCbVPrintfEx( + OUT LPTSTR pszDest OPTIONAL, + IN size_t cbDest, + OUT LPTSTR* ppszDestEnd OPTIONAL, + OUT size_t* pcbRemaining OPTIONAL, + IN DWORD dwFlags, + IN LPCTSTR pszFormat OPTIONAL, + IN va_list argList + ); + +Routine Description: + + This routine is a safer version of the C built-in function 'vsprintf' with + some additional parameters. In addition to functionality provided by + StringCbVPrintf, this routine also returns a pointer to the end of the + destination string and the number of characters left in the destination string + including the null terminator. The flags parameter allows additional controls. + +Arguments: + + pszDest - destination string + + cbDest - size of destination buffer in bytes. + length must be sufficient to contain the resulting + formatted string plus the null terminator. + + ppszDestEnd - if ppszDestEnd is non-null, the function will return + a pointer to the end of the destination string. If the + function printed any data, the result will point to the + null termination character + + pcbRemaining - if pcbRemaining is non-null, the function will return + the number of bytes left in the destination string, + including the null terminator + + dwFlags - controls some details of the string copy: + + STRSAFE_FILL_BEHIND_NULL + if the function succeeds, the low byte of dwFlags will be + used to fill the uninitialize part of destination buffer + behind the null terminator + + STRSAFE_IGNORE_NULLS + treat NULL string pointers like empty strings (TEXT("")) + + STRSAFE_FILL_ON_FAILURE + if the function fails, the low byte of dwFlags will be + used to fill all of the destination buffer, and it will + be null terminated. This will overwrite any truncated + string returned when the failure is + STRSAFE_E_INSUFFICIENT_BUFFER + + STRSAFE_NO_TRUNCATION / + STRSAFE_NULL_ON_FAILURE + if the function fails, the destination buffer will be set + to the empty string. This will overwrite any truncated string + returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER. + + pszFormat - format string which must be null terminated + + argList - va_list from the variable arguments according to the + stdarg.h convention + +Notes: + Behavior is undefined if destination, format strings or any arguments + strings overlap. + + pszDest and pszFormat should not be NULL unless the STRSAFE_IGNORE_NULLS + flag is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and + pszFormat may be NULL. An error may still be returned even though NULLS + are ignored due to insufficient space. + +Return Value: + + S_OK - if there was source data and it was all concatenated and + the resultant dest string was null terminated + + failure - you can use the macro HRESULT_CODE() to get a win32 + error code for all hresult failure cases + + STRSAFE_E_INSUFFICIENT_BUFFER / + HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER + - this return value is an indication that the print + operation failed due to insufficient space. When this + error occurs, the destination buffer is modified to + contain a truncated version of the ideal result and is + null terminated. This is useful for situations where + truncation is ok. + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function + +--*/ + +STRSAFEAPI StringCbVPrintfExA(char* pszDest, size_t cbDest, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags, const char* pszFormat, va_list argList); +#define StringCbVPrintfEx StringCbVPrintfExA + +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCbVPrintfExA(char* pszDest, size_t cbDest, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags, const char* pszFormat, va_list argList) +{ + HRESULT hr; + size_t cchDest; + size_t cchRemaining = 0; + + cchDest = cbDest / sizeof(char); + + if (cchDest > STRSAFE_MAX_CCH) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + hr = StringVPrintfExWorkerA(pszDest, cchDest, cbDest, ppszDestEnd, &cchRemaining, dwFlags, pszFormat, argList); + } + + if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER)) + { + if (pcbRemaining) + { + // safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1 + *pcbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char)); + } + } + + return hr; +} + +#endif // STRSAFE_INLINE +#endif // !STRSAFE_NO_CB_FUNCTIONS + + +#ifndef STRSAFE_NO_CCH_FUNCTIONS +/*++ + +STDAPI +StringCchGets( + OUT LPTSTR pszDest, + IN size_t cchDest + ); + +Routine Description: + + This routine is a safer version of the C built-in function 'gets'. + The size of the destination buffer (in characters) is a parameter and + this function will not write past the end of this buffer and it will + ALWAYS null terminate the destination buffer (unless it is zero length). + + This routine is not a replacement for fgets. That function does not replace + newline characters with a null terminator. + + This function returns a hresult, and not a pointer. It returns + S_OK if any characters were read from stdin and copied to pszDest and + pszDest was null terminated, otherwise it will return a failure code. + +Arguments: + + pszDest - destination string + + cchDest - size of destination buffer in characters. + +Notes: + pszDest should not be NULL. See StringCchGetsEx if you require the handling + of NULL values. + + cchDest must be > 1 for this function to succeed. + +Return Value: + + S_OK - data was read from stdin and copied, and the resultant + dest string was null terminated + + failure - you can use the macro HRESULT_CODE() to get a win32 + error code for all hresult failure cases + + STRSAFE_E_END_OF_FILE / + HRESULT_CODE(hr) == ERROR_HANDLE_EOF + - this return value indicates an error or end-of-file + condition, use feof or ferror to determine which one has + occured. + + STRSAFE_E_INSUFFICIENT_BUFFER / + HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER + - this return value is an indication that there was + insufficient space in the destination buffer to copy any + data + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function. + +--*/ + +#ifndef STRSAFE_LIB_IMPL +STRSAFE_INLINE_API StringCchGetsA(char* pszDest, size_t cchDest); +#define StringCchGets StringCchGetsA + +STRSAFE_INLINE_API StringCchGetsA(char* pszDest, size_t cchDest) +{ + HRESULT hr; + + if (cchDest > STRSAFE_MAX_CCH) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + size_t cbDest; + + // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1 + cbDest = cchDest * sizeof(char); + + hr = StringGetsExWorkerA(pszDest, cchDest, cbDest, NULL, NULL, 0); + } + + return hr; +} + +#endif // !STRSAFE_NO_CCH_FUNCTIONS +#endif // !STRSAFE_LIB_IMPL + +#ifndef STRSAFE_NO_CB_FUNCTIONS +/*++ + +STDAPI +StringCbGets( + OUT LPTSTR pszDest, + IN size_t cbDest + ); + +Routine Description: + + This routine is a safer version of the C built-in function 'gets'. + The size of the destination buffer (in bytes) is a parameter and + this function will not write past the end of this buffer and it will + ALWAYS null terminate the destination buffer (unless it is zero length). + + This routine is not a replacement for fgets. That function does not replace + newline characters with a null terminator. + + This function returns a hresult, and not a pointer. It returns + S_OK if any characters were read from stdin and copied to pszDest + and pszDest was null terminated, otherwise it will return a failure code. + +Arguments: + + pszDest - destination string + + cbDest - size of destination buffer in bytes. + +Notes: + pszDest should not be NULL. See StringCbGetsEx if you require the handling + of NULL values. + + cbDest must be > sizeof(TCHAR) for this function to succeed. + +Return Value: + + S_OK - data was read from stdin and copied, and the resultant + dest string was null terminated + + failure - you can use the macro HRESULT_CODE() to get a win32 + error code for all hresult failure cases + + STRSAFE_E_END_OF_FILE / + HRESULT_CODE(hr) == ERROR_HANDLE_EOF + - this return value indicates an error or end-of-file + condition, use feof or ferror to determine which one has + occured. + + STRSAFE_E_INSUFFICIENT_BUFFER / + HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER + - this return value is an indication that there was + insufficient space in the destination buffer to copy any + data + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function. + +--*/ + +#ifndef STRSAFE_LIB_IMPL +STRSAFE_INLINE_API StringCbGetsA(char* pszDest, size_t cbDest); + +#define StringCbGets StringCbGetsA + +STRSAFE_INLINE_API StringCbGetsA(char* pszDest, size_t cbDest) +{ + HRESULT hr; + size_t cchDest; + + // convert to count of characters + cchDest = cbDest / sizeof(char); + + if (cchDest > STRSAFE_MAX_CCH) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + hr = StringGetsExWorkerA(pszDest, cchDest, cbDest, NULL, NULL, 0); + } + + return hr; +} + +#endif // !STRSAFE_NO_CB_FUNCTIONS +#endif // !STRSAFE_LIB_IMPL + +#ifndef STRSAFE_NO_CCH_FUNCTIONS +/*++ + +STDAPI +StringCchGetsEx( + OUT LPTSTR pszDest OPTIONAL, + IN size_t cchDest, + OUT LPTSTR* ppszDestEnd OPTIONAL, + OUT size_t* pcchRemaining OPTIONAL, + IN DWORD dwFlags + ); + +Routine Description: + + This routine is a safer version of the C built-in function 'gets' with + some additional parameters. In addition to functionality provided by + StringCchGets, this routine also returns a pointer to the end of the + destination string and the number of characters left in the destination string + including the null terminator. The flags parameter allows additional controls. + +Arguments: + + pszDest - destination string + + cchDest - size of destination buffer in characters. + + ppszDestEnd - if ppszDestEnd is non-null, the function will return a + pointer to the end of the destination string. If the + function copied any data, the result will point to the + null termination character + + pcchRemaining - if pcchRemaining is non-null, the function will return the + number of characters left in the destination string, + including the null terminator + + dwFlags - controls some details of the string copy: + + STRSAFE_FILL_BEHIND_NULL + if the function succeeds, the low byte of dwFlags will be + used to fill the uninitialize part of destination buffer + behind the null terminator + + STRSAFE_IGNORE_NULLS + treat NULL string pointers like empty strings (TEXT("")). + + STRSAFE_FILL_ON_FAILURE + if the function fails, the low byte of dwFlags will be + used to fill all of the destination buffer, and it will + be null terminated. + + STRSAFE_NO_TRUNCATION / + STRSAFE_NULL_ON_FAILURE + if the function fails, the destination buffer will be set + to the empty string. + +Notes: + pszDest should not be NULL unless the STRSAFE_IGNORE_NULLS flag is specified. + If STRSAFE_IGNORE_NULLS is passed and pszDest is NULL, an error may still be + returned even though NULLS are ignored + + cchDest must be > 1 for this function to succeed. + +Return Value: + + S_OK - data was read from stdin and copied, and the resultant + dest string was null terminated + + failure - you can use the macro HRESULT_CODE() to get a win32 + error code for all hresult failure cases + + STRSAFE_E_END_OF_FILE / + HRESULT_CODE(hr) == ERROR_HANDLE_EOF + - this return value indicates an error or end-of-file + condition, use feof or ferror to determine which one has + occured. + + STRSAFE_E_INSUFFICIENT_BUFFER / + HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER + - this return value is an indication that there was + insufficient space in the destination buffer to copy any + data + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function. + +--*/ + +#ifndef STRSAFE_LIB_IMPL +STRSAFE_INLINE_API StringCchGetsExA(char* pszDest, size_t cchDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags); +#define StringCchGetsEx StringCchGetsExA + +STRSAFE_INLINE_API StringCchGetsExA(char* pszDest, size_t cchDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags) +{ + HRESULT hr; + + if (cchDest > STRSAFE_MAX_CCH) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + size_t cbDest; + + // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1 + cbDest = cchDest * sizeof(char); + + hr = StringGetsExWorkerA(pszDest, cchDest, cbDest, ppszDestEnd, pcchRemaining, dwFlags); + } + + return hr; +} + +#endif // !STRSAFE_NO_CCH_FUNCTIONS +#endif // !STRSAFE_LIB_IMPL + +#ifndef STRSAFE_NO_CB_FUNCTIONS +/*++ + +STDAPI +StringCbGetsEx( + OUT LPTSTR pszDest OPTIONAL, + IN size_t cbDest, + OUT LPTSTR* ppszDestEnd OPTIONAL, + OUT size_t* pcbRemaining OPTIONAL, + IN DWORD dwFlags + ); + +Routine Description: + + This routine is a safer version of the C built-in function 'gets' with + some additional parameters. In addition to functionality provided by + StringCbGets, this routine also returns a pointer to the end of the + destination string and the number of characters left in the destination string + including the null terminator. The flags parameter allows additional controls. + +Arguments: + + pszDest - destination string + + cbDest - size of destination buffer in bytes. + + ppszDestEnd - if ppszDestEnd is non-null, the function will return a + pointer to the end of the destination string. If the + function copied any data, the result will point to the + null termination character + + pcbRemaining - if pbRemaining is non-null, the function will return the + number of bytes left in the destination string, + including the null terminator + + dwFlags - controls some details of the string copy: + + STRSAFE_FILL_BEHIND_NULL + if the function succeeds, the low byte of dwFlags will be + used to fill the uninitialize part of destination buffer + behind the null terminator + + STRSAFE_IGNORE_NULLS + treat NULL string pointers like empty strings (TEXT("")). + + STRSAFE_FILL_ON_FAILURE + if the function fails, the low byte of dwFlags will be + used to fill all of the destination buffer, and it will + be null terminated. + + STRSAFE_NO_TRUNCATION / + STRSAFE_NULL_ON_FAILURE + if the function fails, the destination buffer will be set + to the empty string. + +Notes: + pszDest should not be NULL unless the STRSAFE_IGNORE_NULLS flag is specified. + If STRSAFE_IGNORE_NULLS is passed and pszDest is NULL, an error may still be + returned even though NULLS are ignored + + cbDest must be > sizeof(TCHAR) for this function to succeed + +Return Value: + + S_OK - data was read from stdin and copied, and the resultant + dest string was null terminated + + failure - you can use the macro HRESULT_CODE() to get a win32 + error code for all hresult failure cases + + STRSAFE_E_END_OF_FILE / + HRESULT_CODE(hr) == ERROR_HANDLE_EOF + - this return value indicates an error or end-of-file + condition, use feof or ferror to determine which one has + occured. + + STRSAFE_E_INSUFFICIENT_BUFFER / + HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER + - this return value is an indication that there was + insufficient space in the destination buffer to copy any + data + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function. + +--*/ + +#ifndef STRSAFE_LIB_IMPL +STRSAFE_INLINE_API StringCbGetsExA(char* pszDest, size_t cbDest, char** ppszDestEnd, size_t* pbRemaining, unsigned long dwFlags); +#define StringCbGetsEx StringCbGetsExA + +STRSAFE_INLINE_API StringCbGetsExA(char* pszDest, size_t cbDest, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags) +{ + HRESULT hr; + size_t cchDest; + size_t cchRemaining = 0; + + cchDest = cbDest / sizeof(char); + + if (cchDest > STRSAFE_MAX_CCH) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + hr = StringGetsExWorkerA(pszDest, cchDest, cbDest, ppszDestEnd, &cchRemaining, dwFlags); + } + + if (SUCCEEDED(hr) || + (hr == STRSAFE_E_INSUFFICIENT_BUFFER) || + (hr == STRSAFE_E_END_OF_FILE)) + { + if (pcbRemaining) + { + // safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1 + *pcbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char)); + } + } + + return hr; +} + +#endif // !STRSAFE_NO_CB_FUNCTIONS +#endif // !STRSAFE_LIB_IMPL + +#ifndef STRSAFE_NO_CCH_FUNCTIONS +/*++ + +STDAPI +StringCchLength( + IN LPCTSTR psz, + IN size_t cchMax, + OUT size_t* pcch OPTIONAL + ); + +Routine Description: + + This routine is a safer version of the C built-in function 'strlen'. + It is used to make sure a string is not larger than a given length, and + it optionally returns the current length in characters not including + the null terminator. + + This function returns a hresult, and not a pointer. It returns + S_OK if the string is non-null and the length including the null + terminator is less than or equal to cchMax characters. + +Arguments: + + psz - string to check the length of + + cchMax - maximum number of characters including the null terminator + that psz is allowed to contain + + pcch - if the function succeeds and pcch is non-null, the current length + in characters of psz excluding the null terminator will be returned. + This out parameter is equivalent to the return value of strlen(psz) + +Notes: + psz can be null but the function will fail + + cchMax should be greater than zero or the function will fail + +Return Value: + + S_OK - psz is non-null and the length including the null + terminator is less than or equal to cchMax characters + + failure - you can use the macro HRESULT_CODE() to get a win32 + error code for all hresult failure cases + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function. + +--*/ + +STRSAFEAPI StringCchLengthA(const char* psz, size_t cchMax, size_t* pcch); +#define StringCchLength StringCchLengthA + +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCchLengthA(const char* psz, size_t cchMax, size_t* pcch) +{ + HRESULT hr; + + if ((psz == NULL) || (cchMax > STRSAFE_MAX_CCH)) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + hr = StringLengthWorkerA(psz, cchMax, pcch); + } + + return hr; +} + +#endif // STRSAFE_INLINE +#endif // !STRSAFE_NO_CCH_FUNCTIONS + + +#ifndef STRSAFE_NO_CB_FUNCTIONS +/*++ + +STDAPI +StringCbLength( + IN LPCTSTR psz, + IN size_t cbMax, + OUT size_t* pcb OPTIONAL + ); + +Routine Description: + + This routine is a safer version of the C built-in function 'strlen'. + It is used to make sure a string is not larger than a given length, and + it optionally returns the current length in bytes not including + the null terminator. + + This function returns a hresult, and not a pointer. It returns + S_OK if the string is non-null and the length including the null + terminator is less than or equal to cbMax bytes. + +Arguments: + + psz - string to check the length of + + cbMax - maximum number of bytes including the null terminator + that psz is allowed to contain + + pcb - if the function succeeds and pcb is non-null, the current length + in bytes of psz excluding the null terminator will be returned. + This out parameter is equivalent to the return value of strlen(psz) * sizeof(TCHAR) + +Notes: + psz can be null but the function will fail + + cbMax should be greater than or equal to sizeof(TCHAR) or the function will fail + +Return Value: + + S_OK - psz is non-null and the length including the null + terminator is less than or equal to cbMax bytes + + failure - you can use the macro HRESULT_CODE() to get a win32 + error code for all hresult failure cases + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function. + +--*/ + +STRSAFEAPI StringCbLengthA(const char* psz, size_t cchMax, size_t* pcch); + +#define StringCbLength StringCbLengthA + +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCbLengthA(const char* psz, size_t cbMax, size_t* pcb) +{ + HRESULT hr; + size_t cchMax; + size_t cch = 0; + + cchMax = cbMax / sizeof(char); + + if ((psz == NULL) || (cchMax > STRSAFE_MAX_CCH)) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + hr = StringLengthWorkerA(psz, cchMax, &cch); + } + + if (SUCCEEDED(hr) && pcb) + { + // safe to multiply cch * sizeof(char) since cch < STRSAFE_MAX_CCH and sizeof(char) is 1 + *pcb = cch * sizeof(char); + } + + return hr; +} + +#endif // STRSAFE_INLINE +#endif // !STRSAFE_NO_CB_FUNCTIONS + + +// these are the worker functions that actually do the work +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCopyWorkerA(char* pszDest, size_t cchDest, const char* pszSrc) +{ + HRESULT hr = S_OK; + + if (cchDest == 0) + { + // can not null terminate a zero-byte dest buffer + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + while (cchDest && (*pszSrc != '\0')) + { + *pszDest++ = *pszSrc++; + cchDest--; + } + + if (cchDest == 0) + { + // we are going to truncate pszDest + pszDest--; + hr = STRSAFE_E_INSUFFICIENT_BUFFER; + } + + *pszDest= '\0'; + } + + return hr; +} + +STRSAFEAPI StringCopyExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags) +{ + HRESULT hr = S_OK; + char* pszDestEnd = pszDest; + size_t cchRemaining = 0; + + // ASSERT(cbDest == (cchDest * sizeof(char)) || + // cbDest == (cchDest * sizeof(char)) + (cbDest % sizeof(char))); + + // only accept valid flags + if (dwFlags & (~STRSAFE_VALID_FLAGS)) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + if (dwFlags & STRSAFE_IGNORE_NULLS) + { + if (pszDest == NULL) + { + if ((cchDest != 0) || (cbDest != 0)) + { + // NULL pszDest and non-zero cchDest/cbDest is invalid + hr = STRSAFE_E_INVALID_PARAMETER; + } + } + + if (pszSrc == NULL) + { + pszSrc = ""; + } + } + + if (SUCCEEDED(hr)) + { + if (cchDest == 0) + { + pszDestEnd = pszDest; + cchRemaining = 0; + + // only fail if there was actually src data to copy + if (*pszSrc != '\0') + { + if (pszDest == NULL) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + hr = STRSAFE_E_INSUFFICIENT_BUFFER; + } + } + } + else + { + pszDestEnd = pszDest; + cchRemaining = cchDest; + + while (cchRemaining && (*pszSrc != '\0')) + { + *pszDestEnd++= *pszSrc++; + cchRemaining--; + } + + if (cchRemaining > 0) + { + if (dwFlags & STRSAFE_FILL_BEHIND_NULL) + { + memset(pszDestEnd + 1, STRSAFE_GET_FILL_PATTERN(dwFlags), ((cchRemaining - 1) * sizeof(char)) + (cbDest % sizeof(char))); + } + } + else + { + // we are going to truncate pszDest + pszDestEnd--; + cchRemaining++; + + hr = STRSAFE_E_INSUFFICIENT_BUFFER; + } + + *pszDestEnd = '\0'; + } + } + } + + if (FAILED(hr)) + { + if (pszDest) + { + if (dwFlags & STRSAFE_FILL_ON_FAILURE) + { + memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest); + + if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0) + { + pszDestEnd = pszDest; + cchRemaining = cchDest; + } + else if (cchDest > 0) + { + pszDestEnd = pszDest + cchDest - 1; + cchRemaining = 1; + + // null terminate the end of the string + *pszDestEnd = '\0'; + } + } + + if (dwFlags & (STRSAFE_NULL_ON_FAILURE | STRSAFE_NO_TRUNCATION)) + { + if (cchDest > 0) + { + pszDestEnd = pszDest; + cchRemaining = cchDest; + + // null terminate the beginning of the string + *pszDestEnd = '\0'; + } + } + } + } + + if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER)) + { + if (ppszDestEnd) + { + *ppszDestEnd = pszDestEnd; + } + + if (pcchRemaining) + { + *pcchRemaining = cchRemaining; + } + } + + return hr; +} + +STRSAFEAPI StringCopyNWorkerA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchSrc) +{ + HRESULT hr = S_OK; + + if (cchDest == 0) + { + // can not null terminate a zero-byte dest buffer + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + while (cchDest && cchSrc && (*pszSrc != '\0')) + { + *pszDest++= *pszSrc++; + cchDest--; + cchSrc--; + } + + if (cchDest == 0) + { + // we are going to truncate pszDest + pszDest--; + hr = STRSAFE_E_INSUFFICIENT_BUFFER; + } + + *pszDest= '\0'; + } + + return hr; +} + +STRSAFEAPI StringCopyNExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, size_t cchSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags) +{ + HRESULT hr = S_OK; + char* pszDestEnd = pszDest; + size_t cchRemaining = 0; + + // ASSERT(cbDest == (cchDest * sizeof(char)) || + // cbDest == (cchDest * sizeof(char)) + (cbDest % sizeof(char))); + + // only accept valid flags + if (dwFlags & (~STRSAFE_VALID_FLAGS)) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + if (dwFlags & STRSAFE_IGNORE_NULLS) + { + if (pszDest == NULL) + { + if ((cchDest != 0) || (cbDest != 0)) + { + // NULL pszDest and non-zero cchDest/cbDest is invalid + hr = STRSAFE_E_INVALID_PARAMETER; + } + } + + if (pszSrc == NULL) + { + pszSrc = ""; + } + } + + if (SUCCEEDED(hr)) + { + if (cchDest == 0) + { + pszDestEnd = pszDest; + cchRemaining = 0; + + // only fail if there was actually src data to copy + if (*pszSrc != '\0') + { + if (pszDest == NULL) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + hr = STRSAFE_E_INSUFFICIENT_BUFFER; + } + } + } + else + { + pszDestEnd = pszDest; + cchRemaining = cchDest; + + while (cchRemaining && cchSrc && (*pszSrc != '\0')) + { + *pszDestEnd++= *pszSrc++; + cchRemaining--; + cchSrc--; + } + + if (cchRemaining > 0) + { + if (dwFlags & STRSAFE_FILL_BEHIND_NULL) + { + memset(pszDestEnd + 1, STRSAFE_GET_FILL_PATTERN(dwFlags), ((cchRemaining - 1) * sizeof(char)) + (cbDest % sizeof(char))); + } + } + else + { + // we are going to truncate pszDest + pszDestEnd--; + cchRemaining++; + + hr = STRSAFE_E_INSUFFICIENT_BUFFER; + } + + *pszDestEnd = '\0'; + } + } + } + + if (FAILED(hr)) + { + if (pszDest) + { + if (dwFlags & STRSAFE_FILL_ON_FAILURE) + { + memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest); + + if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0) + { + pszDestEnd = pszDest; + cchRemaining = cchDest; + } + else if (cchDest > 0) + { + pszDestEnd = pszDest + cchDest - 1; + cchRemaining = 1; + + // null terminate the end of the string + *pszDestEnd = '\0'; + } + } + + if (dwFlags & (STRSAFE_NULL_ON_FAILURE | STRSAFE_NO_TRUNCATION)) + { + if (cchDest > 0) + { + pszDestEnd = pszDest; + cchRemaining = cchDest; + + // null terminate the beginning of the string + *pszDestEnd = '\0'; + } + } + } + } + + if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER)) + { + if (ppszDestEnd) + { + *ppszDestEnd = pszDestEnd; + } + + if (pcchRemaining) + { + *pcchRemaining = cchRemaining; + } + } + + return hr; +} + +STRSAFEAPI StringCatWorkerA(char* pszDest, size_t cchDest, const char* pszSrc) +{ + HRESULT hr; + size_t cchDestCurrent; + + hr = StringLengthWorkerA(pszDest, cchDest, &cchDestCurrent); + + if (SUCCEEDED(hr)) + { + hr = StringCopyWorkerA(pszDest + cchDestCurrent, + cchDest - cchDestCurrent, + pszSrc); + } + + return hr; +} + +STRSAFEAPI StringCatExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags) +{ + HRESULT hr = S_OK; + char* pszDestEnd = pszDest; + size_t cchRemaining = 0; + + // ASSERT(cbDest == (cchDest * sizeof(char)) || + // cbDest == (cchDest * sizeof(char)) + (cbDest % sizeof(char))); + + // only accept valid flags + if (dwFlags & (~STRSAFE_VALID_FLAGS)) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + size_t cchDestCurrent; + + if (dwFlags & STRSAFE_IGNORE_NULLS) + { + if (pszDest == NULL) + { + if ((cchDest == 0) && (cbDest == 0)) + { + cchDestCurrent = 0; + } + else + { + // NULL pszDest and non-zero cchDest/cbDest is invalid + hr = STRSAFE_E_INVALID_PARAMETER; + } + } + else + { + hr = StringLengthWorkerA(pszDest, cchDest, &cchDestCurrent); + + if (SUCCEEDED(hr)) + { + pszDestEnd = pszDest + cchDestCurrent; + cchRemaining = cchDest - cchDestCurrent; + } + } + + if (pszSrc == NULL) + { + pszSrc = ""; + } + } + else + { + hr = StringLengthWorkerA(pszDest, cchDest, &cchDestCurrent); + + if (SUCCEEDED(hr)) + { + pszDestEnd = pszDest + cchDestCurrent; + cchRemaining = cchDest - cchDestCurrent; + } + } + + if (SUCCEEDED(hr)) + { + if (cchDest == 0) + { + // only fail if there was actually src data to append + if (*pszSrc != '\0') + { + if (pszDest == NULL) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + hr = STRSAFE_E_INSUFFICIENT_BUFFER; + } + } + } + else + { + // we handle the STRSAFE_FILL_ON_FAILURE and STRSAFE_NULL_ON_FAILURE cases below, so do not pass + // those flags through + hr = StringCopyExWorkerA(pszDestEnd, + cchRemaining, + (cchRemaining * sizeof(char)) + (cbDest % sizeof(char)), + pszSrc, + &pszDestEnd, + &cchRemaining, + dwFlags & (~(STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE))); + } + } + } + + if (FAILED(hr)) + { + if (pszDest) + { + // STRSAFE_NO_TRUNCATION is taken care of by StringCopyExWorkerA() + + if (dwFlags & STRSAFE_FILL_ON_FAILURE) + { + memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest); + + if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0) + { + pszDestEnd = pszDest; + cchRemaining = cchDest; + } + else + if (cchDest > 0) + { + pszDestEnd = pszDest + cchDest - 1; + cchRemaining = 1; + + // null terminate the end of the string + *pszDestEnd = '\0'; + } + } + + if (dwFlags & STRSAFE_NULL_ON_FAILURE) + { + if (cchDest > 0) + { + pszDestEnd = pszDest; + cchRemaining = cchDest; + + // null terminate the beginning of the string + *pszDestEnd = '\0'; + } + } + } + } + + if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER)) + { + if (ppszDestEnd) + { + *ppszDestEnd = pszDestEnd; + } + + if (pcchRemaining) + { + *pcchRemaining = cchRemaining; + } + } + + return hr; +} + +STRSAFEAPI StringCatNWorkerA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchMaxAppend) +{ + HRESULT hr; + size_t cchDestCurrent; + + hr = StringLengthWorkerA(pszDest, cchDest, &cchDestCurrent); + + if (SUCCEEDED(hr)) + { + hr = StringCopyNWorkerA(pszDest + cchDestCurrent, + cchDest - cchDestCurrent, + pszSrc, + cchMaxAppend); + } + + return hr; +} + +STRSAFEAPI StringCatNExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, size_t cchMaxAppend, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags) +{ + HRESULT hr = S_OK; + char* pszDestEnd = pszDest; + size_t cchRemaining = 0; + size_t cchDestCurrent = 0; + + // ASSERT(cbDest == (cchDest * sizeof(char)) || + // cbDest == (cchDest * sizeof(char)) + (cbDest % sizeof(char))); + + // only accept valid flags + if (dwFlags & (~STRSAFE_VALID_FLAGS)) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + if (dwFlags & STRSAFE_IGNORE_NULLS) + { + if (pszDest == NULL) + { + if ((cchDest == 0) && (cbDest == 0)) + { + cchDestCurrent = 0; + } + else + { + // NULL pszDest and non-zero cchDest/cbDest is invalid + hr = STRSAFE_E_INVALID_PARAMETER; + } + } + else + { + hr = StringLengthWorkerA(pszDest, cchDest, &cchDestCurrent); + + if (SUCCEEDED(hr)) + { + pszDestEnd = pszDest + cchDestCurrent; + cchRemaining = cchDest - cchDestCurrent; + } + } + + if (pszSrc == NULL) + { + pszSrc = ""; + } + } + else + { + hr = StringLengthWorkerA(pszDest, cchDest, &cchDestCurrent); + + if (SUCCEEDED(hr)) + { + pszDestEnd = pszDest + cchDestCurrent; + cchRemaining = cchDest - cchDestCurrent; + } + } + + if (SUCCEEDED(hr)) + { + if (cchDest == 0) + { + // only fail if there was actually src data to append + if (*pszSrc != '\0') + { + if (pszDest == NULL) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + hr = STRSAFE_E_INSUFFICIENT_BUFFER; + } + } + } + else + { + // we handle the STRSAFE_FILL_ON_FAILURE and STRSAFE_NULL_ON_FAILURE cases below, so do not pass + // those flags through + hr = StringCopyNExWorkerA(pszDestEnd, + cchRemaining, + (cchRemaining * sizeof(char)) + (cbDest % sizeof(char)), + pszSrc, + cchMaxAppend, + &pszDestEnd, + &cchRemaining, + dwFlags & (~(STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE))); + } + } + } + + if (FAILED(hr)) + { + if (pszDest) + { + // STRSAFE_NO_TRUNCATION is taken care of by StringCopyNExWorkerA() + + if (dwFlags & STRSAFE_FILL_ON_FAILURE) + { + memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest); + + if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0) + { + pszDestEnd = pszDest; + cchRemaining = cchDest; + } + else if (cchDest > 0) + { + pszDestEnd = pszDest + cchDest - 1; + cchRemaining = 1; + + // null terminate the end of the string + *pszDestEnd = '\0'; + } + } + + if (dwFlags & (STRSAFE_NULL_ON_FAILURE)) + { + if (cchDest > 0) + { + pszDestEnd = pszDest; + cchRemaining = cchDest; + + // null terminate the beginning of the string + *pszDestEnd = '\0'; + } + } + } + } + + if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER)) + { + if (ppszDestEnd) + { + *ppszDestEnd = pszDestEnd; + } + + if (pcchRemaining) + { + *pcchRemaining = cchRemaining; + } + } + + return hr; +} + +STRSAFEAPI StringVPrintfWorkerA(char* pszDest, size_t cchDest, const char* pszFormat, va_list argList) +{ + HRESULT hr = S_OK; + + if (cchDest == 0) + { + // can not null terminate a zero-byte dest buffer + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + int iRet; + size_t cchMax; + + // leave the last space for the null terminator + cchMax = cchDest - 1; + + iRet = vsnprintf(pszDest, cchMax, pszFormat, argList); + // ASSERT((iRet < 0) || (((size_t)iRet) <= cchMax)); + + if ((iRet < 0) || (((size_t)iRet) > cchMax)) + { + // need to null terminate the string + pszDest += cchMax; + *pszDest = '\0'; + + // we have truncated pszDest + hr = STRSAFE_E_INSUFFICIENT_BUFFER; + } + else if (((size_t)iRet) == cchMax) + { + // need to null terminate the string + pszDest += cchMax; + *pszDest = '\0'; + } + } + + return hr; +} + +STRSAFEAPI StringVPrintfExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const char* pszFormat, va_list argList) +{ + HRESULT hr = S_OK; + char* pszDestEnd = pszDest; + size_t cchRemaining = 0; + + // ASSERT(cbDest == (cchDest * sizeof(char)) || + // cbDest == (cchDest * sizeof(char)) + (cbDest % sizeof(char))); + + // only accept valid flags + if (dwFlags & (~STRSAFE_VALID_FLAGS)) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + if (dwFlags & STRSAFE_IGNORE_NULLS) + { + if (pszDest == NULL) + { + if ((cchDest != 0) || (cbDest != 0)) + { + // NULL pszDest and non-zero cchDest/cbDest is invalid + hr = STRSAFE_E_INVALID_PARAMETER; + } + } + + if (pszFormat == NULL) + { + pszFormat = ""; + } + } + + if (SUCCEEDED(hr)) + { + if (cchDest == 0) + { + pszDestEnd = pszDest; + cchRemaining = 0; + + // only fail if there was actually a non-empty format string + if (*pszFormat != '\0') + { + if (pszDest == NULL) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + hr = STRSAFE_E_INSUFFICIENT_BUFFER; + } + } + } + else + { + int iRet; + size_t cchMax; + + // leave the last space for the null terminator + cchMax = cchDest - 1; + + iRet = vsnprintf(pszDest, cchMax, pszFormat, argList); + // ASSERT((iRet < 0) || (((size_t)iRet) <= cchMax)); + + if ((iRet < 0) || (((size_t)iRet) > cchMax)) + { + // we have truncated pszDest + pszDestEnd = pszDest + cchMax; + cchRemaining = 1; + + // need to null terminate the string + *pszDestEnd = '\0'; + + hr = STRSAFE_E_INSUFFICIENT_BUFFER; + } + else if (((size_t)iRet) == cchMax) + { + // string fit perfectly + pszDestEnd = pszDest + cchMax; + cchRemaining = 1; + + // need to null terminate the string + *pszDestEnd = '\0'; + } + else if (((size_t)iRet) < cchMax) + { + // there is extra room + pszDestEnd = pszDest + iRet; + cchRemaining = cchDest - iRet; + + if (dwFlags & STRSAFE_FILL_BEHIND_NULL) + { + memset(pszDestEnd + 1, STRSAFE_GET_FILL_PATTERN(dwFlags), ((cchRemaining - 1) * sizeof(char)) + (cbDest % sizeof(char))); + } + } + } + } + } + + if (FAILED(hr)) + { + if (pszDest) + { + if (dwFlags & STRSAFE_FILL_ON_FAILURE) + { + memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest); + + if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0) + { + pszDestEnd = pszDest; + cchRemaining = cchDest; + } + else if (cchDest > 0) + { + pszDestEnd = pszDest + cchDest - 1; + cchRemaining = 1; + + // null terminate the end of the string + *pszDestEnd = '\0'; + } + } + + if (dwFlags & (STRSAFE_NULL_ON_FAILURE | STRSAFE_NO_TRUNCATION)) + { + if (cchDest > 0) + { + pszDestEnd = pszDest; + cchRemaining = cchDest; + + // null terminate the beginning of the string + *pszDestEnd = '\0'; + } + } + } + } + + if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER)) + { + if (ppszDestEnd) + { + *ppszDestEnd = pszDestEnd; + } + + if (pcchRemaining) + { + *pcchRemaining = cchRemaining; + } + } + + return hr; +} + +STRSAFEAPI StringLengthWorkerA(const char* psz, size_t cchMax, size_t* pcch) +{ + HRESULT hr = S_OK; + size_t cchMaxPrev = cchMax; + + while (cchMax && (*psz != '\0')) + { + psz++; + cchMax--; + } + + if (cchMax == 0) + { + // the string is longer than cchMax + hr = STRSAFE_E_INVALID_PARAMETER; + } + + if (SUCCEEDED(hr) && pcch) + { + *pcch = cchMaxPrev - cchMax; + } + + return hr; +} + +#endif // STRSAFE_INLINE + +#ifndef STRSAFE_LIB_IMPL +STRSAFE_INLINE_API StringGetsExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags) +{ + HRESULT hr = S_OK; + char* pszDestEnd = pszDest; + size_t cchRemaining = 0; + + // ASSERT(cbDest == (cchDest * sizeof(char)) || + // cbDest == (cchDest * sizeof(char)) + (cbDest % sizeof(char))); + + // only accept valid flags + if (dwFlags & (~STRSAFE_VALID_FLAGS)) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + if (dwFlags & STRSAFE_IGNORE_NULLS) + { + if (pszDest == NULL) + { + if ((cchDest != 0) || (cbDest != 0)) + { + // NULL pszDest and non-zero cchDest/cbDest is invalid + hr = STRSAFE_E_INVALID_PARAMETER; + } + } + } + + if (SUCCEEDED(hr)) + { + if (cchDest <= 1) + { + pszDestEnd = pszDest; + cchRemaining = cchDest; + + if (cchDest == 1) + { + *pszDestEnd = '\0'; + } + + hr = STRSAFE_E_INSUFFICIENT_BUFFER; + } + else + { + char ch; + + pszDestEnd = pszDest; + cchRemaining = cchDest; + + while ((cchRemaining > 1) && (ch = (char)getc(stdin)) != '\n') + { + if (ch == EOF) + { + if (pszDestEnd == pszDest) + { + // we failed to read anything from stdin + hr = STRSAFE_E_END_OF_FILE; + } + break; + } + + *pszDestEnd = ch; + + pszDestEnd++; + cchRemaining--; + } + + if (cchRemaining > 0) + { + // there is extra room + if (dwFlags & STRSAFE_FILL_BEHIND_NULL) + { + memset(pszDestEnd + 1, STRSAFE_GET_FILL_PATTERN(dwFlags), ((cchRemaining - 1) * sizeof(char)) + (cbDest % sizeof(char))); + } + } + + *pszDestEnd = '\0'; + } + } + } + + if (FAILED(hr)) + { + if (pszDest) + { + if (dwFlags & STRSAFE_FILL_ON_FAILURE) + { + memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest); + + if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0) + { + pszDestEnd = pszDest; + cchRemaining = cchDest; + } + else if (cchDest > 0) + { + pszDestEnd = pszDest + cchDest - 1; + cchRemaining = 1; + + // null terminate the end of the string + *pszDestEnd = '\0'; + } + } + + if (dwFlags & (STRSAFE_NULL_ON_FAILURE | STRSAFE_NO_TRUNCATION)) + { + if (cchDest > 0) + { + pszDestEnd = pszDest; + cchRemaining = cchDest; + + // null terminate the beginning of the string + *pszDestEnd = '\0'; + } + } + } + } + + if (SUCCEEDED(hr) || + (hr == STRSAFE_E_INSUFFICIENT_BUFFER) || + (hr == STRSAFE_E_END_OF_FILE)) + { + if (ppszDestEnd) + { + *ppszDestEnd = pszDestEnd; + } + + if (pcchRemaining) + { + *pcchRemaining = cchRemaining; + } + } + + return hr; +} + +#endif // !STRSAFE_LIB_IMPL + + +// Do not call these functions, they are worker functions for internal use within this file +#ifdef DEPRECATE_SUPPORTED +#pragma deprecated(StringCopyWorkerA) +#pragma deprecated(StringCopyWorkerW) +#pragma deprecated(StringCopyExWorkerA) +#pragma deprecated(StringCopyExWorkerW) +#pragma deprecated(StringCatWorkerA) +#pragma deprecated(StringCatWorkerW) +#pragma deprecated(StringCatExWorkerA) +#pragma deprecated(StringCatExWorkerW) +#pragma deprecated(StringCatNWorkerA) +#pragma deprecated(StringCatNWorkerW) +#pragma deprecated(StringCatNExWorkerA) +#pragma deprecated(StringCatNExWorkerW) +#pragma deprecated(StringVPrintfWorkerA) +#pragma deprecated(StringVPrintfWorkerW) +#pragma deprecated(StringVPrintfExWorkerA) +#pragma deprecated(StringVPrintfExWorkerW) +#pragma deprecated(StringLengthWorkerA) +#pragma deprecated(StringLengthWorkerW) +#else +#define StringCopyWorkerA StringCopyWorkerA_instead_use_StringCchCopyA_or_StringCchCopyExA; +#define StringCopyWorkerW StringCopyWorkerW_instead_use_StringCchCopyW_or_StringCchCopyExW; +#define StringCopyExWorkerA StringCopyExWorkerA_instead_use_StringCchCopyA_or_StringCchCopyExA; +#define StringCopyExWorkerW StringCopyExWorkerW_instead_use_StringCchCopyW_or_StringCchCopyExW; +#define StringCatWorkerA StringCatWorkerA_instead_use_StringCchCatA_or_StringCchCatExA; +#define StringCatWorkerW StringCatWorkerW_instead_use_StringCchCatW_or_StringCchCatExW; +#define StringCatExWorkerA StringCatExWorkerA_instead_use_StringCchCatA_or_StringCchCatExA; +#define StringCatExWorkerW StringCatExWorkerW_instead_use_StringCchCatW_or_StringCchCatExW; +#define StringCatNWorkerA StringCatNWorkerA_instead_use_StringCchCatNA_or_StrincCbCatNA; +#define StringCatNWorkerW StringCatNWorkerW_instead_use_StringCchCatNW_or_StringCbCatNW; +#define StringCatNExWorkerA StringCatNExWorkerA_instead_use_StringCchCatNExA_or_StringCbCatNExA; +#define StringCatNExWorkerW StringCatNExWorkerW_instead_use_StringCchCatNExW_or_StringCbCatNExW; +#define StringVPrintfWorkerA StringVPrintfWorkerA_instead_use_StringCchVPrintfA_or_StringCchVPrintfExA; +#define StringVPrintfWorkerW StringVPrintfWorkerW_instead_use_StringCchVPrintfW_or_StringCchVPrintfExW; +#define StringVPrintfExWorkerA StringVPrintfExWorkerA_instead_use_StringCchVPrintfA_or_StringCchVPrintfExA; +#define StringVPrintfExWorkerW StringVPrintfExWorkerW_instead_use_StringCchVPrintfW_or_StringCchVPrintfExW; +#define StringLengthWorkerA StringLengthWorkerA_instead_use_StringCchLengthA_or_StringCbLengthA; +#define StringLengthWorkerW StringLengthWorkerW_instead_use_StringCchLengthW_or_StringCbLengthW; +#endif // !DEPRECATE_SUPPORTED + +#ifdef _NTSTRSAFE_H_INCLUDED_ +#pragma warning(pop) +#endif // _NTSTRSAFE_H_INCLUDED_ + +#endif diff --git a/Src/replicant/nu/utf.c b/Src/replicant/nu/utf.c new file mode 100644 index 00000000..04495aa5 --- /dev/null +++ b/Src/replicant/nu/utf.c @@ -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; + +} diff --git a/Src/replicant/nu/utf.h b/Src/replicant/nu/utf.h new file mode 100644 index 00000000..cc1437e1 --- /dev/null +++ b/Src/replicant/nu/utf.h @@ -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 diff --git a/Src/replicant/nu/win-amd64/LockFreeLIFO.h b/Src/replicant/nu/win-amd64/LockFreeLIFO.h new file mode 100644 index 00000000..5cf087e7 --- /dev/null +++ b/Src/replicant/nu/win-amd64/LockFreeLIFO.h @@ -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 diff --git a/Src/replicant/nu/win-amd64/ThreadLoop.cpp b/Src/replicant/nu/win-amd64/ThreadLoop.cpp new file mode 100644 index 00000000..4c78a431 --- /dev/null +++ b/Src/replicant/nu/win-amd64/ThreadLoop.cpp @@ -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); +} diff --git a/Src/replicant/nu/win-amd64/ThreadLoop.h b/Src/replicant/nu/win-amd64/ThreadLoop.h new file mode 100644 index 00000000..f1552b51 --- /dev/null +++ b/Src/replicant/nu/win-amd64/ThreadLoop.h @@ -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; +};
\ No newline at end of file diff --git a/Src/replicant/nu/win-x86/LockFreeLIFO.asm b/Src/replicant/nu/win-x86/LockFreeLIFO.asm new file mode 100644 index 00000000..dd67e5b1 --- /dev/null +++ b/Src/replicant/nu/win-x86/LockFreeLIFO.asm @@ -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 + diff --git a/Src/replicant/nu/win-x86/LockFreeLIFO.c b/Src/replicant/nu/win-x86/LockFreeLIFO.c new file mode 100644 index 00000000..ecffde9a --- /dev/null +++ b/Src/replicant/nu/win-x86/LockFreeLIFO.c @@ -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); +}
\ No newline at end of file diff --git a/Src/replicant/nu/win-x86/LockFreeLIFO.h b/Src/replicant/nu/win-x86/LockFreeLIFO.h new file mode 100644 index 00000000..90ba9d33 --- /dev/null +++ b/Src/replicant/nu/win-x86/LockFreeLIFO.h @@ -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 diff --git a/Src/replicant/nu/win-x86/ThreadLoop.cpp b/Src/replicant/nu/win-x86/ThreadLoop.cpp new file mode 100644 index 00000000..5dae339d --- /dev/null +++ b/Src/replicant/nu/win-x86/ThreadLoop.cpp @@ -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); +} diff --git a/Src/replicant/nu/win-x86/ThreadLoop.h b/Src/replicant/nu/win-x86/ThreadLoop.h new file mode 100644 index 00000000..cc412386 --- /dev/null +++ b/Src/replicant/nu/win-x86/ThreadLoop.h @@ -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; +};
\ No newline at end of file diff --git a/Src/replicant/nu/win/MessageLoop.cpp b/Src/replicant/nu/win/MessageLoop.cpp new file mode 100644 index 00000000..2353801c --- /dev/null +++ b/Src/replicant/nu/win/MessageLoop.cpp @@ -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; +} diff --git a/Src/replicant/nu/win/MessageLoop.h b/Src/replicant/nu/win/MessageLoop.h new file mode 100644 index 00000000..88e4520f --- /dev/null +++ b/Src/replicant/nu/win/MessageLoop.h @@ -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; + }; +}
\ No newline at end of file diff --git a/Src/replicant/nu/win/ThreadLoop.cpp b/Src/replicant/nu/win/ThreadLoop.cpp new file mode 100644 index 00000000..3ff89e59 --- /dev/null +++ b/Src/replicant/nu/win/ThreadLoop.cpp @@ -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); +} diff --git a/Src/replicant/nu/win/ThreadLoop.h b/Src/replicant/nu/win/ThreadLoop.h new file mode 100644 index 00000000..22f16c3d --- /dev/null +++ b/Src/replicant/nu/win/ThreadLoop.h @@ -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; +};
\ No newline at end of file diff --git a/Src/replicant/nu/x86/ByteWriter.h b/Src/replicant/nu/x86/ByteWriter.h new file mode 100644 index 00000000..d38f0c95 --- /dev/null +++ b/Src/replicant/nu/x86/ByteWriter.h @@ -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++; +} |