Initial commit

This commit is contained in:
2026-04-03 00:22:39 -05:00
commit eca1e8c458
945 changed files with 218160 additions and 0 deletions

547
ZUtil/ZKVTree.cpp Normal file
View File

@@ -0,0 +1,547 @@
#include <ZUtil/ZKVTree.hpp>
#include <ZUtil/ZLog.hpp>
#include <ZSTL/ZBasicStringAlgo.hpp>
#include <ZSTL/ZArrayAlgo.hpp>
/*************************************************************************/
void ZKVTree::NodeCheckIn(Node* _node)
{
for (size_t i = 0; i < _node->Children.Size(); i++)
NodeCheckIn(_node->Children[i]);
_node->Children.Clear();
NodeAllocator.Deallocate(_node);
}
/*************************************************************************/
ZKVTree::ZKVTree()
: RootNode()
{
RootNode.ParentNode = NULL;
RootNode.Index = 0;
}
/*************************************************************************/
ZKVTree::ZKVTree( const ZKVTree& _other )
: RootNode()
{
RootNode.ParentNode = NULL;
RootNode.Index = 0;
for (ZKVTree::Iterator itr = _other.Begin(); itr != _other.End(); ++itr) {
Put(itr, itr.GetValue());
}
}
/*************************************************************************/
ZKVTree::~ZKVTree()
{
for (size_t i = 0; i < RootNode.Children.Size(); i++)
NodeCheckIn(RootNode.Children[i]);
}
/*************************************************************************/
ZKVTree& ZKVTree::operator = ( const ZKVTree& _other )
{
Clear();
for (ZKVTree::Iterator itr = _other.Begin(); itr != _other.End(); ++itr) {
Put(itr.GetPath(), itr.GetValue());
}
return *this;
}
/*************************************************************************/
// This will convert to a full path, with subscripts everywhere and path separators. EXAMPLES:
//
// A.B[2].C.d -> A[0].B[2].C[0].d[0]
// A.B[0][1] -> A[0].B[0].[1]
//
static bool ConvertPath(const ZString& _path, ZString& _out)
{
const ZString zeroSubscript("[0]");
size_t i = 0;
size_t j = 0;
size_t k = 0;
//We are going to loop, looking for path separator and making sure we have subscripts and proper path separators
while (i != ZSTL::InvalidPos) {
// find the next path separator or the first occurrence of a nested array access
j = ZStringAlgo::FindFirst(_path, ZKVTREE_PATH_SEPARATOR, i, _path.Length());
k = ZStringAlgo::FindSub(_path, i, _path.Length(), "][", 0, 2);
// if a nested array access occurs
if (k < j) {
// append the first part of it, a path separators, and then process from there
ZStringAlgo::Append(_out, _path, i, k + 1);
_out.PushBack(ZKVTREE_PATH_SEPARATOR);
i = k + 1;
} else if (i == j) {
// this means we are dealing with a node without a name (potentially first node)
if (_out.Empty() || _out.Back() != ']')
ZStringAlgo::Append(_out, zeroSubscript);
_out.PushBack(ZKVTREE_PATH_SEPARATOR);
i = j + 1;
} else if (j != ZSTL::InvalidPos) {
// found a substring between two path separators
ZStringAlgo::Append(_out, _path, i, j);
if (_out.Back() != ']')
ZStringAlgo::Append(_out, zeroSubscript);
_out.PushBack(ZKVTREE_PATH_SEPARATOR);
i = j + 1;
} else {
// we have reached the end
ZStringAlgo::Append(_out, _path, i, _path.Length());
if (_out.Empty() || _out.Back() != ']')
ZStringAlgo::Append(_out, zeroSubscript);
i = ZSTL::InvalidPos;
}
}
// this method may be expanded later for path verification via regex or some such
return true;
}
//This will insert a child node at the given index and increment the indices on the other child values
static void InsertNode( ZKVTree::Node* _parent, ZKVTree::Node* _child, size_t _index )
{
_parent->Children.Insert(_index, _child);
for (size_t i = _index + 1; i < _parent->Children.Size(); i++)
_parent->Children.Data()[i]->Index++;
}
//This will erase a child node at the given index and decrement the indices on the other child values
static ZKVTree::Node* EraseChildNode( ZKVTree::Node* _parent, size_t _index )
{
for (size_t i = _index + 1; i < _parent->Children.Size(); i++)
_parent->Children.Data()[i]->Index--;
return _parent->Children.Erase(_index);
}
/*************************************************************************/
// gets the value from a completed path (returned from ConvertFullPath)
ZKVTree::Node* ZKVTree::GetFromPath(const ZString& _path) const
{
size_t i, j, k;
size_t subscript;
const Node* node = &RootNode;
// path[i...j] is our path window (current value we are looking for)
// path[subscript].nextpath
// ^ ^ ^
// i j k
// or
// path.nextpath
// ^ ^
// i j,k
i = 0;
j = 0;
k = 0;
//While we still have a node to look in
while (node != NULL)
{
//See if we have reached the end
if (i >= _path.Length())
break;
subscript = 0;
j = ZStringAlgo::FindFirst(_path, ZKVTREE_PATH_SEPARATOR, i, _path.Length());
//If we can't find a path separator, go to the end
if (j == ZSTL::InvalidPos)
j = _path.Length();
//See if we have found the node
if (i >= j)
break;
k = j;
//Determine if it has a subscript
if (_path[j - 1] == ']')
{
j = ZStringAlgo::FindLast(_path, '[', i, j);
//Make sure this is bounded correct
if (j == ZSTL::InvalidPos || j < i)
{
node = NULL;
break;
}
subscript = (size_t)ZStringAlgo::NumericInt(_path, j + 1, k - 1);
}
node = node->GetChildByName(_path, i, j, subscript);
//Move up our path 'window'
i = k + 1;
}
return const_cast<ZKVTree::Node*>(node);
}
/*************************************************************************/
ZKVTree::Node* ZKVTree::CreatePath(const ZString& _path)
{
size_t i, j, k;
size_t subscript;
Node* node = &RootNode;
// path[i...j] is our path window (current value we are looking for)
// path[subscript].nextpath
// ^ ^ ^
// i j k
// or
// path.nextpath
// ^ ^
// i j,k
i = 0;
j = 0;
k = 0;
// while we still have a node to look in
while (node != NULL) {
// see if we have reached the end
if (i >= _path.Length()) {
break;
}
subscript = 0;
j = ZStringAlgo::FindFirst(_path, ZKVTREE_PATH_SEPARATOR, i, _path.Length());
//If we can't find a path separator, go to the end
if (j == ZSTL::InvalidPos) {
j = _path.Length();
}
//See if we are done creating
if (i == j) {
break;
}
k = j;
// determine if it has a subscript
if (_path[j - 1] == ']') {
j = ZStringAlgo::FindLast(_path, '[', i, j);
// make sure this is bounded correct (if not, bail out)
if (j == ZSTL::InvalidPos || j < i) {
node = NULL;
break;
}
subscript = (size_t)ZStringAlgo::NumericInt(_path, j + 1, k - 1);
}
Node* childNode = node->GetChildByName(_path, i, j, subscript);
// create this node if it doesn't exist
if (childNode == NULL) {
// get the child count by name at the parent node
size_t count = node->GetChildCountByName(_path, i, j);
// see if the subscript at the end is the 'next' value (if not, bail out)
if (count != subscript) {
node = NULL;
break;
}
// get a new node, set the parent node, name, index (leave out the value)
childNode = NodeAllocator.Allocate();
childNode->ParentNode = node;
childNode->Name.Resize(j - i);
ZStringAlgo::Copy(childNode->Name, 0, childNode->Name.Length(), _path, i, j);
if (count > 0) {
childNode->Index = node->GetLastIndexByName(_path, i, j) + 1;
// increment the index on the rest of the nodes
for (size_t i = childNode->Index; i < node->Children.Size(); i++)
node->Children[i]->Index++;
node->Children.Insert(childNode->Index, childNode);
} else {
childNode->Index = node->Children.Size();
node->Children.PushBack(childNode);
}
}
// set our new node
node = childNode;
// move up our 'path window'
i = k + 1;
}
return node;
}
/*************************************************************************/
ZKVTree::Iterator ZKVTree::Add( const ZString& _name, const ZString& _value )
{
return Add(End(), _name, _value);
}
/*************************************************************************/
ZKVTree::Iterator ZKVTree::Add( const ZString& _parent, const ZString& _name, const ZString& _value )
{
ZString path;
if (!ConvertPath(_parent, path))
return End();
Node* node = CreatePath(path);
return Add(Iterator(node, this), _name, _value);
}
/*************************************************************************/
ZKVTree::Iterator ZKVTree::Add( const Iterator& _itr, const ZString& _name, const ZString& _value )
{
ZKVTree::Node* parentNode = NULL;
ZKVTree::Node* childNode = NULL;
if (_itr.CurrentNode == NULL) {
return End();
}
if (_itr.KVTree == this) {
parentNode = _itr.CurrentNode;
// allocate a child node and set values
childNode = NodeAllocator.Allocate();
childNode->ParentNode = parentNode;
childNode->Name = _name;
childNode->Value = _value;
// determine if any other children of the same name exist (grouped on insert)
size_t count = parentNode->GetChildCountByName(_name, 0, _name.Length());
if (count > 0) {
childNode->Index = parentNode->GetLastIndexByName(_name, 0, _name.Length()) + 1;
} else {
childNode->Index = parentNode->Children.Size();
}
InsertNode(parentNode, childNode, childNode->Index);
return Iterator(childNode, this);
} else {
// if this is from another tree, just add at the path
return Add(_itr.GetPath(), _name, _value);
}
}
/*************************************************************************/
void ZKVTree::Clear()
{
for (size_t i = 0; i < RootNode.Children.Size(); i++) {
NodeCheckIn(RootNode.Children.Data()[i]);
}
RootNode.Children.Clear();
}
/*************************************************************************/
void ZKVTree::Erase( const ZString& _path )
{
Erase(Find(_path));
}
/*************************************************************************/
void ZKVTree::Erase( const Iterator& _itr )
{
ZASSERT_RUNTIME(_itr.CurrentNode != NULL, "ZKVTree: Unable to erase value from provided iterator!");
ZASSERT_RUNTIME(_itr.CurrentNode != &RootNode, "ZKVTree: Unable to erase root node!");
if (_itr.KVTree == this) {
NodeCheckIn(EraseChildNode(_itr.CurrentNode->ParentNode, _itr.CurrentNode->Index));
} else {
Erase(_itr.GetPath());
}
}
/*************************************************************************/
ZKVTree::Iterator ZKVTree::Find( const ZString& _path ) const
{
ZString path;
if (!ConvertPath(_path, path)) {
return End();
}
Node* node = GetFromPath(path);
if (node == NULL) {
return End();
}
return ZKVTree::Iterator(node, const_cast<ZKVTree*>(this));
}
/*************************************************************************/
ZKVTree::Iterator ZKVTree::Find( const Iterator& _itr, const ZString& _path ) const
{
ZString path;
if (!ConvertPath(_path, path)) {
return End();
}
Node* node = GetFromPath(_itr.GetPath() + ZKVTREE_PATH_SEPARATOR + path);
if (node == NULL) {
return End();
}
return ZKVTree::Iterator(node, const_cast<ZKVTree*>(this));
}
/*************************************************************************/
const ZString& ZKVTree::Get( const ZString& _path ) const
{
ZKVTree::Iterator itr = Find(_path);
ZASSERT_RUNTIME(itr != End(), "ZKVTree: No value mapped to path!");
return itr.GetValue();
}
/*************************************************************************/
const ZString& ZKVTree::Get( const Iterator& _itr, const ZString& _path )
{
ZKVTree::Iterator itr = Find(_itr, _path);
ZASSERT_RUNTIME(itr != End(), "ZKVTree: No value mapped to path!");
return itr.GetValue();
}
/*************************************************************************/
void ZKVTree::Merge( const ZKVTree& _other, bool _overwrite )
{
for (ZKVTree::Iterator itr = _other.Begin(); itr != _other.End(); ++itr) {
if (_overwrite) {
Put(itr.GetPath(), itr.GetValue());
} else {
ZKVTree::Iterator found = Find(itr.GetPath());
if (found == End()) {
Put(itr.GetPath(), itr.GetValue());
}
}
}
}
/*************************************************************************/
ZKVTree::Iterator ZKVTree::Put( const ZString& _path, const ZString& _value )
{
ZString path;
if (!ConvertPath(_path, path))
return End();
// create the node at the path given (will return the existing node if there)
Node* node = CreatePath(path);
// if we were able to successfully create the node, set its value
if (node != NULL) {
node->Value = _value;
return Iterator(node, this);
} else return End();
}
/*************************************************************************/
ZKVTree::Iterator ZKVTree::Put(const Iterator& _itr, const ZString& _value)
{
// ensure this iterator belongs to us
if (_itr.KVTree == this) {
// it does, just set the value
_itr.CurrentNode->Value = _value;
return _itr;
} else {
// it does not, just create a new value using the path variant
return Put(_itr.GetPath(), _value);
}
}
/*************************************************************************/
ZKVTree::Iterator ZKVTree::Begin() const
{
if (RootNode.Children.Empty()) {
return End();
} else return ZKVTree::Iterator(const_cast<Node*>(RootNode.Children.Front()), const_cast<ZKVTree*>(this));
}
/*************************************************************************/
const ZKVTree::Iterator ZKVTree::End() const
{
return ZKVTree::Iterator(const_cast<Node*>(&RootNode), const_cast<ZKVTree*>(this));
}
/*************************************************************************/
static bool isInvalidKeyNameChar(const char _char)
{
return ( isspace(_char) != 0) ||
( iscntrl(_char) != 0) ||
( _char == ZKVTREE_PATH_SEPARATOR ) ||
( _char == '[' ) ||
( _char == ']' );
}
bool ZKVTree::IsValidKeyName( const ZString& _name ) const
{
for (size_t i = 0; i < _name.Length(); i++) {
if (isInvalidKeyNameChar( _name.Data()[i])) {
return false;
}
}
return true;
}