aboutsummaryrefslogtreecommitdiff
path: root/Src/replicant/nu
diff options
context:
space:
mode:
authorJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
committerJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
commit20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/replicant/nu
parent537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff)
downloadwinamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz
Initial community commit
Diffstat (limited to 'Src/replicant/nu')
-rw-r--r--Src/replicant/nu/AutoBuffer.h49
-rw-r--r--Src/replicant/nu/AutoChar.h141
-rw-r--r--Src/replicant/nu/AutoLock.h377
-rw-r--r--Src/replicant/nu/AutoWide.h50
-rw-r--r--Src/replicant/nu/Benchmark.h44
-rw-r--r--Src/replicant/nu/BitReader.cpp109
-rw-r--r--Src/replicant/nu/BitReader.h18
-rw-r--r--Src/replicant/nu/ByteReader.c203
-rw-r--r--Src/replicant/nu/ByteReader.h83
-rw-r--r--Src/replicant/nu/ByteWriter.c77
-rw-r--r--Src/replicant/nu/ByteWriter.h48
-rw-r--r--Src/replicant/nu/LockFreeFIFO.cpp110
-rw-r--r--Src/replicant/nu/LockFreeFIFO.h34
-rw-r--r--Src/replicant/nu/LockFreeItem.h35
-rw-r--r--Src/replicant/nu/LockFreeLIFO.c61
-rw-r--r--Src/replicant/nu/LockFreeLIFO.h18
-rw-r--r--Src/replicant/nu/LockFreeRingBuffer.cpp421
-rw-r--r--Src/replicant/nu/LockFreeRingBuffer.h52
-rw-r--r--Src/replicant/nu/MessageLoop.h16
-rw-r--r--Src/replicant/nu/Pairs.h15
-rw-r--r--Src/replicant/nu/ProgressTracker.cpp117
-rw-r--r--Src/replicant/nu/ProgressTracker.h24
-rw-r--r--Src/replicant/nu/RingBuffer.cpp464
-rw-r--r--Src/replicant/nu/RingBuffer.h61
-rw-r--r--Src/replicant/nu/SafeSize.h51
-rw-r--r--Src/replicant/nu/ThreadLoop.h16
-rw-r--r--Src/replicant/nu/lfitem.c25
-rw-r--r--Src/replicant/nu/lfitem.h23
-rw-r--r--Src/replicant/nu/lfmpscq.c50
-rw-r--r--Src/replicant/nu/lfmpscq.h23
-rw-r--r--Src/replicant/nu/lfringbuffer.c158
-rw-r--r--Src/replicant/nu/lfringbuffer.h46
-rw-r--r--Src/replicant/nu/nodelist.c71
-rw-r--r--Src/replicant/nu/nodelist.h24
-rw-r--r--Src/replicant/nu/ns_wc.h51
-rw-r--r--Src/replicant/nu/nu.sln28
-rw-r--r--Src/replicant/nu/nu.vcxproj267
-rw-r--r--Src/replicant/nu/nu.vcxproj.filters107
-rw-r--r--Src/replicant/nu/precomp.h18
-rw-r--r--Src/replicant/nu/queue_node.h31
-rw-r--r--Src/replicant/nu/strsafe.h4497
-rw-r--r--Src/replicant/nu/utf.c649
-rw-r--r--Src/replicant/nu/utf.h26
-rw-r--r--Src/replicant/nu/win-amd64/LockFreeLIFO.h34
-rw-r--r--Src/replicant/nu/win-amd64/ThreadLoop.cpp70
-rw-r--r--Src/replicant/nu/win-amd64/ThreadLoop.h38
-rw-r--r--Src/replicant/nu/win-x86/LockFreeLIFO.asm50
-rw-r--r--Src/replicant/nu/win-x86/LockFreeLIFO.c48
-rw-r--r--Src/replicant/nu/win-x86/LockFreeLIFO.h31
-rw-r--r--Src/replicant/nu/win-x86/ThreadLoop.cpp84
-rw-r--r--Src/replicant/nu/win-x86/ThreadLoop.h37
-rw-r--r--Src/replicant/nu/win/MessageLoop.cpp121
-rw-r--r--Src/replicant/nu/win/MessageLoop.h45
-rw-r--r--Src/replicant/nu/win/ThreadLoop.cpp146
-rw-r--r--Src/replicant/nu/win/ThreadLoop.h40
-rw-r--r--Src/replicant/nu/x86/ByteWriter.h85
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++;
+}