#include #include #include #define BUFFER_SYNC 0 #define BUFFER_LOCAL 1 #define BUFFER_WRITE 0 #define BUFFER_READ 1 #define MIN_ALLOC 4 struct PropertyPage { eID Id; // id of the entity that these properties are allocated for void* Data[2]; // the data for this page (read and write) size_t Size; // amount of data on this page size_t Offset; // the current offset on this page PropertyPage* Next; // the next page, or NULL ZArray> Avail; // available data slots on this page ZArray> Updates; // properties that have been updated since last update // c'tor PropertyPage(size_t alloc_size) : Next(NULL) { Data[0] = SST_OS_AllocPages(alloc_size*2); // allocate 2x to account for read and write Data[1] = (char*)Data[0] + alloc_size; // split in half, with first half being write and second for read Size = alloc_size; Offset = 0; } // d'tor ~PropertyPage() { delete Next; if (Size > 0) { SST_OS_FreePages(Data[0], Size*2); } } }; /*************************************************************************/ // Key is currently a 64 bit integer, which is partitioned as follows // // [ SYNC MOE (2) | OFFSET (14) | SUB PAGE (16) | PAGE (32) ] static ZPropertyBuffer::PropertyKey GenerateKey(ZPropertyBuffer::SyncMode mode, uint32_t page, uint16_t sub_page, uint16_t offset) { uint64_t key; key = mode; key = key << 14; key = key + offset; key = key << 16; key = key + sub_page; key = key << 32; key = key + page; // dumb assert static_assert(sizeof(eID) == 4, "Cannot generate property key with sizeof(eID) not 32 bits, update property buffer"); return key; } static void ParseKey(ZPropertyBuffer::PropertyKey key, ZPropertyBuffer::SyncMode* mode, uint32_t* page, uint16_t* sub_page, uint16_t* offset) { *page = (uint32_t)(key & 0xFFFFFFFF); *sub_page = (uint16_t)((key >> 32) & 0xFFFF); *offset = (uint16_t)((key >> 48) & 0x3FFF); *mode = (ZPropertyBuffer::SyncMode)((key >> 62) & 0xFFFF); } void* ZPropertyBuffer::GetData( PropertyKey key, int rw, PropertyPage** page_out /*= NULL*/, SyncMode* mode_out /*= NULL*/ ) { SyncMode mode; uint32_t page_idx; uint16_t sub_page_idx, offset; ParseKey(key, &mode, &page_idx, &sub_page_idx, &offset); // parse data key ZArray& pages = mode != SYNC_NONE ? Pages[BUFFER_SYNC] : Pages[BUFFER_LOCAL]; // determine if we are using sync or local data if (pages.Size() >= page_idx) { // determine if we have this many pages PropertyPage* page = pages[page_idx]; for (size_t i = 0; i < sub_page_idx; i++) { // see of we can find the sub page page = page->Next; if (page == NULL) { // check for bad sub page index return NULL; } } if (offset % MIN_ALLOC == 0 && offset <= page->Size - MIN_ALLOC) { // see if our offset is valid if (page_out != NULL) *page_out = page; // get pointer to page if (mode_out != NULL) *mode_out = mode; // get sync value return (char*)page->Data[rw] + offset; // get read / write pointer } else return NULL; } else return NULL; } /*************************************************************************/ ZPropertyBuffer::Property ZPropertyBuffer::AllocData( PropertyPage* page, eID id, uint32_t page_idx, size_t size, SyncMode mode) { Property prop; ZArray& pages = mode ? Pages[BUFFER_SYNC] : Pages[BUFFER_LOCAL]; // determine if we are using sync or local data size_t alloc_size = size > MIN_ALLOC ? (size + MIN_ALLOC - 1) & ~(MIN_ALLOC - 1) // ensure we allocate at least MIN_ALLOC, and align to MIN_ALLOC : size; uint16_t sub_page_idx; for (sub_page_idx = 0; page != NULL; sub_page_idx++) { // loop pages seeing if we can drop the data in here if (page->Offset + size <= page->Size) { // see if we have room uint16_t offset = (uint16_t)page->Offset; // we do, so get current offset page->Offset = page->Offset + alloc_size; // increment stored offset by alloc size, not property size prop.Key = GenerateKey(mode, page_idx, sub_page_idx, offset); // key for this value prop.Write = (char*)page->Data[BUFFER_WRITE] + offset; // write pointer for this value prop.Read = (char*)page->Data[BUFFER_READ] + offset; // read pointer for this value prop.Size = size; // size of this value return prop; } else { for (size_t i = 0; i < page->Avail.Size(); i++) { // for each available slot from previous deallocs if (page->Avail[i].Second >= size) { // see if we have room uint16_t offset = (uint16_t)page->Avail[i].First; // we do, so store the offset page->Avail.Erase(i); // no longer available prop.Key = GenerateKey(mode, page_idx, sub_page_idx, offset); // key for this value prop.Write = (char*)page->Data[BUFFER_WRITE] + offset; // write pointer for this value prop.Read = (char*)page->Data[BUFFER_READ] + offset; // read pointer for this value prop.Size = size; // size of this value return prop; } } if (page->Next != NULL) { page = page->Next; // NEXT } else { sub_page_idx++; // sub page index will be next index break; } } } // found nothing, time to allocate new if (Pages[BUFFER_LOCAL].Size() <= page_idx) { // push NULL entry for local buffer if not present at page_idx Pages[BUFFER_LOCAL].PushBack(NULL); } if (Pages[BUFFER_SYNC].Size() <= page_idx) { // push NULL entry for sync buffer if not present at page_idx Pages[BUFFER_SYNC].PushBack(NULL); } const uint32_t page_size = SST_OS_GetPageSize(); // system page size const uint32_t min_buffer_alloc = page_size / 2; // minimum amount we will allocate uint32_t page_alloc_size = min_buffer_alloc; // starting page alloc if (size > page_alloc_size) { page_alloc_size = ((size+min_buffer_alloc-1) & ~(min_buffer_alloc-1)); // round up to nearest multiple of min_buffer_alloc } PropertyPage* npage = new PropertyPage(page_alloc_size); // new page npage->Id = id; // set id for page npage->Offset = alloc_size; // allocate enough space for the first property if (page != NULL) { // link page into page list page->Next = npage; } else { // allocating new pages pages[page_idx] = npage; } prop.Key = GenerateKey(mode, page_idx, sub_page_idx, 0); // generate a key for the correct page with sub-page and offset 0 prop.Write = (char*)npage->Data[BUFFER_WRITE]; // set the write pointer prop.Read = (char*)npage->Data[BUFFER_READ]; // set the read pointer prop.Size = size; // set the size of the data return prop; } /*************************************************************************/ ZPropertyBuffer::ZPropertyBuffer() { } /*************************************************************************/ ZPropertyBuffer::~ZPropertyBuffer() { for (size_t i = 0; i < Pages[BUFFER_LOCAL].Size(); i++) { delete Pages[BUFFER_LOCAL][i]; } for (size_t i = 0; i < Pages[BUFFER_SYNC].Size(); i++) { delete Pages[BUFFER_SYNC][i]; } } /*************************************************************************/ ZPropertyBuffer::Property ZPropertyBuffer::AllocProperty(eID id, size_t size, SyncMode mode) { // sanity check - synchronized properties should be less than ZSIM_BUFFER_SYNC_MAX ZASSERT_RUNTIME(!mode || size < ZSIM_SYNCBUFFER_MAX, "Synchronized property allocation too large!"); auto itr = IdPageMap.Find(id); ZArray& pages = mode != SYNC_NONE ? Pages[BUFFER_SYNC] : Pages[BUFFER_LOCAL]; if (itr != IdPageMap.End()) { uint32_t page_idx = itr.GetValue(); PropertyPage* page = pages[page_idx]; return AllocData(page, id, page_idx, size, mode); } if (Avail.Size() > 0) { size_t page_idx = Avail.PopBack(); IdPageMap.Put(id, page_idx); return AllocData(NULL, id, page_idx, size, mode); } else { IdPageMap.Put(id, pages.Size()); return AllocData(NULL, id, pages.Size(), size, mode); } } /*************************************************************************/ void ZPropertyBuffer::DeallocEntity( eID id ) { auto itr = IdPageMap.Find(id); if (itr != IdPageMap.End()) { uint32_t page_idx = itr.GetValue(); delete Pages[BUFFER_LOCAL][page_idx]; delete Pages[BUFFER_SYNC][page_idx]; Pages[BUFFER_LOCAL][page_idx] = NULL; Pages[BUFFER_SYNC][page_idx] = NULL; Avail.PushBack(page_idx); IdPageMap.Erase(id); } } /*************************************************************************/ void ZPropertyBuffer::DeallocProperty( PropertyKey key, size_t size ) { SyncMode mode; uint32_t page_idx; uint16_t sub_page_idx, offset; ParseKey(key, &mode, &page_idx, &sub_page_idx, &offset); ZArray& pages = mode != SYNC_NONE ? Pages[BUFFER_SYNC] : Pages[BUFFER_LOCAL]; PropertyPage* page = pages[page_idx]; for (size_t i = 0; i < sub_page_idx; i++) { page = page->Next; } page->Avail.PushBack(ZPair(offset, size)); } /*************************************************************************/ void ZPropertyBuffer::FlagModified( PropertyKey key, size_t size ) { PropertyPage* page; SyncMode mode; void* data = GetData(key, BUFFER_WRITE, &page, &mode); ZASSERT(data != NULL && page != NULL, "Attempt to flag modified on invalid property key"); page->Updates.PushBack(ZPair(key, size)); } /*************************************************************************/ void ZPropertyBuffer::UpdateModified(bool sync, ZNetworkUpdateStream& stream) { // wherever we have an update, copy the write buffer for (size_t b = 0; b < 2; b++) { for (size_t i = 0; i < Pages[b].Size(); i++) { PropertyPage* page = Pages[b][i]; ZPropertyBufferUpdate* buffer_update = NULL; // synchronized buffers require a property buffer update call if (sync && b == BUFFER_SYNC && page != NULL && page->Updates.Size() > 0) { buffer_update = stream.AllocUpdate(page->Id); } // for all pages and sub-pages while (page != NULL) { for (size_t j = 0; page != NULL && j < page->Updates.Size(); j++) { PropertyKey key = page->Updates[j].First; size_t size = page->Updates[j].Second; SyncMode mode; uint32_t page_idx; uint16_t sub_page_idx, offset; ParseKey(key, &mode, &page_idx, &sub_page_idx, &offset); void* data = (char*)page->Data[BUFFER_WRITE] + offset; MemCopyChar((char*)page->Data[BUFFER_READ] + offset, data, size); // if needed, make it happen if (buffer_update != NULL && mode == SYNC_SEND) { ZPropertyBufferUpdate::Update update; update.SubPage = sub_page_idx; update.Offset = offset; update.Size = size; update.Data = (char*)page->Data[BUFFER_READ] + offset; buffer_update->Updates.PushBack(update); } } page->Updates.Clear(); page = page->Next; } // push the entity update if (buffer_update != NULL) { stream.EnqueueOutgoingUpdate(buffer_update); } } } } /*************************************************************************/ void ZPropertyBuffer::UpdateModified( eID id, bool sync, ZNetworkUpdateStream& stream ) { auto itr = IdPageMap.Find(id); if (itr != IdPageMap.End()) { uint32_t page_idx = itr.GetValue(); // wherever we have an update, copy the write buffer for (size_t b = BUFFER_LOCAL; b != BUFFER_SYNC; b = BUFFER_SYNC) { PropertyPage* page = Pages[b][page_idx]; ZPropertyBufferUpdate* buffer_update = NULL; // synchronized buffers require a property buffer update call if (sync && b == BUFFER_SYNC && page != NULL) { buffer_update = stream.AllocUpdate(id); } // for all pages and sub-pages while (page != NULL) { for (size_t j = 0; page != NULL && j < page->Updates.Size(); j++) { PropertyKey key = page->Updates[j].First; size_t size = page->Updates[j].Second; SyncMode mode; uint32_t page_idx; uint16_t sub_page_idx, offset; ParseKey(key, &mode, &page_idx, &sub_page_idx, &offset); void* data = (char*)page->Data[BUFFER_WRITE] + offset; MemCopyChar((char*)page->Data[BUFFER_READ] + offset, data, size); if (buffer_update != NULL && mode == SYNC_SEND) { ZPropertyBufferUpdate::Update update; update.SubPage = sub_page_idx; update.Offset = offset; update.Size = size; update.Data = (char*)page->Data[BUFFER_READ] + offset; buffer_update->Updates.PushBack(update); } } page->Updates.Clear(); page = page->Next; } // push the entity update if (buffer_update != NULL) { stream.EnqueueOutgoingUpdate(buffer_update); } } } else { SystemLogError("Unable to update entity property buffer - invalid id"); } } /*************************************************************************/ static void* GetSyncData(PropertyPage* page, uint16_t offset) { if (offset % MIN_ALLOC == 0 && offset <= page->Size - MIN_ALLOC) { // see if our offset is valid return (char*)page->Data[BUFFER_READ] + offset; // return read } else return NULL; } void ZPropertyBuffer::ApplyBufferUpdates( const ZArray& updates ) { for (size_t i = 0; i < updates.Size(); i++) { ZPropertyBufferUpdate& buffer_update = *updates[i]; eID id = buffer_update.Id; ZArray& pages = Pages[BUFFER_SYNC]; // always sync data auto itr = IdPageMap.Find(id); // find page mapped for id if (itr != IdPageMap.End()) { // make sure we have a valid one uint32_t page_idx = itr.GetValue(); PropertyPage* base_page = pages[page_idx]; for (size_t j = 0; j < buffer_update.Updates.Size(); j++) { ZPropertyBufferUpdate::Update& update = buffer_update.Updates[j]; uint16_t sub_page_idx = update.SubPage; uint16_t offset = update.Offset; size_t size = update.Size; PropertyPage* sub_page = base_page; for (uint16_t k = 0; k < sub_page_idx; k++) { if (sub_page->Next != NULL) { sub_page = sub_page->Next; } } if (sub_page != NULL) { void* rd_ptr = GetSyncData(sub_page, offset); if (rd_ptr != NULL) { MemCopyChar(rd_ptr, update.Data, size); } else { SystemLogError("Recieved invalid buffer update offset"); } } else { SystemLogError("Recieved invalid buffer update sub page"); } } } } }