From 77352e34b52c391fe50774e81b4e3862d79f4cfa Mon Sep 17 00:00:00 2001 From: Laury Date: Fri, 15 Apr 2022 22:30:53 +0200 Subject: [PATCH] [storage] Possibility to store metadata with records (cursor in scripts) --- apps/code/editor_controller.cpp | 8 +- apps/code/script.cpp | 15 +++ apps/code/script.h | 2 + ion/include/ion/internal_storage.h | 24 ++++- ion/include/ion/storage.h | 1 + ion/src/shared/internal_storage.cpp | 152 ++++++++++++++++++++++++++-- ion/src/shared/storage.cpp | 2 +- 7 files changed, 195 insertions(+), 9 deletions(-) diff --git a/apps/code/editor_controller.cpp b/apps/code/editor_controller.cpp index be09c9662..d286b92cf 100644 --- a/apps/code/editor_controller.cpp +++ b/apps/code/editor_controller.cpp @@ -65,13 +65,19 @@ void EditorController::viewWillAppear() { ViewController::viewWillAppear(); m_editorView.loadSyntaxHighlighter(); if(GlobalPreferences::sharedGlobalPreferences()->cursorSaving()) { - m_editorView.setCursorLocation(m_editorView.text() + strlen(m_editorView.text())); + int offset = m_script.cursorOffset(); + if (offset != -1) { + m_editorView.setCursorLocation(m_editorView.text() + offset); + } else { + m_editorView.setCursorLocation(m_editorView.text() + strlen(m_editorView.text())); + } } else { m_editorView.setCursorLocation(m_editorView.text() + strlen(m_editorView.text())); } } void EditorController::viewDidDisappear() { + m_script.setCursorOffset(m_editorView.cursorLocation() - m_script.content()); m_editorView.resetSelection(); m_menuController->scriptContentEditionDidFinish(); } diff --git a/apps/code/script.cpp b/apps/code/script.cpp index 4b39b3452..3b73d54bd 100644 --- a/apps/code/script.cpp +++ b/apps/code/script.cpp @@ -65,6 +65,21 @@ bool Script::nameCompliant(const char * name) { return false; } +uint16_t Script::cursorOffset() { + assert(!isNull()); + Ion::Storage::MetadataRowHeader * metadataForRecord = Ion::Storage::sharedStorage()->metadataForRecord(*this); + if (metadataForRecord != nullptr) { + assert(metadataForRecord->metadataSize == 2); + return *((uint16_t*) metadataForRecord->data()); + } + + return -1; +} +void Script::setCursorOffset(uint16_t position) { + assert(!isNull()); + Ion::Storage::sharedStorage()->setMetadataForRecord(*this, sizeof(uint16_t), &position); +} + uint8_t * StatusFromData(Script::Data d) { return const_cast(static_cast(d.buffer)); } diff --git a/apps/code/script.h b/apps/code/script.h index 6f7df9cda..ffa8038bc 100644 --- a/apps/code/script.h +++ b/apps/code/script.h @@ -50,6 +50,8 @@ public: void toggleAutoimportationStatus(); const char * content() const; size_t contentSize() { return value().size - k_statusSize; } + void setCursorOffset(uint16_t position); // -1 if no metadata + uint16_t cursorOffset(); /* Fetched status */ bool fetchedFromConsole() const; diff --git a/ion/include/ion/internal_storage.h b/ion/include/ion/internal_storage.h index 07287cf40..933877edb 100644 --- a/ion/include/ion/internal_storage.h +++ b/ion/include/ion/internal_storage.h @@ -64,6 +64,7 @@ public: return m_fullNameCRC32 == 0; } const char * fullName() const; + uint32_t fullNameCRC32() const { return m_fullNameCRC32; } ErrorStatus setBaseNameWithExtension(const char * baseName, const char * extension); ErrorStatus setName(const char * fullName); Data value() const; @@ -74,11 +75,19 @@ public: uint32_t m_fullNameCRC32; }; + struct MetadataRowHeader { // In fact, it's a struct with a method to get data + public: + char * data() const { return (char *) this + sizeof(MetadataRowHeader); } + uint32_t size() const { return sizeof(MetadataRowHeader) + metadataSize; } + uint32_t fullNameCRC32; + uint32_t metadataSize; // To fullfill alignment + MetadataRowHeader * nextRow; + }; #if ION_STORAGE_LOG void log(); #endif - size_t availableSize(); + size_t availableSize(char ** endBufferReturn = nullptr); size_t putAvailableSpaceAtEndOfRecord(Record r); void getAvailableSpaceFromEndOfRecord(Record r, size_t recordAvailableSpace); uint32_t checksum(); @@ -114,6 +123,11 @@ public: // Used by Python OS module int numberOfRecords(); Record recordAtIndex(int index); + + // Metadata + MetadataRowHeader * metadataForRecord(Record r); + bool setMetadataForRecord(Record r, int size, const void * metadata); + void removeMetadataForRecord(Record r); protected: InternalStorage(); /* Getters on address in buffer */ @@ -123,6 +137,13 @@ protected: const void * valueOfRecordStarting(char * start) const; void destroyRecord(const Record record); + struct MetadataMapHeader { + char * startOfMetadataMap() { return (char *) this - metadataMapSize + sizeof(MetadataMapHeader); } + uint8_t metadataMapSize; + uint8_t numberOfRows; + MetadataRowHeader * firstRow; + }; + class RecordIterator { public: RecordIterator(char * start) : m_recordStart(start) {} @@ -142,6 +163,7 @@ protected: mutable Record m_lastRecordRetrieved; mutable char * m_lastRecordRetrievedPointer; + MetadataMapHeader * m_metadataMapHeader; private: constexpr static uint32_t Magic = 0xEE0BDDBA; constexpr static size_t k_maxRecordSize = (1 << sizeof(record_size_t)*8); diff --git a/ion/include/ion/storage.h b/ion/include/ion/storage.h index 5385f4608..300e77d39 100644 --- a/ion/include/ion/storage.h +++ b/ion/include/ion/storage.h @@ -8,6 +8,7 @@ namespace Ion { class Storage : public InternalStorage { public: using InternalStorage::Record; + using InternalStorage::MetadataRowHeader; using InternalStorage::expExtension; using InternalStorage::funcExtension; diff --git a/ion/src/shared/internal_storage.cpp b/ion/src/shared/internal_storage.cpp index 5fc4a90d1..eade3a611 100644 --- a/ion/src/shared/internal_storage.cpp +++ b/ion/src/shared/internal_storage.cpp @@ -112,11 +112,138 @@ void InternalStorage::log() { } #endif -size_t InternalStorage::availableSize() { - /* TODO maybe do: availableSize(char ** endBuffer) to get the endBuffer if it - * is needed after calling availableSize */ - assert(k_storageSize >= (endBuffer() - m_buffer) + sizeof(record_size_t)); - return k_storageSize-(endBuffer()-m_buffer)-sizeof(record_size_t); +InternalStorage::MetadataRowHeader * InternalStorage::metadataForRecord(Record r) { + MetadataRowHeader * header = m_metadataMapHeader->firstRow; + for (int i = 0; i < m_metadataMapHeader->numberOfRows; i++) { + if (header->fullNameCRC32 == r.fullNameCRC32()) { + return header; + } + header = header->nextRow; + } + + return nullptr; +} + +void InternalStorage::removeMetadataForRecord(Record r) { + if (r.isNull()) { + return; + } + + MetadataRowHeader ** rowPointer = &m_metadataMapHeader->firstRow; + MetadataRowHeader * headerToRemove = nullptr; + size_t headerToRemoveSize = 0; // We compute it now as it will be more difficult later + for (int i = 0; i < m_metadataMapHeader->numberOfRows; i++) { + if ((*rowPointer)->fullNameCRC32 == r.fullNameCRC32()) { + headerToRemove = *rowPointer; + headerToRemoveSize = headerToRemove->size(); + if ((*rowPointer)->nextRow != nullptr) { + *rowPointer = (MetadataRowHeader *) ((char *) (*rowPointer)->nextRow + headerToRemove->size()); + } else { + *rowPointer = nullptr; + } + break; + } + + rowPointer = &(*rowPointer)->nextRow; + } + + if (headerToRemove == nullptr) { + return; + } + + MetadataRowHeader * header = headerToRemove->nextRow; + if (header != nullptr) { + while (header->nextRow) { + MetadataRowHeader * nextRow = header->nextRow; + header->nextRow = (MetadataRowHeader *) ((char *) header->nextRow +headerToRemoveSize); + header = nextRow; + } + } + + char * startOfMetadataMap = m_metadataMapHeader->startOfMetadataMap(); + uint8_t sizeToMove = (char *) headerToRemove - startOfMetadataMap; + + memmove(startOfMetadataMap + headerToRemoveSize, startOfMetadataMap, sizeToMove); + m_metadataMapHeader->numberOfRows--; + m_metadataMapHeader->metadataMapSize -= headerToRemoveSize; +} + +bool InternalStorage::setMetadataForRecord(Record r, int size, const void * metadata) { + int neededSize = 0; + char * endBufferPointer = nullptr; + MetadataRowHeader * headerToUpdate = nullptr; + MetadataRowHeader ** headerToUpdatePointer = nullptr; + int headerToUpdateIndex = -1; + + // We find the metadata row header for this record + MetadataRowHeader ** headerPointer = &m_metadataMapHeader->firstRow; + for (int i = 0; i < m_metadataMapHeader->numberOfRows; i++) { + if ((*headerPointer)->fullNameCRC32 == r.fullNameCRC32()) { + neededSize = size - (*headerPointer)->metadataSize; + headerToUpdate = (*headerPointer); + headerToUpdatePointer = headerPointer; + headerToUpdateIndex = i; + if (neededSize > 0 && neededSize > availableSize(&endBufferPointer)) { + return false; + } + break; + } + + headerPointer = &((*headerPointer)->nextRow); + } + + char * startOfMetadataMap = m_metadataMapHeader->startOfMetadataMap(); // Me must compute it now because it may change + + if (headerToUpdate == nullptr) { // If we didn't find a header, we need to create one + if (size != 0) { + uint8_t newRowSize = sizeof(MetadataRowHeader) + size; + + if (endBufferPointer < m_buffer + k_storageSize - m_metadataMapHeader->metadataMapSize - newRowSize) { + m_metadataMapHeader->numberOfRows++; + m_metadataMapHeader->metadataMapSize += newRowSize; + headerToUpdate = (MetadataRowHeader *) ((char *) startOfMetadataMap - newRowSize); + headerToUpdate->fullNameCRC32 = r.fullNameCRC32(); + headerToUpdate->nextRow = nullptr; + + if (m_metadataMapHeader->numberOfRows == 0) { + m_metadataMapHeader->firstRow = headerToUpdate; + } else { + ((MetadataRowHeader *) startOfMetadataMap)->nextRow = headerToUpdate; + } + + } else { + return false; + } + } else { + return true; + } + } + + if (neededSize != 0) { // If we must move some data to make or fill empty space + m_metadataMapHeader->metadataMapSize += neededSize; + memmove(startOfMetadataMap - neededSize, startOfMetadataMap, (char *) headerToUpdate + sizeof(MetadataRowHeader) - startOfMetadataMap); + + headerToUpdate = (MetadataRowHeader *) ((char *) headerToUpdate - neededSize); + MetadataRowHeader ** headerAfterPointer = headerToUpdatePointer; // Now we update each header below the one we just updated + for (int i = headerToUpdateIndex; i < m_metadataMapHeader->numberOfRows; i++) { + (*headerAfterPointer) = (MetadataRowHeader *) ((char *) (*headerAfterPointer) - neededSize); + headerAfterPointer = &((*headerAfterPointer)->nextRow); + } + } + + headerToUpdate->metadataSize = size; + memcpy(headerToUpdate->data(), metadata, size); + + return true; +} + +size_t InternalStorage::availableSize(char ** endBufferReturn) { + char * endBufferPointer = endBuffer(); + if (endBufferReturn) { + *endBufferReturn = endBufferPointer; + } + assert(k_storageSize >= (endBufferPointer - m_buffer) + sizeof(record_size_t) + m_metadataMapHeader->metadataMapSize); + return k_storageSize-(endBufferPointer-m_buffer)-sizeof(record_size_t) - m_metadataMapHeader->metadataMapSize; } size_t InternalStorage::putAvailableSpaceAtEndOfRecord(InternalStorage::Record r) { @@ -126,7 +253,7 @@ size_t InternalStorage::putAvailableSpaceAtEndOfRecord(InternalStorage::Record r char * nextRecord = p + previousRecordSize; memmove(nextRecord + availableStorageSize, nextRecord, - (m_buffer + k_storageSize - availableStorageSize) - nextRecord); + (m_buffer + k_storageSize - m_metadataMapHeader->metadataMapSize - availableStorageSize) - nextRecord); size_t newRecordSize = previousRecordSize + availableStorageSize; overrideSizeAtPosition(p, (record_size_t)newRecordSize); return newRecordSize; @@ -348,6 +475,12 @@ InternalStorage::InternalStorage() : assert(m_magicFooter == Magic); // Set the size of the first record to 0 overrideSizeAtPosition(m_buffer, 0); + + // Set the metadata map header at the end of the buffer + m_metadataMapHeader = (MetadataMapHeader*) (m_buffer + k_storageSize - sizeof(MetadataMapHeader)); + m_metadataMapHeader->numberOfRows = 0; + m_metadataMapHeader->firstRow = nullptr; + m_metadataMapHeader->metadataMapSize = sizeof(MetadataMapHeader); } // PRIVATE @@ -381,6 +514,11 @@ InternalStorage::Record::ErrorStatus InternalStorage::setFullNameOfRecord(const notifyChangeToDelegate(record); m_lastRecordRetrieved = record; m_lastRecordRetrievedPointer = p; + // Update metadata map + MetadataRowHeader * row = metadataForRecord(record); + if (row != nullptr) { + row->fullNameCRC32 = Record(fullName).fullNameCRC32(); + } return Record::ErrorStatus::None; } return Record::ErrorStatus::RecordDoesNotExist; @@ -452,6 +590,8 @@ void InternalStorage::destroyRecord(Record record) { } char * p = pointerOfRecord(record); if (p != nullptr) { + // Erase metadata + InternalStorage::removeMetadataForRecord(record); record_size_t previousRecordSize = sizeOfRecordStarting(p); slideBuffer(p+previousRecordSize, -previousRecordSize); notifyChangeToDelegate(); diff --git a/ion/src/shared/storage.cpp b/ion/src/shared/storage.cpp index 50a8a4c70..3a90f51ad 100644 --- a/ion/src/shared/storage.cpp +++ b/ion/src/shared/storage.cpp @@ -21,7 +21,7 @@ size_t Storage::availableSize() { bufferSize += sizeOfRecordStarting(p); } } - return k_storageSize-bufferSize-sizeof(record_size_t); + return k_storageSize-bufferSize-sizeof(record_size_t)-InternalStorage::m_metadataMapHeader->metadataMapSize; } else { return InternalStorage::availableSize(); }