

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
//_/_/
//_/_/  This file is part of CMSDK version 2.1.1.260726 (26 July 2026)
//_/_/  
//_/_/  developed by
//_/_/  
//_/_/  Communicative Machines Ltd. 
//_/_/    http://cmlabs.com/
//_/_/  Copyright (c) 2026
//_/_/  
//_/_/  in collaboration with 
//_/_/  
//_/_/  Icelandic Institute for Intelligent Machines ses
//_/_/    Menntavegur 1
//_/_/    101 Reykjavik
//_/_/    ICELAND
//_/_/    http://www.iiim.is/
//_/_/
//_/_/  and is released under the following
//_/_/  
//_/_/  EXTENDED BSD License
//_/_/
//_/_/  Redistribution and use in source and binary forms, with or without
//_/_/  modification, is permitted provided that the following conditions 
//_/_/  are met:
//_/_/
//_/_/  - Redistributions of source code must retain the above copyright 
//_/_/    and collaboration notice, this list of conditions and the following 
//_/_/    disclaimer.
//_/_/
//_/_/  - Redistributions in binary form must reproduce the above copyright 
//_/_/    notice, this list of conditions and the following disclaimer in the 
//_/_/    documentation and/or other materials provided with the distribution.
//_/_/
//_/_/  - Neither the name of its copyright holders nor the names of its 
//_/_/    contributors may be used to endorse or promote products derived from 
//_/_/    this software without specific prior written permission.
//_/_/
//_/_/  - CADIA Clause: The license granted in and to the software under this 
//_/_/    agreement is a limited-use license. The software may not be used in 
//_/_/    furtherance of: (i) intentionally causing bodily injury or severe 
//_/_/    emotional harm to any person; (ii) invading the personal privacy or 
//_/_/    violating the human rights of any person; or (iii) committing or 
//_/_/    preparing for any act of war. 
//_/_/
//_/_/  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
//_/_/  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
//_/_/  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
//_/_/  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 
//_/_/  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
//_/_/  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
//_/_/  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
//_/_/  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
//_/_/  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
//_/_/  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
//_/_/  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//_/_/
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
/*! @file MemoryMaps.tpl.h
    @brief Template implementations of the GenericMemoryMap shared-map algorithms.
    @ingroup Memory

    Included at the bottom of MemoryMaps.h and DataMapsMemory.h so the template bodies are
    visible wherever the maps are instantiated. The algorithms operate directly on raw
    mapped map regions: a GenericMapHeader, an occupancy bitfield, then fixed-size entries.
    Callers are responsible for holding the owning segment's cross-process mutex.

    @warning These functions write directly into shared memory; keep them in sync with the
    entry-struct layouts and rebuild all binaries after any change (`make clean && make`).

    Typical two-phase usage (caller holds the segment mutex; @c data is the raw mapped
    map region starting with a GenericMapHeader):
    @code
    uint16 id;
    TypeMapEntry* e = GenericMemoryMap<TypeMapEntry,uint16>::CreateFirstFreeEntry(data, id, "Vision.Face");
    if (e) {
        // ... negotiate id with the rest of the cluster ...
        GenericMemoryMap<TypeMapEntry,uint16>::ConfirmEntry(data, id);   // commit (status 2)
        // or GenericMemoryMap<TypeMapEntry,uint16>::CancelEntry(data, id); // roll back
    }
    uint16 found = GenericMemoryMap<TypeMapEntry,uint16>::GetEntryID(data, "Vision.Face");
    @endcode */

#include "Utils.h"

