Skip to content

Instantly share code, notes, and snippets.

@Quackward
Created December 6, 2019 19:57
Show Gist options
  • Save Quackward/2ceabaa0a42e888b5962cccd30a6a884 to your computer and use it in GitHub Desktop.
Save Quackward/2ceabaa0a42e888b5962cccd30a6a884 to your computer and use it in GitHub Desktop.
/*
Dowsing
Copyright 2020 Clairvoire 2020
clairvoire@gmail.com //// \
| |
[ ] ___| _|___
// ` v`) __ __ ||___|___|_|
o / \ o/ o/ |_|_____|_||
> o o / \ _____\|v v / |____
LEGAL -------------------------------------------------------------------------------------------
Licensed under the Apache License, Version 2.0 (the "License") with additional restrictions;
you may not use this file or included executable except in compliance with this License
and additional restrictions*** appended below.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*** Additional Restrictions:
This file and included executable may NOT be used in the development or application of the following:
weapons, weapon platforms such as drones or guidance systems, surveillance equipment or analysis,
combat training simulations, the non-consenting or coerced collection of personally identifiable
information or the analysis of such a dataset, applications or games made in collaboration with
military or police organizations, or automated systems for submitting human-like statements or
interations to online forums when this system does not prodominately disclose its automated nature.
--------------------------------------------------------------------------------------------------
Usage:
To setup, include this header into a .cpp file AND define DOWSING_IMPLEMENT like this:
(order matters, and do the #define in ONE .cpp file, otherwise you'll be defining symbols twice which is bad)
#define DOWSING_IMPLEMENT
#include "Dowsing.h"
Then include Dowsing.h into any files that use the dowsing functions. That's it!
Here are the functions (C macros) you'll want to use mainly:
> dowsingGroup(uint count)
> dowsingVar([float/uint/int] x)
> dowsingStr([const] char* str)
> dowsingStrLen([const] char* str, uint len)
> dowsingTail([const] char* str)
> dowsingPrompt()
`dowsingVar(value)` will display that value in the Dowser; use `value = dowsingVar(value)` to allow the Dowser to change that value
`dowsingStr(string)` will display a string in the Dowser; use `string = dowsingStr(string)` or something similar to allow Dowser to change it
`dowsingStrLen(string)` is the same, but has a strict length (this allows for non-null-terminated strings, or for sub strings with `dowsingStrLen(string + start, len)`
`dowsingTail(string)` will feed strings constantly to the Dowser just like -tail in a unix thingy (imagine it as a chat log)
`dowsingPrompt()` returns a string (const char *) when the Dowser is given a string, otherwise it returns NULL
`dowsingGroup(count)` will group UP TO FOUR (4) dowsingVar() after it into a single variable group, depending on `count`
Use this with 2 or 3 vars specifically to use spacial x,y,z graphs. `dowsingStr()` cannot be grouped.
Just use `dowsingVar()` with no ranging if within a `dowingGroup____()`; var ranges are ignored, as the group's range will override them.
`dowsingGroup_____()` and `dowsingVar_____()` have different types of ranges you can select (groups use "range" by default, var use "auto" by default)
`....One` takes zero args and has a range of 0 to 1
`....Normal` takes zero args and has a range of -1 to 1
`....Range` takes two args for a min and max (max does not need to be higher than min)
`....Wave` takes one arg for a range of -arg to arg
`....Zero` takes one arg for a range of 0 to arg
`....Auto` takes zero args and just tries to pick the best range automatically (it will always include 0)
Limitations / Bugs:
* if you try to dows the same dowsing command on the same code line, it'll confuse the dowser (it uses varname + filename + linenum to generate a unique id for each var)
* it cannot handle if the dowser is is used in multiple threads for now, so it must be used in only one thread
*/
#ifndef _DOWSING_H_
#define _DOWSING_H_
//#define DOWSING_IMPLEMENT_MULTITHREADED
//#define DOWSING_IMPLEMENT
#ifdef DOWSING_IMPLEMENT_MULTITHREADED
#error "Sorry, DOWSING_IMPLEMENT_MULTITHREADED isn't ready yet, you'll have to use DOWSING_IMPLEMENT"
#endif
#define CV_STRINGIFY(x) #x
#define CV_STRINGIFY_MORE(x) CV_STRINGIFY(x)
#if defined(FINAL_RELEASE) || defined(SHIPPING) || !(defined(_WIN64) || defined(_WIN32))
#define NO_DOWSING
#endif
#ifndef NO_DOWSING
// grouping functions for tying variables together (limit of 4 total)
#define dowsingGroupOne( count) dowsingPrivate::groupRange(CV_STRINGIFY_MORE(__FILE__), __LINE__, (count), 0.0f, 1.0f)
#define dowsingGroupNormal( count) dowsingPrivate::groupRange(CV_STRINGIFY_MORE(__FILE__), __LINE__, (count), -1.0f, 1.0f)
#define dowsingGroupRange( count, min, max) dowsingPrivate::groupRange(CV_STRINGIFY_MORE(__FILE__), __LINE__, (count), (min), (max))
#define dowsingGroupWave( count, range) dowsingPrivate::groupRange(CV_STRINGIFY_MORE(__FILE__), __LINE__, (count), -(range), (range))
#define dowsingGroupZero( count, rangeFrom0) dowsingPrivate::groupRange(CV_STRINGIFY_MORE(__FILE__), __LINE__, (count), 0.f, (rangeFrom0))
#define dowsingGroupAuto( count) dowsingPrivate::groupAuto(CV_STRINGIFY_MORE(__FILE__), __LINE__, (count))
#define dowsingGroup( count, min, max) dowsingGroupRange((count), (min), (max))
// variable probing functions (numbers)
#define dowsingVarOne( x) dowsingPrivate::varRange(CV_STRINGIFY_MORE(__FILE__), __LINE__, (#x), (x), 0.f, 1.f)
#define dowsingVarNormal( x) dowsingPrivate::varRange(CV_STRINGIFY_MORE(__FILE__), __LINE__, (#x), (x), -1.f, 1.f)
#define dowsingVarRange( x, min, max) dowsingPrivate::varRange(CV_STRINGIFY_MORE(__FILE__), __LINE__, (#x), (x), (min), (max))
#define dowsingVarWave( x, range) dowsingPrivate::varRange(CV_STRINGIFY_MORE(__FILE__), __LINE__, (#x), (x), -(range), (range))
#define dowsingVarZero( x, rangeFrom0) dowsingPrivate::varRange(CV_STRINGIFY_MORE(__FILE__), __LINE__, (#x), (x), 0.f, (rangeFrom0))
#define dowsingVarAuto( x) dowsingPrivate::varAuto(CV_STRINGIFY_MORE(__FILE__), __LINE__, (#x), (x))
#define dowsingVar( x) dowsingVarAuto((x))
// variable probing functions (strings)
#define dowsingStr( str) dowsingPrivate::varStr(CV_STRINGIFY_MORE(__FILE__), __LINE__, (#str), (str), false)
#define dowsingStrLen( str, len) dowsingPrivate::varStr(CV_STRINGIFY_MORE(__FILE__), __LINE__, (#str), (str), false, (len))
#define dowsingTail( str) dowsingPrivate::varTail(CV_STRINGIFY_MORE(__FILE__), __LINE__, (#str), (str), true)
#define dowsingPrompt() dowsingPrivate::prompt(CV_STRINGIFY_MORE(__FILE__), __LINE__)
// optional / settings
// (you don't have to open or close the dowser; IF you open it this way however, you WILL have to close it manually too)
#define dowsingOpenManually() dowsingPrivate::open(true)
#define dowsingCloseManually() dowsingPrivate::close(true)
#else
#define dowsingGroupOne( count) do{}while(0)
#define dowsingGroupNormal( count) do{}while(0)
#define dowsingGroupRange( count, min, max) do{}while(0)
#define dowsingGroupWave( count, range) do{}while(0)
#define dowsingGroupZero( count, rangeFrom0) do{}while(0)
#define dowsingGroupAuto( count) do{}while(0)
#define dowsingGroup( count, min, max) do{}while(0)
#define dowsingVarOne( x) (x)
#define dowsingVarNormal( x) (x)
#define dowsingVarRange( x, min, max) (x)
#define dowsingVarWave( x, range) (x)
#define dowsingVarZero( x, rangeFrom0) (x)
#define dowsingVarAuto( x) (x)
#define dowsingVar( x) (x)
#define dowsingStr( str) (str)
#define dowsingStrLen( str, len) (str)
#define dowsingTail( str) (str)
#define dowsingPrompt() 0
#define dowsingOpenManually() do{}while(0)
#define dowsingCloseManually() do{}while(0)
#endif
namespace dowsingPrivate {
void open(bool manual);
void close(bool manual);
void groupAuto(const char * locFile, unsigned long locLine, unsigned long count);
void groupRange(const char * locFile, unsigned long locLine, unsigned long count, float min, float max);
float varAuto(const char * locFile, unsigned long locLine, const char * varName, float x);
long varAuto(const char * locFile, unsigned long locLine, const char * varName, long x);
unsigned long varAuto(const char * locFile, unsigned long locLine, const char * varName, unsigned long x);
float varRange(const char * locFile, unsigned long locLine, const char * varName, float x, float min, float max);
long varRange(const char * locFile, unsigned long locLine, const char * varName, long x, float min, float max);
unsigned long varRange(const char * locFile, unsigned long locLine, const char * varName, unsigned long x, float min, float max);
unsigned long varAuto(const char * locFile, unsigned long locLine, const char * varName, unsigned long x);
unsigned long varRange(const char * locFile, unsigned long locLine, const char * varName, unsigned long x, float min, float max);
const char * varStr(const char * locFile, unsigned long locLine, const char * varName, const char * input, unsigned long lenGiven);
char * varStr(const char * locFile, unsigned long locLine, const char * varName, char * input, unsigned long lenGiven);
const char * varTail(const char * locFile, unsigned long locLine, const char * varName, const char * input);
char * varTail(const char * locFile, unsigned long locLine, const char * varName, char * input);
const char * prompt(const char * locFile, unsigned long locLine);
}
#if (defined(DOWSING_IMPLEMENT) || defined(DOWSING_IMPLEMENT_MULTITHREADED)) && !defined(DOWSING_IMPLEMENTED_DONT_DO_IT_AGAIN)
// replace these if you wanna
#ifndef DOWSING_EXE_PATH
#define DOWSING_EXE_PATH "Dowsing.exe"
#endif
#define DOWSING_MALLOC(type, count) new type[(count)];
#define DOWSING_FREE(ptr) delete [] (ptr);
#define DOWSING_ASSERT(x) assert((x));
#include <cstdint>
#include <cstring>
#include <cstdio>
#include <cassert>
#ifdef DOWSING_IMPLEMENT_MULTITHREADED
#include <mutex>
#endif
#include <Windows.h>
// assertions
static_assert(sizeof(float) == sizeof(uint32_t) && sizeof(float) == 4, "dowsing needs you to stop trying to use it on the cray mainframe");
static_assert(alignof(float) <= 4, "dowsing needs float to align to 4 bytes");
static_assert(sizeof(uintptr_t) >= sizeof(uint32_t), "dowsing needs addr size of machine to be at least 32bit lmao");
static_assert(alignof(uintptr_t) >= alignof(float), "dowsing needs alignment of float to be as permissive or more than uintptr_t");
/*
There are 4 buffers, 2 for double-sided sending, 2 for double-sided receiving, all four are owned by the host (this program)
We'll call these txA, txB, rxA, rxB
[ Host ] <--- [rxA --- rxB] <-------------------- [ Dowser ]
[ Host ] ---> [txA --- txB] --------------------> [ Dowser ]
(host gives data to txA and uses rxA until rxB indicates it has finished being written to)
(flip buffers by writing to txA it is ready to be read)
(if txA must be resized (either by us or as a request), indicate it in txA and dowser will rapidly read it and re-switch to the new txB pad when it's ready without a rx send step)
[ Host ] <--- [rxB --- rxA] <-------------------- [ Dowser ]
[ Host ] ---> [txB --- txA] --------------------> [ Dowser ]
*/
namespace dowsingPrivate {
// state
enum DirectoryLocations {
dlSizeTX,
dlAddrTX0, // pointers get two slots
dlAddrTX1,
dlWindowDied,
dl__size
};
// serves as a static memory location for data we need to find other buffers
volatile uint32_t _directory[dl__size] {0};
struct GroupRegisterNode {
uint32_t hash; // top bit of hash defines the type (0 is num, 1 is text)
uint32_t line;
const char * filename;
float min;
float max;
bool autoRange;
};
struct VarRegisterNode {
uint32_t hash; // top bit of hash defines the type (0 is num, 1 is text)
uint32_t line;
const char * varname;
const char * filename;
};
struct VarRegisterExtendedData {
uint32_t groupID {0}; // no grouping if 0, cannot change grouping after first encounter, is why it's here
uint16_t type {0}; // 0: number, 1: string
bool bypass {false};
bool acquire {false};
union {
struct {
float value;
float min;
float max;
bool autoRange;
}num;
struct {
uint32_t len;
uint32_t givenLen;
uint32_t reserve;
uint8_t * c;
}str;
};
uint8_t shortVarname[16] {0};
uint8_t shortFilename[16] {0};
};
uint32_t _varRegisterCount = 0;
uint32_t _varRegisterReserve = 0;
VarRegisterNode * _varRegisters = NULL;
VarRegisterExtendedData * _varRegistersExtended = NULL;
GroupRegisterNode * _groupRegisters = NULL;
uint32_t _groupRegisterCount = 0;
uint32_t _groupRegisterReserve = 0;
uint32_t _groupAcc = 0;
uint32_t _groupRemaining = 0;
bool _initialized = false;
bool _stayDead = false;
bool _performUpload = false;
HANDLE _winDowserHandle = 0;
HANDLE _winOurHandle = 0;
// end state
static const uint8_t ELLIPSIS_NUM = '@';
struct ReceiveVar {
uint32_t index;
uint32_t value;
bool isText;
bool bypass;
bool acquire;
};
uint32_t genHash(const char * fullVarname, uint32_t lineNum, bool isText) {
// just a quicky to mash these into a unique-ish value
uint32_t v = 0;
// name + line are enough entropy for a good hash, ignoring filename
uint32_t nameLen = strlen(fullVarname);
for (uint32_t n = 0; n < nameLen; ++n) v ^= uint32_t(fullVarname[n]) << (n&31u); // shift adds some stink for repeating letters
v ^= lineNum ^ (lineNum<<16); // doubling the lineNum's contribution as it's the strongest indicater of uniquity
if (isText) {
v |= 0x80000000;
} else {
v &= 0x7FFFFFFF;
}
return v;
}
void genShortFilename(const char * full, uint8_t * out16) {
const char * startFwd = strrchr(full, '/');
const char * startBwd = strrchr(full, '\\');
const char * start = full > startFwd ? full : startFwd;
start = start > startBwd ? start : startBwd;
uint32_t len = strlen(start);
if (len < 16) {
memcpy(out16, start, len);
out16[len+1] = 0;
} else {
// 0123456789012345####
// name_reduct@tion0
memcpy(out16, start, 10);
out16[10] = ELLIPSIS_NUM;
memcpy(out16+11, start+len-5, 4); // I think?
out16[15] = 0;
}
}
void genShortVarname(const char * full, uint8_t * out16) {
uint32_t len = strlen(full);
if (len < 16) {
memcpy(out16, full, len);
out16[len+1] = 0;
} else {
// 0123456789012345####
// name_reduct@tion0
memcpy(out16, full, 10);
out16[10] = ELLIPSIS_NUM;
memcpy(out16+11, full+len-5, 4); // I think?
out16[15] = 0;
}
}
void assignToStrVar(VarRegisterExtendedData & var, const char * input, uint32_t len) {
if (len + 1 < var.str.reserve) {
var.str.len = len;
memcpy(var.str.c, input, len + 1);
} else {
var.str.reserve = len + 16;
uint8_t * next = DOWSING_MALLOC(uint8_t, var.str.reserve);
memcpy(next, input, len + 1);
if (var.str.c)
DOWSING_FREE(var.str.c);
var.str.len = len;
var.str.c = next;
}
}
bool getGroupIndex(const char * fullFilename, uint32_t lineNum, uint32_t & groupIndex_ret) {
uint32_t hash = genHash(fullFilename, lineNum, lineNum&0x1);
uint32_t index = 0;
bool madeOne = false;
while (index < _groupRegisterCount) {
if (hash == _groupRegisters[index].hash
&& lineNum == _groupRegisters[index].line
&& 0 == strcmp(fullFilename, _groupRegisters[index].filename))
{
break;
}
++index;
}
if (index == _groupRegisterCount) {
// we have a new group eww
madeOne = true;
if (index >= _groupRegisterReserve) {
// need to resize the variable register bank
const uint32_t INCREMENT_SIZE = 16;
_groupRegisterReserve += INCREMENT_SIZE;
{
GroupRegisterNode * next = DOWSING_MALLOC(GroupRegisterNode, _groupRegisterReserve);
memcpy(next, _groupRegisters, sizeof(GroupRegisterNode) * _groupRegisterCount);
if(_groupRegisters != NULL)
DOWSING_FREE(_groupRegisters);
}
}
++_groupRegisterCount;
_groupRegisters[index].hash = hash;
_groupRegisters[index].line = lineNum;
_groupRegisters[index].filename = fullFilename;
_groupRegisters[index].min = 0.f;
_groupRegisters[index].max = 0.f;
_groupRegisters[index].autoRange = false;
}
// we got a valid index now and did our registration!!
groupIndex_ret = index;
return madeOne;
}
// called with the var's meta data to get (or add) it's place in the line-up (once one is added, it's there for LIFE)
bool getVarIndex(const char * fullFilename, const char * fullVarname, uint32_t lineNum, bool isText, uint32_t & varIndex_ret) {
// some folks don't use the std library, so we can't use a map here.
// generate a hash and use it to short-circuit most bad matches for our super shitty linear search
uint32_t hash = genHash(fullVarname, lineNum, isText);
uint32_t index = 0;
bool madeOne = false;
while (index < _varRegisterCount) {
if (hash == _varRegisters[index].hash
&& lineNum == _varRegisters[index].line
&& 0 == strcmp(fullVarname, _varRegisters[index].varname)
&& 0 == strcmp(fullFilename, _varRegisters[index].filename))
{
break;
}
++index;
}
if (index == _varRegisterCount) {
// we have a new variable aww
madeOne = true;
if (index >= _varRegisterReserve) {
// need to resize the variable register bank
const uint32_t INCREMENT_SIZE = 32;
_varRegisterReserve += INCREMENT_SIZE;
{
VarRegisterNode * next = DOWSING_MALLOC(VarRegisterNode, _varRegisterReserve);
memcpy(next, _varRegisters, sizeof(VarRegisterNode) * _varRegisterCount);
if(_varRegisters != NULL)
DOWSING_FREE(_varRegisters);
}
{
VarRegisterExtendedData * next = DOWSING_MALLOC(VarRegisterExtendedData, _varRegisterReserve);
memcpy(next, _varRegistersExtended, sizeof(VarRegisterExtendedData) * _varRegisterCount);
if(_varRegistersExtended != NULL)
DOWSING_FREE(_varRegistersExtended);
}
}
++_varRegisterCount;
_varRegisters[index].hash = hash;
_varRegisters[index].line = lineNum;
_varRegisters[index].varname = fullVarname;
_varRegisters[index].filename = fullFilename;
if (_groupRemaining) {
if (isText) {
_groupRemaining = 0;
} else {
--_groupRemaining;
_varRegistersExtended[index].groupID = _groupAcc + 1;
}
} else {
_varRegistersExtended[index].groupID = 0;
}
genShortFilename(fullFilename, _varRegistersExtended[index].shortFilename);
genShortVarname(fullVarname, _varRegistersExtended[index].shortVarname);
}
// we got a valid index now and did our registration!!
varIndex_ret = index;
return madeOne;
}
class SharedBuffer {
public:
uint32_t * reserve(uint32_t count) {
if (m_data == 0 || count + m_len < m_reserve) {
uint32_t size = m_reserve;
if(size < 256)
size = 256;
while(size < count + m_len)
size *= 2;
uint32_t * next = DOWSING_MALLOC(uint32_t, size);
if (m_data != 0) {
memcpy(next, m_data, m_len * sizeof(uint32_t));
DOWSING_FREE(m_data);
}
}
uint32_t * ptr = m_data + m_len;
m_len += count;
return ptr;
}
uint32_t size() {
return m_len;
}
uint32_t * getPtr() {
return m_data;
}
void clear() {
memset(m_data, 0, m_len);
m_len = 0;
}
private:
uint32_t m_reserve {0};
uint32_t m_len {0};
uint32_t * m_data {0};
};
class SharedPad {
public:
uint32_t * reserve(uint32_t count) {
return m_buffers[m_curBuffer].reserve(count);
}
uint32_t size() {
return m_buffers[m_curBuffer].size();
}
uint32_t * getPtr() {
return m_buffers[m_curBuffer].getPtr();
}
void clear() {
return m_buffers[m_curBuffer].clear();
}
void flip() {
m_curBuffer = uint32_t(m_curBuffer == 0);
}
private:
uint32_t m_curBuffer {0};
SharedBuffer m_buffers[2];
};
SharedPad _tx;
SharedBuffer _rx; // single buffered receiving pad
bool _flipBuffers = false;
enum txLocations {
tReady, // 0: not ready, !0: ready
tSizeRX,
tNextRXptr0,
tNextRXptr1,
tEventCount,
t__end,
// events are organized as such:
// u32: format
// if format==1 (var registration)
// u32: register index
// u32: type (0:float, 1:uint, 2:int, 3:string, 4:tail, 5:prompt)
// u32: groupID
// u8[16]: short filename (4 uint)
// u8[16]: short varname (4 uint)
// u32: line number
// if format==2 (group registration)
// u32: register index
// f32: min
// f32: max
// u32: autorange
// u8[16]: short filename (4 uint)
// u32: line number
// if format==3 (group update)
// u32: register index
// f32: min
// f32: max
// u32: autorange
// if format==4 (number)
// u32: register index
// f32: number
// f32: min
// f32: max
// u32: autorange
// if format==5 (string)
// u32: register index
// u32: string given length (0 if none)
// u32: string length (in uint32_t units)
// u8[]: string [string length]
};
enum rxLocations {
rReady, // 0: not ready, !0: ready
rResizeRequest, // requests are in units of uint32, 0 if no request
rEventCount,
r__end // offset that events begin
// events are organized as such:
// u32: format
// if format==1 (number)
// u32: register index
// u32: bypass/acquire (0: bypass off, 1: bypass on, 2: reacquiring)
// f32: number
// if format==2 (string)
// u32: register index
// u32: bypass/acquire (0: bypass off, 1: bypass on, 2: reacquiring)
// u32: string length
// u8[]: string [string length]
};
// returns new size if resize requested, otherwise 0
void processAllIncoming() {
uint32_t * d = _rx.getPtr();
uint32_t resize = d[rResizeRequest];
uint32_t count = d[rEventCount];
d += r__end;
for (uint32_t n = 0; n < count; ++n) {
DOWSING_ASSERT(d[0] < 2);
if (d[0] == 1) {
// numbers
uint32_t reg = d[1];
uint32_t bypass = d[2];
DOWSING_ASSERT(reg < _varRegisterCount && _varRegistersExtended[reg].type == 0);
if (bypass == 0) {
// bypass OFF
_varRegistersExtended[reg].bypass = false;
_varRegistersExtended[reg].acquire = false;
} else if(bypass == 1) {
// bypass ON (sent number is relevant)
float num;
memcpy(&num, d+3, sizeof(float));
_varRegistersExtended[reg].bypass = true;
_varRegistersExtended[reg].num.value = num;
} else {
// bypass ON (requesting acquire)
_varRegistersExtended[reg].bypass = true;
_varRegistersExtended[reg].acquire = true;
}
} else {
// strings
uint32_t reg = d[1];
uint32_t bypass = d[2];
DOWSING_ASSERT(reg < _varRegisterCount && _varRegistersExtended[reg].type == 1);
if (bypass == 0) {
// bypass OFF
_varRegistersExtended[reg].bypass = false;
_varRegistersExtended[reg].acquire = false;
} else if(bypass == 1) {
// bypass ON (sent str is relevant)
uint32_t len = d[3];
if (len > _varRegistersExtended[reg].str.reserve) {
// need to resize this string, we'll do it here
_varRegistersExtended[reg].str.reserve = len+16;
uint8_t * next = DOWSING_MALLOC(uint8_t, _varRegistersExtended[reg].str.reserve);
memset(next, 0, _varRegistersExtended[reg].str.reserve);
if(_varRegistersExtended[reg].str.c)
DOWSING_FREE(_varRegistersExtended[reg].str.c);
_varRegistersExtended[reg].str.c = next;
}
_varRegistersExtended[reg].bypass = true;
_varRegistersExtended[reg].str.len = len;
memcpy(_varRegistersExtended[reg].str.c, d+4, len);
} else {
// bypass ON (requesting acquire)
_varRegistersExtended[reg].bypass = true;
_varRegistersExtended[reg].acquire = true;
}
}
}
// we're now done reading this buffer
if (resize && resize > _rx.size()) {
_rx.reserve(resize);
}
_rx.clear();
_rx.reserve(r__end);
_rx.getPtr()[rReady] = 0;
}
void download() {
if (_directory[dlWindowDied]) {
close(true);
return;
}
uint32_t * d = _rx.getPtr();
if (d[rReady] != 0) {
processAllIncoming(); // clears and resizes for us
_flipBuffers = true;
}
}
void upload() {
if (_flipBuffers) {
_flipBuffers = false;
_tx.flip(); // take the ready trigger off the outward buffer, which isn't being read right now
_tx.getPtr()[tReady] = 0;
_tx.flip(); // we are now back to our buffer for now
uint32_t * d = _tx.getPtr();
d[tSizeRX] = _rx.size();
d[tNextRXptr0] = uint32_t(uintptr_t(_rx.getPtr()));
d[tNextRXptr1] = uint32_t(uintptr_t(_rx.getPtr()) >> 32);
_directory[dlSizeTX] = _tx.size();
_directory[dlAddrTX0]= uint32_t(uintptr_t(d));
_directory[dlAddrTX1]= uint32_t(uintptr_t(d) >> 32);
d[tReady] = 1; // IT'S LIVE, Dowser could eat it at any point now
_tx.flip();
_tx.clear();
d = _tx.reserve(t__end);
d[tEventCount] = 0;
}
}
enum TxEventFormats{ tefReg=1, tefGroupReg, tefGroupUpdate, tefNum, tefStr};
enum TxVarRegTypes{ tvrtFloat, tvrtUint, tvrtInt, tvrtStr, tvrtTail, tvrtPrompt};
enum TxVarRegLocations{ tvrlFormat, tvrlVarIndex, tvrlType, tvrlGroup, tvrlFilename, tvrlVarname = tvrlFilename +4, tvrlLinenum = tvrlVarname +4, tvrl__size};
void addVarRegisterEvent(uint32_t index, dowsingPrivate::TxVarRegTypes type) {
_tx.reserve(tvrl__size);
uint32_t * d = _tx.getPtr();
d[tvrlFormat] = tefReg;
d[tvrlVarIndex] = index;
d[tvrlType] = type;
d[tvrlGroup] = _varRegistersExtended[index].groupID;
memcpy(d + tvrlFilename, _varRegistersExtended[index].shortFilename, 16);
memcpy(d + tvrlVarname, _varRegistersExtended[index].shortVarname, 16);
d[tvrlLinenum] = _varRegisters[index].line;
}
enum TxGroupRegLocations{ tgrlFormat, tgrlIndex, tgrlMin, tgrlMax, tgrlRange, tgrlFilename, tgrlLinenum = tgrlVarname +4, tgrl__size};
void addGroupRegisterEvent(uint32_t index) {
_tx.reserve(tgrl__size);
uint32_t * d = _tx.getPtr();
d[tgrlFormat] = tefGroupReg;
d[tgrlIndex] = index;
memcpy(d + tgrlMin, &_groupRegisters[index].min, sizeof(uint32_t));
memcpy(d + tgrlMax, &_groupRegisters[index].max, sizeof(uint32_t));
memcpy(d + tgrlFilename, _groupRegisters[index].filename, 16);
d[tgrlLinenum] = _groupRegisters[index].line;
}
enum TxGroupEventLocations{tgelFormat, tgelIndex, tgelMin, tgelMax, tgelRange, tgel__size};
void addGroupEvent(uint32_t index, float min, float max, bool autoRange) {
_tx.reserve(tgel__size);
uint32_t * d = _tx.getPtr();
d[tgelFormat] = tefGroupUpdate;
d[tgelIndex] = index;
memcpy(d + tgelMin , &min, sizeof(uint32_t));
memcpy(d + tgelMax , &max, sizeof(uint32_t));
d[tgelRange] = uint32_t(autoRange);
}
enum TxNumEventLocations{tnelFormat, tnelVarIndex, tnelNumber, tnelMin, tnelMax, tnelRange, tnel__size};
void addNumEvent(uint32_t index, float value, float min, float max, bool autoRange) {
_tx.reserve(tnel__size);
uint32_t * d = _tx.getPtr();
d[tnelFormat] = tefNum;
d[tnelVarIndex] = index;
memcpy(d + tnelNumber , &value, sizeof(uint32_t));
memcpy(d + tnelMin , &min, sizeof(uint32_t));
memcpy(d + tnelMax , &max, sizeof(uint32_t));
d[tnelRange] = uint32_t(autoRange);
}
enum TxStrEventLocations{tselFormat, tselVarIndex, tselLen, tselTrueLen, tnelStr};
void addStrEvent(uint32_t index, const char * str, uint32_t givenLen) {
uint32_t len = givenLen;
if(len == 0)
len = strlen(str);
uint32_t trueLen = len;
trueLen >>= 2;
trueLen += 2;
_tx.reserve(tnelStr + trueLen);
uint32_t * d = _tx.getPtr();
d[tselFormat] = tefStr;
d[tselVarIndex] = index;
d[tselLen] = givenLen;
d[tselTrueLen] = trueLen;
memcpy(d + tnelStr , str, len);
d[tnelStr + len + 1] = 0;
}
void open(bool manual) {
if (_stayDead == true && manual == false) {
// we won't open if the window was closed by itself
return;
}
_directory[dlSizeTX] = 0;
_directory[dlAddrTX0] = 0;
_directory[dlAddrTX1] = 0;
_directory[dlWindowDied] = 0;
HANDLE handle = GetCurrentProcess();
STARTUPINFOA si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
si.wShowWindow = 1;
DuplicateHandle(handle, handle, handle, &_winDowserHandle,
PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_OPERATION | PROCESS_TERMINATE | PROCESS_DUP_HANDLE | PROCESS_VM_READ | PROCESS_VM_WRITE, true, 0);
char args[256];
sprintf(args, "%p %p",
_winOurHandle,
_directory);
bool success = CreateProcessA(DOWSING_EXE_PATH, args, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
_winDowserHandle = pi.hProcess;
_initialized = success;
_stayDead = false;
}
void close(bool manual) {
if (_initialized) {
if(_winDowserHandle) TerminateProcess(_winDowserHandle, 0);
_initialized = false;
if(manual)
_stayDead = true;
}
}
void groupAuto(const char * locFile, unsigned long locLine, unsigned long count) {
if (!_initialized)
open(false);
if (_initialized) {
if(_groupRemaining)
upload();
download();
uint32_t index;
if (getGroupIndex(locFile, locLine, index)) {
_groupRegisters[index].autoRange = true;
_groupAcc = index;
_groupRemaining = count;
addGroupRegisterEvent(index);
} else {
_groupRemaining = 0;
}
if (_groupRegisters[index].autoRange != true) {
_groupRegisters[index].autoRange = true;
addGroupEvent(index, 0.f, 0.f, true);
}
}
}
void groupRange(const char * locFile, unsigned long locLine, unsigned long count, float min, float max) {
if (!_initialized)
open(false);
if (_initialized) {
if(_groupRemaining)
upload();
download();
uint32_t index;
if (getGroupIndex(locFile, locLine, index)) {
_groupRegisters[index].min = min;
_groupRegisters[index].max = max;
_groupRegisters[index].autoRange = false;
_groupAcc = index;
_groupRemaining = count;
addGroupRegisterEvent(index);
} else {
_groupRemaining = 0;
}
if (_groupRegisters[index].autoRange || _groupRegisters[index].min != min || _groupRegisters[index].max != max) {
_groupRegisters[index].min = min;
_groupRegisters[index].max = max;
_groupRegisters[index].autoRange = false;
addGroupEvent(index, min, max, false);
}
}
}
float varAuto(const char * locFile, unsigned long locLine, const char * varName, float x) {
return varAutoDelegate(locFile, locLine, varName, x, tvrtFloat);
}
unsigned long varAuto(const char * locFile, unsigned long locLine, const char * varName, unsigned long x) {
return unsigned long(varAutoDelegate(locFile, locLine, varName, float(x), tvrtUint));
}
long varAuto(const char * locFile, unsigned long locLine, const char * varName, long x) {
return long(varAutoDelegate(locFile, locLine, varName, float(x), tvrtInt));
}
float varAutoDelegate(const char * locFile, unsigned long locLine, const char * varName, float x, TxVarRegTypes type) {
float value = x;
if (!_initialized)
open(false);
if (_initialized) {
if (_groupRemaining == 0) {
download();
}
uint32_t index;
if (getVarIndex(locFile, varName, locLine, false, index)) {
addVarRegisterEvent(index, type);
}
if (_varRegistersExtended[index].acquire || _varRegistersExtended[index].bypass == false) {
_varRegistersExtended[index].acquire = false;
if (_varRegistersExtended[index].num.value != x || _varRegistersExtended[index].num.autoRange == false) {
_varRegistersExtended[index].num.value = x;
_varRegistersExtended[index].num.autoRange = true;
addNumEvent(index, x, 0.f, 0.f, true);
}
} else {
value = _varRegistersExtended[index].num.value;
}
if (_groupRemaining == 0) {
upload();
}
}
return value;
}
float varRange(const char * locFile, unsigned long locLine, const char * varName, float x, float min, float max) {
return varRangeDelegate(locFile, locLine, varName, x, min, max, tvrtFloat);
}
unsigned long varRange(const char * locFile, unsigned long locLine, const char * varName, unsigned long x, float min, float max) {
return unsigned long(varRangeDelegate(locFile, locLine, varName, float(x), min, max, tvrtUint));
}
long varRange(const char * locFile, unsigned long locLine, const char * varName, long x, float min, float max) {
return long(varRangeDelegate(locFile, locLine, varName, float(x), min, max, tvrtInt));
}
float varRangeDelegate(const char * locFile, unsigned long locLine, const char * varName, float x, float min, float max, TxVarRegTypes type) {
float value = x;
if (!_initialized)
open(false);
if (_initialized) {
if (_groupRemaining == 0) {
download();
}
uint32_t index;
if (getVarIndex(locFile, varName, locLine, false, index)) {
addVarRegisterEvent(index, type);
}
if (_varRegistersExtended[index].acquire || _varRegistersExtended[index].bypass == false) {
_varRegistersExtended[index].acquire = false;
if (_varRegistersExtended[index].num.value != x || _varRegistersExtended[index].num.autoRange || _varRegistersExtended[index].num.min != min || _varRegistersExtended[index].num.max != max) {
_varRegistersExtended[index].num.value = x;
_varRegistersExtended[index].num.min = min;
_varRegistersExtended[index].num.max = max;
_varRegistersExtended[index].num.autoRange = false;
addNumEvent(index, x, min, max, false);
}
} else {
value = _varRegistersExtended[index].num.value;
}
if (_groupRemaining == 0) {
upload();
}
}
return value;
}
const char * varStr(const char * locFile, unsigned long locLine, const char * varName, const char * input, unsigned long lenGiven = 0) {
const char * out = input;
if (!_initialized)
open(false);
if (_initialized) {
if (_groupRemaining != 0) {
_groupRemaining = 0;
download();
}
uint32_t index;
if (getVarIndex(locFile, varName, locLine, true, index)) {
addVarRegisterEvent(index, tvrtStr);
}
if (_varRegistersExtended[index].acquire || _varRegistersExtended[index].bypass == false) {
_varRegistersExtended[index].acquire = false;
uint32_t len = lenGiven ? lenGiven : strlen(input);
if (_varRegistersExtended[index].str.len != len || _varRegistersExtended[index].str.givenLen != lenGiven || 0 != strcmp((char*)_varRegistersExtended[index].str.c, input)) {
assignToStrVar(_varRegistersExtended[index], input, len);
_varRegistersExtended[index].str.givenLen = lenGiven;
addStrEvent(index, input, lenGiven);
}
} else {
out = (char*)_varRegistersExtended[index].str.c;
}
upload();
}
return out;
}
char * varStr(const char * locFile, unsigned long locLine, const char * varName, char * input, unsigned long lenGiven = 0) {
char * out = input;
if (!_initialized)
open(false);
if (_initialized) {
if (_groupRemaining != 0) {
_groupRemaining = 0;
download();
}
uint32_t index;
if (getVarIndex(locFile, varName, locLine, true, index)) {
addVarRegisterEvent(index, tvrtStr);
}
if (_varRegistersExtended[index].acquire || _varRegistersExtended[index].bypass == false) {
_varRegistersExtended[index].acquire = false;
uint32_t len = lenGiven ? lenGiven : strlen(input);
if (_varRegistersExtended[index].str.len != len || _varRegistersExtended[index].str.givenLen != lenGiven || 0 != strcmp((char*)_varRegistersExtended[index].str.c, input)) {
assignToStrVar(_varRegistersExtended[index], input, len);
_varRegistersExtended[index].str.givenLen = lenGiven;
addStrEvent(index, input, lenGiven);
}
} else {
out = (char*)_varRegistersExtended[index].str.c;
}
upload();
}
return out;
}
const char * varTail(const char * locFile, unsigned long locLine, const char * varName, const char * input) {
const char * out = input;
if (!_initialized)
open(false);
if (_initialized) {
if (_groupRemaining != 0) {
_groupRemaining = 0;
download();
}
uint32_t index;
if (getVarIndex(locFile, varName, locLine, true, index)) {
addVarRegisterEvent(index, tvrtTail);
}
uint32_t len = strlen(input);
assignToStrVar(_varRegistersExtended[index], input, len);
addStrEvent(index, input, 0);
upload();
}
return out;
}
char * varTail(const char * locFile, unsigned long locLine, const char * varName, char * input) {
char * out = input;
if (!_initialized)
open(false);
if (_initialized) {
if (_groupRemaining != 0) {
_groupRemaining = 0;
download();
}
uint32_t index;
if (getVarIndex(locFile, varName, locLine, true, index)) {
addVarRegisterEvent(index, tvrtTail);
}
uint32_t len = strlen(input);
assignToStrVar(_varRegistersExtended[index], input, len);
addStrEvent(index, input, 0);
upload();
}
return out;
}
// prompt uses `acquire` differently, to specify that a command was sent (otherewise this returns null)
const char * prompt(const char * locFile, unsigned long locLine) {
const char * out = NULL;
if (!_initialized)
open(false);
if (_initialized) {
if (_groupRemaining != 0) {
_groupRemaining = 0;
download();
}
uint32_t index;
if (getVarIndex(locFile, "@@@PROMPT@@@", locLine, true, index)) {
addVarRegisterEvent(index, tvrtPrompt);
}
if (_varRegistersExtended[index].acquire) {
_varRegistersExtended[index].acquire = false;
out = (char*)_varRegistersExtended[index].str.c;
}
upload();
}
return out;
}
} // namespace dowsingPrivate
// cleanup whatever defs I can
#undef DOWSING_MALLOC
#undef DOWSING_FREE
#undef DOWSING_ASSERT
#undef DOWSING_EXE_PATH
#define DOWSING_IMPLEMENTED_DONT_DO_IT_AGAIN
#endif // DOWSING_IMPLEMENT
#endif // include guard
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment