I've been sitting on this code for a while. It's already implemented in blocklandjs- but, what the hell.
struct Namespace
{
const char* mName;
const char* mPackage;
Namespace *mParent;
Namespace *mNext;
void *mClassRep;
U32 mRefCountToParent;
struct Entry
{
enum
{
GroupMarker = -3,
OverloadMarker = -2,
InvalidFunctionType = -1,
ScriptFunctionType,
StringCallbackType,
IntCallbackType,
FloatCallbackType,
VoidCallbackType,
BoolCallbackType
};
Namespace *mNamespace;
//char _padding1[4];
Entry *mNext;
const char *mFunctionName;
S32 mType;
S32 mMinArgs;
S32 mMaxArgs;
const char *mUsage;
const char *mPackage;
void *mCode; // CodeBlock *mCode;
U32 mFunctionOffset;
union {
StringCallback mStringCallbackFunc;
IntCallback mIntCallbackFunc;
VoidCallback mVoidCallbackFunc;
FloatCallback mFloatCallbackFunc;
BoolCallback mBoolCallbackFunc;
const char* mGroupName;
} cb;
};
Entry *mEntryList;
Entry **mHashTable;
U32 mHashSize;
U32 mHashSequence; ///< @note The hash sequence is used by the autodoc console facility
/// as a means of testing reference state.
char * lastUsage;
};
static std::map<const char*, Namespace::Entry*> cache;
static std::map<const char*, Namespace*> nscache;
static Namespace* GlobalNS;
void rewrite__fatal() {
Printf("!!! THIS SHOULD NEVER HAPPEN !!!");
}
Namespace::Entry* fastLookup(const char* ourNamespace, const char* name) {
Namespace* ns;
Namespace::Entry* entry;
if(_stricmp(ourNamespace, "") == 0) { //If the namespace is blank, assume we're looking in the global namespace.
if(GlobalNS == NULL) {
GlobalNS = LookupNamespace(NULL);
}
ns = GlobalNS;
}
else {
std::map<const char*, Namespace*>::iterator its;
its = nscache.find(ourNamespace);
if(its != nscache.end()) {
ns = its->second;
if(ns == NULL) {
//Somehow it got nullptr'd..
ns = LookupNamespace(ourNamespace);
if(ns == NULL) {
//THIS SHOULD NEVER HAPPEN!
rewrite__fatal();
Printf("Fatal: Found cached NS entry with nullptr, could not find namespace!");
nscache.erase(its);
return nullptr;
}
nscache.erase(its);
nscache.insert(nscache.end(), std::make_pair(ourNamespace, ns));
}
}
else {
ns = LookupNamespace(StringTableEntry(ourNamespace));
if(ns == NULL) {
rewrite__fatal();
Printf("Fatal: fastLookup FAILED (2/2)!");
return nullptr;
}
nscache.insert(nscache.end(), std::make_pair(ourNamespace, ns));
}
}
std::map<const char*, Namespace::Entry*>::iterator it;
it = cache.find(name); //Look into our Namespace::Entry* cache..
if(it != cache.end()) {
entry = it->second;
if(entry == NULL) {
rewrite__fatal();
Printf("Fatal: found nullptr in cache!");
cache.erase(it); //Erase it so we don't encounter it again.
return nullptr;
}
}
else {
entry = Namespace__lookup(ns, StringTableEntry(name));
if(entry == NULL) {
Printf("Could not find function.");
return nullptr;
}
cache.insert(cache.end(), std::make_pair(name, entry)); //Insert it so further calls are optimized.
}
return entry;
}
If you're looking to implement this in the publicly available dllbase, add this to your Torque.h
BLFUNC_EXTERN(Namespace *, , LookupNamespace, const char *ns);
BLFUNC_EXTERN(Namespace::Entry *, __thiscall, Namespace__lookup, Namespace *this_, const char *name)
//blfunc_extern, or blfunc.. whichever matches the style of your dll.
and this to your Torque.cpp
//in torque_init, or InitTorqueStuff
BLSCAN(LookupNamespace, "\x8B\x44\x24\x04\x85\xC0\x75\x05", "xxxxxxxx");
BLSCAN(Namespace__lookup, "\x53\x56\x8B\xF1\x8B\x46\x24", "xxxxxxx");
This code can definitely be improved- this is just a rough "hacky" version. You want to do fast lookups to avoid the "TorqueScript problem", in which, when looking up namespaces and entries- nothing is cached, forcing the engine to search. This avoids all of this, by caching them in std::map objects. I've attempted to make this as "idiot-proof" as possible, but, if you notice any problems, feel free to point them out. :)