

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
//_/_/
//_/_/  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 TemporalMemory.h
    @brief Slot/bin shared-memory store for time-limited DataMessages (the node's "dynamic" memory).
    @ingroup Memory

    Every message published in a Psyclone system carries an end-of-life (EOL) timestamp.
    TemporalMemory stores such messages in a single shared segment organised as:
    a TemporalMemoryStruct header, a slot bitfield, an array of SlotEntryStruct (one per
    stored message), and a series of *bins* — pools of fixed-size blocks (small bins for
    small messages, larger bins for big ones). A message occupies contiguous blocks in
    exactly one bin; the slot records where. Expired slots are reclaimed by maintenance().

    Message ids pack node|serial|slot (see GETMSGNODE/GETMSGSERIAL/GETMSGSLOT); the serial
    guards against reading a slot that has been recycled since the id was issued.

    @warning Shared-memory layouts; changing any struct here requires a full rebuild of
    all binaries (`make clean && make`) — see MemoryManager.h for the ABI warning. */

#if !defined(_TEMPORALMEMORY_H_)
#define _TEMPORALMEMORY_H_

#include "MemoryManager.h"
#include "PsyTime.h"

namespace cmlabs {

	/*! @brief Per-message slot record: which bin/blocks hold the message and until when.
	    @warning Shared-memory layout; see MemoryManager.h for the ABI/rebuild warning. */
	struct SlotEntryStruct {
		uint16 serial; 		/*!< Reuse serial; incremented when the slot is recycled and checked against the serial packed in the message id (wraps around). */
		uint64 eol;			/*!< Absolute end-of-life timestamp (µs); slot reclaimable after this. */
		uint32 bin;			/*!< Index of the bin holding the message data. */
		uint64 offset;		/*!< Byte offset of the message inside the bin's data area. */
		uint64 size; 		/*!< Message byte size; 0 means the slot is not in use. */
		uint64 usage; 		/*!< Bytes actually reserved (blocks used * blocksize >= size). */
		uint32 startBlock;	/*!< First block index used within the bin. */
		uint32 blockUsage;	/*!< Number of contiguous blocks used. */
		//uint32 blockSize;
	};

	/*! @brief Root header of the temporal-memory segment.
	    Followed in memory by the slot bitfield, slotCount SlotEntryStructs and the bins.
	    @warning Shared-memory layout; see MemoryManager.h for the ABI/rebuild warning. */
	struct TemporalMemoryStruct {
		uint64 size;			/*!< Total segment size in bytes. */
		uint32 cid;				/*!< Check/magic id validated on attach. */
		uint32 slotCount;		/*!< Total message slots allocated. */
		uint32 slotsInUse;		/*!< Slots currently holding live messages. */
		uint64 usage;			/*!< Bytes currently in use across all bins. */
		uint64 growStep;		/*!< Byte increment used when growing the segment. */
		uint64 maxSize;			/*!< Hard ceiling for segment growth in bytes. */
		uint64 createdTime;		// time of creation
		uint64 lastCheckTime;		/*!< Timestamp of the last expiry sweep. */
		uint64 checkTimeInterval;	/*!< Interval between expiry sweeps (µs). */
		uint16 binCount;		/*!< Number of block-size bins. */
		uint32 bitFieldSize; // number of bytes in the bitfield
		// Bitfield(bitFieldSize)
		// slotCount * SlotEntryStruct
		// bin0(size), bin1(size), bin2(size)...
	};

	/*! @brief Header of one fixed-block-size bin inside the temporal segment.
	    Followed in memory by a block-usage bitfield and blockCount*blockSize data bytes.
	    @warning Shared-memory layout; see MemoryManager.h for the ABI/rebuild warning. */
	struct BinHeaderStruct {
		uint64 size;		/*!< Total bin size in bytes (header + bitfield + data). */
		uint32 blockCount;	/*!< Number of fixed-size blocks in this bin. */
		uint32 blockSize;	/*!< Byte size of each block. */
		uint32 blocksInUse;	/*!< Blocks currently allocated. */
		uint64 usage;		/*!< Payload bytes currently stored. */
		uint32 bitFieldSize; // number of bytes in the bitfield
		// Bitfield(bitFieldSize)
		// blockCount * blockSize data
	};

	//struct BinSpaceFit {
	//	uint32 bin;
	//	uint32 blocksNeeded;
	//	uint32 spaceWaisted;
	//};
	//bool BinSpaceFitSort(const BinSpaceFit& a, const BinSpaceFit& b) {
	//	return (a.blocksNeeded < b.blocksNeeded);
	//}

/*! @name Message-id packing macros
    A 64-bit message id encodes node (16 bits), slot serial (16 bits) and slot index (32 bits).
    @{ */
#define GETMSGNODE(id) (uint16)((id & 0xffff000000000000L)>>48)
#define GETMSGSERIAL(id) (uint16)((id & 0x0000ffff00000000L)>>32)
#define GETMSGSLOT(id) (uint32)(id & 0x00000000ffffffffL)
#define SETMSGID(id,node,serial,slot) id = (((uint64)node) << 48) | (((uint64)serial) << 32) | ((uint64)slot)
/*! @} */

/*! Re-open the temporal segment if the master's resize serial no longer matches ours
    (i.e. another process re-created the segment); bails out of the calling method on failure. */
#define CHECKTEMPORALMEMORYSERIAL 	if (serial != master->getDynamicShmemSerial()) {if (!open()) {mutex->leave();return 0;}}

	class MasterMemory;
	/*! @brief Accessor for the temporal (dynamic) message store in shared memory.
	    @ingroup Memory
	    One instance per process, created by MemoryManager. The master process calls
	    create(); other processes call open(). All operations take the segment's named
	    cross-process mutex and transparently re-attach when the segment was resized by
	    another process (serial check). Backing memory is owned by the OS; @a master is
	    borrowed from MemoryManager.
	    @code
	    uint64 id;
	    temporalMemory->insertMessage(msg, id);           // copy msg into shared memory
	    DataMessage* copy = temporalMemory->getCopyOfMessage(id); // any process; delete after use
	    @endcode */
	class TemporalMemory {
	public:
		/*! @param master Segment bookkeeping interface (borrowed, not owned). */
		TemporalMemory(MemoryController* master);
		~TemporalMemory();

		/*! Report allocation/usage of the temporal segment.
		    @param alloc Receives allocated bytes. @param usage Receives used bytes. @return true on success. */
		bool getMemoryUsage(uint64& alloc, uint64& usage);

		/*! Attach to the existing temporal segment (size/serial from the master). @return true on success. */
		bool open();
		/*! Create the temporal segment (master process only).
		    @param slotCount Message slots. @param binCount Number of block-size bins.
		    @param minBlockSize Smallest bin block size (bytes). @param maxBlockSize Largest bin block size (bytes).
		    @param initSize Initial segment bytes. @param maxSize Growth ceiling in bytes.
		    @param growSteps Number of growth increments between initSize and maxSize.
		    @return true on success. */
		bool create(uint32 slotCount, uint16 binCount, uint32 minBlockSize, uint32 maxBlockSize, uint64 initSize, uint64 maxSize, uint32 growSteps = 8);
		/*! Set the node id used when composing message ids. @return true on success. */
		bool setNodeID(uint16 id);

		// Insert new block of memory and return full id
		/*! Copy @a msg into shared memory until its EOL passes.
		    @param msg Message to store; caller retains ownership.
		    @param id Receives the packed node|serial|slot id.
		    @return true on success; false if the store is full and cannot grow. Blocks briefly on the mutex. */
		bool insertMessage(DataMessage* msg, uint64& id);
		// Get copy of block of memory
		/*! Retrieve a private copy of a stored message.
		    @param id Id from insertMessage() (any process).
		    @return Caller-owned DataMessage (delete after use), or NULL if unknown/expired/recycled. */
		DataMessage* getCopyOfMessage(uint64 id);

		/*! Write current usage statistics to the log. @return true on success. */
		bool logUsage();

		/*! Expire slots whose EOL has passed and reclaim their blocks. Called periodically
		    by the master's management thread. @return true on success. */
		bool maintenance();

		/*! Self-test of the temporal store. @return true if all checks pass. */
		static bool UnitTest();

	private:

		std::vector<BinHeaderStruct> calcBinHeaders(uint32 binCount, uint32 minBlockSize, uint32 maxBlockSize, uint64 binDataSize);
		BinHeaderStruct* getBestBin(uint32 size, uint32& bin, uint32 &blocksNeeded);
		char* getMemoryBlock(uint64 id);
		bool checkSlots(uint64 now);

		bool resizeSlots();
		bool resizeBin(uint32 bin, BinHeaderStruct* binHeader, uint32 msgSize);
		std::vector<BinHeaderStruct> calcBinHeadersGrowth(uint32 bin = 0, uint32 msgSize = 0);
		bool resizeMemory(uint32 slotGrowth, std::vector<BinHeaderStruct>& binHeaderSizes);

		utils::Mutex* mutex;			/*!< Named cross-process mutex guarding the segment (owned). */
		MemoryController* master;		/*!< Bookkeeping interface for size/serial (borrowed). */
		TemporalMemoryStruct* header;	/*!< Mapped segment header (owned by the OS). */
		SlotEntryStruct* slotEntries;	/*!< Slot array within the mapped segment. */
		uint64 memorySize;				/*!< Size of our current mapping in bytes. */
		uint16 port;					/*!< Node port used in segment naming. */
		uint16 node;					/*!< Node id used in message ids. */
		uint32 serial;					/*!< Cached resize serial; mismatch with master's triggers re-open. */
	};


//	
//	
//struct TemporalMemoryStruct {
//	uint64 size;
//	uint32 cid;
//	uint64 usage;
//	uint64 growStep;
//	uint64 maxSize;
//	uint64 createdTime;		// time of creation
//	uint16 slotCount;
//	uint64 slotSize;
//	uint32 slotDuration;
//	uint16 startSlot;
//	uint64 startTime;
//	// slots * SlotHeaderStruct
//};
//
//struct SlotHeaderStruct {
//	uint64 size;
//	uint32 count;
//	uint64 usage;
//	uint64 eol;
//	uint64 offset;
//};
//
//#define GETMSGNODE(id) (uint16)((id & 0xffff000000000000L)>>48)
//#define GETMSGSLOT(id) (uint16)((id & 0x0000ffff00000000L)>>32)
//#define GETMSGOFFSET(id) (uint64)(id & 0x00000000ffffffffL)
//#define SETMSGID(id,node,slot,offset) id = (((uint64)node) << 48) | (((uint64)slot) << 32) | ((uint64)offset)
//
//#define CHECKTEMPORALMEMORYSERIAL 	if (serial != master->getDynamicShmemSerial()) {if (!open()) {mutex->leave();return 0;}}
//
//class MasterMemory;
//class TemporalMemory {
//public:
//	TemporalMemory(MemoryController* master);
//	~TemporalMemory();
//
//	bool getMemoryUsage(uint64& alloc, uint64& usage);
//
//	bool open();
//	bool create(uint16 slotCount, uint32 slotDuration, uint64 initSize, uint64 maxSize, uint32 growSteps = 8);
//	bool setNodeID(uint16 id);
//
//	// Insert new block of memory and return full id
//	bool insertMessage(DataMessage* msg, uint64& id);
//	// Get copy of block of memory
//	DataMessage* getCopyOfMessage(uint64 id);
//
//	bool logUsage();
//
//	bool maintenance();
//
//	static bool UnitTest();
//
//private:
//	bool getSlotFromEOL(uint64 eol, uint16& slot);
//	char* getMemoryBlock(uint64 id);
//	bool growSlotSize(uint64 growAmount);
//	bool checkSlots();
//	bool resetSlot(uint16 slot, uint64 eol);
//	uint64 calcResizeAmount(uint32 msgSize);
//
//	utils::Mutex* mutex;
//	MemoryController* master;
//	TemporalMemoryStruct* header;
//	SlotHeaderStruct* slotHeaders;
//	uint64 memorySize;
//	uint16 port;
//	uint16 node;
//	uint32 serial;
//};

} // namespace cmlabs

#endif //_TEMPORALMEMORY_H_