namespace cmlabs {

/*! Reserve the map entry at index @a id and stamp it with @a name.

    Sets the occupancy bit, initialises the entry (status 1 = assigned/unconfirmed,
    creation time = now) and increments the map's count. If the slot is already
    occupied by an entry with the same name, that existing entry is returned instead
    (idempotent re-registration); a name clash on an occupied slot returns NULL.

    @param data Raw mapped map region (GenericMapHeader at offset 0).
    @param id   Fixed index to reserve; must be less than GenericMapHeader::maxID.
    @param name Entry name (truncated to MAXKEYNAMELEN); may be NULL to skip naming.
    @return Pointer to the entry inside shared memory, or NULL on failure.
    @warning The returned pointer aliases shared memory: it is only valid while the
    segment mapping is unchanged and the caller holds the segment's cross-process mutex.
    Follow up with ConfirmEntry() (commit) or CancelEntry() (roll back); GetEntry()
    ignores entries still in status 1. */
template <typename T, typename ID>
T* GenericMemoryMap<T,ID>::CreateEntry(char* data, ID id, const char* name) {
	T* entry = NULL;
	if (data == NULL) {
		fprintf(stderr, "CreateEntry %u no data provided\n", id);
		return NULL;
	}
	GenericMapHeader* header = (GenericMapHeader*)data;

	if (id >= header->maxID) {
		fprintf(stderr, "CreateEntry bit location %u out of range\n", id);
		return NULL;
	}

	//printf("{%X} {%X} {%X} {%X}\n", 
	//	*(uint32*)((char*)header + sizeof(GenericMapHeader)),
	//	*(uint32*)((char*)header + sizeof(GenericMapHeader) + 4),
	//	*(uint32*)((char*)header + sizeof(GenericMapHeader) + 8),
	//	*(uint32*)((char*)header + sizeof(GenericMapHeader) + 12)	); fflush(stdout);

	bit check;
	if (!utils::GetBit(id, ((char*)header + sizeof(GenericMapHeader)), header->bitFieldSize, check) || (check == BITOCCUPIED)) {
		//fprintf(stderr, "CreateEntry bit location %u already in use\n", id);
		// Already in use, let's check if it is the same one
		entry = GetEntry(data, id);
		if (entry && (strcmp(name, entry->name) == 0)) {
			// Same entry name, return this existing one
			return entry;
		}
		else
			return NULL;
	}

	// Reserve it
	utils::SetBit(id, BITOCCUPIED, ((char*)header + sizeof(GenericMapHeader)), header->bitFieldSize);

	// Calc offset
	uint32 offset = sizeof(GenericMapHeader) + header->bitFieldSize + id * sizeof(T);
	if (offset > header->size) {
		fprintf(stderr, "CreateEntry offset error in Memory Map\n");
		return NULL;
	}
	entry = (T*) (data+offset);
	if ((entry->id == id) || (entry->time == 0)) {
		entry->id = id;
		entry->status = 1; // only assigned, not ready
		entry->time = GetTimeNow();
		if (name != NULL)
			utils::strcpyavail(entry->name, name, MAXKEYNAMELEN, true);
		header->count++;
		return entry;
	}
	else {
		fprintf(stderr, "CreateEntry entry error in Memory Map\n");
		return NULL;
	}
}


/*! Reserve the lowest free index in the map and stamp it with @a name.

    Same two-phase semantics as CreateEntry(), but the index is chosen by scanning the
    occupancy bitfield for the first free bit instead of being supplied by the caller.
    Used when the id value carries no meaning and only uniqueness matters.

    Operates on the raw mapped map region passed in @a data.
    Receives the allocated index in @a id; @a name is the entry name (may be NULL).
    @return Pointer into shared memory (valid only under the segment mutex), or NULL
    if the map is full or the slot is corrupt.
    @note Not atomic across processes by itself; the caller must hold the segment
    mutex or two processes may race for the same free bit. */
template <typename T, typename ID>
T* GenericMemoryMap<T,ID>::CreateFirstFreeEntry(char* data, ID &id, const char* name) {
	T* entry = NULL;
	if (data == NULL)
		return NULL;
	GenericMapHeader* header = (GenericMapHeader*)data;

	//printf("{%X} {%X} {%X} {%X}\n", 
	//	*(uint32*)((char*)header + sizeof(GenericMapHeader)),
	//	*(uint32*)((char*)header + sizeof(GenericMapHeader) + 4),
	//	*(uint32*)((char*)header + sizeof(GenericMapHeader) + 8),
	//	*(uint32*)((char*)header + sizeof(GenericMapHeader) + 12)	); fflush(stdout);

	uint32 loc;
	if (!utils::GetFirstFreeBitLoc(((char*)header + sizeof(GenericMapHeader)), header->bitFieldSize, loc) || (loc >= header->maxID))  {
		fprintf(stderr, "No more bit locations available\n");
		return NULL;
	}

	// Reserve it
	id = (ID)loc;
	utils::SetBit(id, BITOCCUPIED, ((char*)header + sizeof(GenericMapHeader)), header->bitFieldSize);

	// Calc offset
	uint32 offset = sizeof(GenericMapHeader) + header->bitFieldSize + id * sizeof(T);
	if (offset > header->size) {
		fprintf(stderr, "Offset error in Memory Map\n");
		return NULL;
	}
	entry = (T*) (data+offset);
	if ((entry->id == id) || (entry->time == 0)) {
		entry->id = id;
		entry->status = 1; // only assigned, not ready
		entry->time = GetTimeNow();
		if (name != NULL)
			utils::strcpyavail(entry->name, name, MAXKEYNAMELEN, true);
		header->count++;
		return entry;
	}
	else {
		fprintf(stderr, "Entry error in Memory Map\n");
		return NULL;
	}
}

/*! Free the entry at index @a id.

    Verifies the slot actually holds entry @a id (matching id field and non-zero time)
    before zeroing its timestamp, decrementing the count and clearing the occupancy bit.
    The entry bytes themselves are not wiped; the slot may be recycled by a later
    CreateEntry()/CreateFirstFreeEntry().

    @return true if the entry existed and was freed; false on a stale/invalid id.
    @note Caller must hold the segment's cross-process mutex. */
template <typename T, typename ID>
bool GenericMemoryMap<T,ID>::DeleteEntry(char* data, ID id) {
	T* entry = NULL;
	if (data == NULL)
		return false;
	GenericMapHeader* header = (GenericMapHeader*)data;

	// Calc offset
	uint32 offset = sizeof(GenericMapHeader) + header->bitFieldSize + id * sizeof(T);
	if (offset > header->size)
		return false;
	entry = (T*) (data+offset);
	if ((entry->id != id) || (entry->time == 0))
		return false;

	// Free it
	entry->time = 0;
	header->count--;
	utils::SetBit(id, BITFREE, ((char*)header + sizeof(GenericMapHeader)), header->bitFieldSize);
	return true;
}


/*! Creation timestamp of entry @a id.
    @return Time (µs) the entry was created, or 0 if the entry is absent/unconfirmed. */
template <typename T, typename ID>
uint64 GenericMemoryMap<T,ID>::GetEntryTime(char* data, ID id) {
	T* entry = GetEntry(data, id);
	if (entry == NULL)
		return 0;
	return entry->time;
}

/*! Fetch the confirmed entry at index @a id.

    Only returns entries that are fully committed (status greater than 1, i.e. past
    ConfirmEntry()); entries still in the reserved/unconfirmed state are treated as
    absent. This is what makes the two-phase create-then-confirm protocol race-safe
    for readers.

    @return Pointer into shared memory (valid only under the segment mutex and while
    the mapping is unchanged), or NULL if free, unconfirmed or out of range. */
template <typename T, typename ID>
T* GenericMemoryMap<T,ID>::GetEntry(char* data, ID id) {
	T* entry = NULL;
	if (data == NULL)
		return NULL;
	GenericMapHeader* header = (GenericMapHeader*)data;

	// Calc offset
	uint32 offset = sizeof(GenericMapHeader) + header->bitFieldSize + id * sizeof(T);
	if (offset > header->size)
		return NULL;
	entry = (T*) (data+offset);
	if ((entry->id == id) && (entry->time != 0) && (entry->status > 1))
		return entry;
	else
		return NULL;
}

/*! Commit a reserved entry: promote status from 1 (assigned) to 2 (ready).

    Second phase of the two-phase id allocation used for cluster-wide agreement: once
    confirmed, the entry becomes visible to GetEntry()/GetEntryID() in every process.
    @return true if the entry existed and was confirmed (or already ready); false on
    a stale/invalid id. */
template <typename T, typename ID>
bool GenericMemoryMap<T,ID>::ConfirmEntry(char* data, ID id) {
	T* entry = NULL;
	if (data == NULL)
		return false;
	GenericMapHeader* header = (GenericMapHeader*)data;

	// Calc offset
	uint32 offset = sizeof(GenericMapHeader) + header->bitFieldSize + id * sizeof(T);
	if (offset > header->size)
		return false;
	entry = (T*) (data+offset);
	if ((entry->id == id) && entry->time && (entry->status >= 1)) {
		entry->status = 2;
		return true;
	}
	else
		return false;
}

/*! Roll back a reserved-but-unconfirmed entry (status 1 or less), clearing its status
    and timestamp so the slot can be reused.

    @return true if the entry was cancelled or was already empty; false if the entry
    has already been confirmed (status 2); confirmed entries must be removed with
    DeleteEntry() instead.
    @note Does not clear the occupancy bit; the slot is recycled via the id/time checks
    in CreateEntry(). */
template <typename T, typename ID>
bool GenericMemoryMap<T,ID>::CancelEntry(char* data, ID id) {
	T* entry = NULL;
	if (data == NULL)
		return false;
	GenericMapHeader* header = (GenericMapHeader*)data;

	// Calc offset
	uint32 offset = sizeof(GenericMapHeader) + header->bitFieldSize + id * sizeof(T);
	if (offset > header->size)
		return false;
	entry = (T*) (data+offset);
	if ((entry->id == id) && entry->time && (entry->status <= 1)) {
		entry->status = 0;
		entry->time = 0;
		return true;
	}
	else if ((entry->id == 0) || (entry->status == 0))
		return true;
	else
		return false;
}

/*! Copy the name of confirmed entry @a id into the caller's buffer.
    @param data Raw mapped map region. @param id Entry index.
    @param name Destination buffer (caller-owned). @param maxSize Buffer capacity in bytes.
    @return true if the entry exists (confirmed) and the name was copied. */
template <typename T, typename ID>
bool GenericMemoryMap<T,ID>::GetEntryName(char* data, ID id, char* name, uint32 maxSize) {
	T* entry = GetEntry(data, id);
	if (entry == NULL)
		return false;
	return (utils::strcpyavail(name, entry->name, maxSize, true) > 0);
}

/*! Case-insensitive reverse lookup: find the id registered under @a name.

    Linearly scans every entry slot (freed slots keep their bytes but are filtered by
    status), so cost is O(maxID); hold the segment mutex while calling.

    @param data Raw mapped map region. @param name Name to look up (case-insensitive).
    @param force When true, also match reserved-but-unconfirmed entries (status 1);
    used during id negotiation.
    @return The entry's id, or 0 if not found.
    @note These maps rely on id 0 never being handed out for name-addressed entries,
    since 0 doubles as the not-found value. */
template <typename T, typename ID>
ID GenericMemoryMap<T,ID>::GetEntryID(char* data, const char* name, bool force) {
	T* entry = NULL;
	if (data == NULL)
		return 0;
	GenericMapHeader* header = (GenericMapHeader*)data;

	entry = (T*) (data + sizeof(GenericMapHeader) + header->bitFieldSize);
	while ((uint32)((char*)entry - data) < header->size) {
		if (stricmp(name, entry->name) == 0) {
			if ((entry->status > 1) || force)
				return entry->id;
			else
				return 0;
		}
		entry = entry + 1; //sizeof(TypeMapEntry);
	}
	return 0;
}

/*! Case-insensitive lookup returning the entry's sync status rather than filtering by it.

    Unlike GetEntryID(), this reports reserved entries too, letting callers distinguish
    "absent" from "being negotiated" from "ready".

    @param data Raw mapped map region. @param name Name to look up (case-insensitive).
    @param id Receives the matching entry's id when found.
    @return 0 if no entry has @a name, 1 if reserved/in-sync, 2 if confirmed/ready. */
template <typename T, typename ID>
uint8 GenericMemoryMap<T,ID>::LookupEntryID(char* data, const char* name, ID& id) {
	T* entry = NULL;
	if (data == NULL)
		return 0;
	GenericMapHeader* header = (GenericMapHeader*)data;

	uint32 n = 0;
	entry = (T*) (data + sizeof(GenericMapHeader) + header->bitFieldSize);
	while ((uint32)((char*)entry - data) < header->size) {
		if (stricmp(name, entry->name) == 0) {
			id = entry->id;
			return entry->status;
		}
		n++;
		entry = entry + 1; //sizeof(TypeMapEntry);
	}
	return 0;
}

/*! Human-readable text dump of all occupied entries, one per line
    ("id = name (time)"; unconfirmed entries prefixed with '*'). Diagnostic use. */
template <typename T, typename ID>
std::string GenericMemoryMap<T,ID>::PrintAllEntries(char* data) {
	std::string str;
	T* entry = NULL;
	if (data == NULL)
		return 0;
	GenericMapHeader* header = (GenericMapHeader*)data;

	entry = (T*) (data + sizeof(GenericMapHeader) + header->bitFieldSize);
	while ((uint32)((char*)entry - data) < header->size) {
		if (entry->id) {
			if (entry->status > 1)
				str += utils::StringFormat("%u = %s (%s)\n", entry->id, entry->name, PrintTimeString(entry->time).c_str());
			else
				str += utils::StringFormat("*%u = %s (%s)\n", entry->id, entry->name, PrintTimeString(entry->time).c_str());
		}
		entry = entry + 1; //sizeof(TypeMapEntry);
	}
	return str;
}

/*! Dump all occupied entries as HTML table rows (no surrounding table element);
    used by the node's monitoring pages. */
template <typename T, typename ID>
std::string GenericMemoryMap<T,ID>::PrintAllEntriesHTML(char* data) {
	std::string str;
	T* entry = NULL;
	if (data == NULL)
		return 0;
	GenericMapHeader* header = (GenericMapHeader*)data;

	entry = (T*) (data + sizeof(GenericMapHeader) + header->bitFieldSize);
	while ((uint32)((char*)entry - data) < header->size) {
		if (entry->id) {
			if (entry->status > 1)
				str += utils::StringFormat("<tr><td>%u</td><td>%s</td><td>%s</td></tr>", entry->id, entry->name, PrintTimeString(entry->time).c_str());
			else
				str += utils::StringFormat("<tr><td>%**u</td><td>%s</td><td>%s</td></tr>\n", entry->id, entry->name, PrintTimeString(entry->time).c_str());
		}
		entry = entry + 1; //sizeof(TypeMapEntry);
	}
	return str;
}

/*! Serialize all occupied entries as XML by delegating to each entry's toXML(),
    wrapped in the outer element named by T::GetOuterXMLName() with a count attribute. */
template <typename T, typename ID>
std::string GenericMemoryMap<T,ID>::ToXML(char* data) {
	std::string str;
	T* entry = NULL;
	if (data == NULL)
		return 0;
	GenericMapHeader* header = (GenericMapHeader*)data;

	uint32 count = 0;
	entry = (T*) (data + sizeof(GenericMapHeader) + header->bitFieldSize);
	while ((uint32)((char*)entry - data) < header->size) {
		if (entry->id) {
			str += entry->toXML();
			count++;
		}
		entry = entry + 1; //sizeof(TypeMapEntry);
	}
	std::string outerName = T::GetOuterXMLName();
	if (!count)
		return utils::StringFormat("<%s count=\"%u\" />\n", outerName.c_str(), count);
	else
		return utils::StringFormat("<%s count=\"%u\">\n%s</%s>\n", outerName.c_str(), count, str.c_str(), outerName.c_str());
}

/*! Write just the names of all occupied entries into @a msg as array @a arrayname,
    via each entry's writeIDToMsg(). Used for lightweight cluster synchronisation.
    @return true if every entry was written; aborts and returns false on the first failure. */
template <typename T, typename ID>
bool GenericMemoryMap<T, ID>::WriteAllIDsToMsg(const char* arrayname, char* data, DataMessage* msg) {
	T* entry = NULL;
	if (data == NULL)
		return false;
	GenericMapHeader* header = (GenericMapHeader*)data;

	uint32 n = 0;
	entry = (T*)(data + sizeof(GenericMapHeader) + header->bitFieldSize);
	while ((uint32)((char*)entry - data) < header->size) {
		if (entry->id) {
			if (!entry->writeIDToMsg(arrayname, n, msg))
				return false;
			n++;
		}
		entry = entry + 1; //sizeof(TypeMapEntry);
	}
	return true;
}


/*! Serialize all occupied entries (full fields) into @a msg via each entry's
    writeToMsg(), indexed 0..n-1. Used to ship whole maps to remote nodes for syncing.
    @return true if every entry was written; aborts and returns false on the first failure. */
template <typename T, typename ID>
bool GenericMemoryMap<T, ID>::WriteAllEntriesToMsg(char* data, DataMessage* msg) {
	T* entry = NULL;
	if (data == NULL)
		return false;
	GenericMapHeader* header = (GenericMapHeader*)data;

	uint32 n = 0;
	entry = (T*)(data + sizeof(GenericMapHeader) + header->bitFieldSize);
	while ((uint32)((char*)entry - data) < header->size) {
		if (entry->id) {
			if (!entry->writeToMsg(n, msg))
				return false;
			n++;
		}
		entry = entry + 1; //sizeof(TypeMapEntry);
	}
	return true;
}


/*! Number of entries currently registered in the map (from the header's count field). */
template <typename T, typename ID>
uint32 GenericMemoryMap<T,ID>::GetCount(char* data) {
	if (data == NULL)
		return false;
	return ((GenericMapHeader*)data)->count;
}

/*! Approximate bytes in use by entry payloads (count * sizeof(T)); excludes the
    header and bitfield. */
template <typename T, typename ID>
uint32 GenericMemoryMap<T,ID>::GetUsage(char* data) {
	if (data == NULL)
		return 0;
	return ((GenericMapHeader*)data)->count * sizeof(T);
}

} // namespace cmlabs
