diff --git a/apps/sequence/sequence.cpp b/apps/sequence/sequence.cpp index 0cb52c3a9..95f026c03 100644 --- a/apps/sequence/sequence.cpp +++ b/apps/sequence/sequence.cpp @@ -200,6 +200,20 @@ Poincare::Layout Sequence::SequenceHandle::name(Sequence * sequence) { return m_name; } +void Sequence::SequenceHandle::updateNewDataWithExpression(Ion::Storage::Record * record, Expression & expressionToStore, void * expressionAddress, size_t newExpressionSize, size_t previousExpressionSize) { + Ion::Storage::Record::Data newData = record->value(); + // Translate expressions located downstream + size_t sizeBeforeExpression = (char *)expressionAddress -(char *)newData.buffer; + size_t remainingSize = newData.size - sizeBeforeExpression - previousExpressionSize; + memmove((char *)expressionAddress + newExpressionSize, (char *)expressionAddress + previousExpressionSize, remainingSize); + // Copy the expression + if (!expressionToStore.isUninitialized()) { + memmove(expressionAddress, expressionToStore.addressInPool(), newExpressionSize); + } + // Update meta data + updateMetaData(record, newExpressionSize); +} + /* Definition Handle*/ void * Sequence::DefinitionHandle::expressionAddress(const Ion::Storage::Record * record) const { @@ -243,6 +257,10 @@ size_t Sequence::FirstInitialConditionHandle::expressionSize(const Ion::Storage: return static_cast(record)->recordData()->firstInitialConditionSize(); } +void Sequence::FirstInitialConditionHandle::updateMetaData(const Ion::Storage::Record * record, size_t newSize) { + static_cast(record)->recordData()->setFirstInitialConditionSize(newSize); +} + void Sequence::FirstInitialConditionHandle::buildName(Sequence * sequence) { assert(sequence->type() == Type::SingleRecurrence || sequence->type() == Type::DoubleRecurrence); char buffer[k_initialRankNumberOfDigits+1]; @@ -262,6 +280,10 @@ void * Sequence::SecondInitialConditionHandle::expressionAddress(const Ion::Stor return (char *)data.buffer+offset; } +void Sequence::SecondInitialConditionHandle::updateMetaData(const Ion::Storage::Record * record, size_t newSize) { + static_cast(record)->recordData()->setSecondInitialConditionSize(newSize); +} + size_t Sequence::SecondInitialConditionHandle::expressionSize(const Ion::Storage::Record * record) const { return static_cast(record)->recordData()->secondInitialConditionSize(); } diff --git a/apps/sequence/sequence.h b/apps/sequence/sequence.h index 4192ca9f9..9a521c5fd 100644 --- a/apps/sequence/sequence.h +++ b/apps/sequence/sequence.h @@ -101,6 +101,9 @@ private: protected: virtual void buildName(Sequence * sequence) = 0; Poincare::Layout m_name; + private: + void updateNewDataWithExpression(Ion::Storage::Record * record, Poincare::Expression & newExpression, void * expressionAddress, size_t newExpressionSize, size_t previousExpressionSize) override; + virtual void updateMetaData(const Ion::Storage::Record * record, size_t newSize) {} }; class DefinitionHandle : public SequenceHandle { @@ -115,6 +118,7 @@ private: public: void * expressionAddress(const Ion::Storage::Record * record) const override; private: + void updateMetaData(const Ion::Storage::Record * record, size_t newSize) override; size_t expressionSize(const Ion::Storage::Record * record) const override; void buildName(Sequence * sequence) override; }; @@ -123,6 +127,7 @@ private: public: void * expressionAddress(const Ion::Storage::Record * record) const override; private: + void updateMetaData(const Ion::Storage::Record * record, size_t newSize) override; size_t expressionSize(const Ion::Storage::Record * record) const override; void buildName(Sequence * sequence) override; }; diff --git a/apps/shared/expression_model_handle.cpp b/apps/shared/expression_model_handle.cpp index 65ea9d96e..ae9697a99 100644 --- a/apps/shared/expression_model_handle.cpp +++ b/apps/shared/expression_model_handle.cpp @@ -11,6 +11,8 @@ using namespace Poincare; namespace Shared { +static inline int maxInt(int x, int y) { return x > y ? x : y; } + ExpressionModelHandle::ExpressionModelHandle() : m_expression(), m_layout(), @@ -68,30 +70,45 @@ Ion::Storage::Record::ErrorStatus ExpressionModelHandle::setContent(Ion::Storage return setExpressionContent(record, e); } -Ion::Storage::Record::ErrorStatus ExpressionModelHandle::setExpressionContent(Ion::Storage::Record * record, Expression & expressionToStore) { +Ion::Storage::Record::ErrorStatus ExpressionModelHandle::setExpressionContent(Ion::Storage::Record * record, Expression & newExpression) { assert(record->fullName() != nullptr); - // Prepare the new data to store + // Prepare the new data to be stored Ion::Storage::Record::Data newData = record->value(); - size_t expressionToStoreSize = expressionToStore.isUninitialized() ? 0 : expressionToStore.size(); - newData.size = newData.size - expressionSize(record) + expressionToStoreSize; - - // Set the data + size_t previousExpressionSize = expressionSize(record); + size_t newExpressionSize = newExpression.isUninitialized() ? 0 : newExpression.size(); + size_t previousDataSize = newData.size; + size_t newDataSize = previousDataSize - previousExpressionSize + newExpressionSize; + void * expAddress = expressionAddress(record); + // Update size of record to maximal size between previous and new data + newData.size = maxInt(previousDataSize, newDataSize); Ion::Storage::Record::ErrorStatus error = record->setValue(newData); if (error != Ion::Storage::Record::ErrorStatus::None) { assert(error == Ion::Storage::Record::ErrorStatus::NotEnoughSpaceAvailable); return error; } + // Prepare the new data content + /* WARNING: expressionAddress() cannot be used while the metadata is invalid + * (as it is sometimes computed from metadata). Thus, the expression address + * is given as a parameter to updateNewDataWithExpression. */ + updateNewDataWithExpression(record, newExpression, expAddress, newExpressionSize, previousExpressionSize); + // Set the data with the right size + newData.size = newDataSize; + error = record->setValue(newData); + // Any error would have occured at the first call to setValue + assert(error == Ion::Storage::Record::ErrorStatus::None); - // Copy the expression if needed - if (!expressionToStore.isUninitialized()) { - memcpy(expressionAddress(record), expressionToStore.addressInPool(), expressionToStore.size()); - } /* Here we delete only the elements relative to the expression model kept in * this handle. */ tidy(); return error; } +void ExpressionModelHandle::updateNewDataWithExpression(Ion::Storage::Record * record, Expression & expressionToStore, void * expressionAddress, size_t expressionToStoreSize, size_t previousExpressionSize) { + if (!expressionToStore.isUninitialized()) { + memmove(expressionAddress, expressionToStore.addressInPool(), expressionToStoreSize); + } +} + void ExpressionModelHandle::tidy() const { m_layout = Layout(); m_expression = Expression(); diff --git a/apps/shared/expression_model_handle.h b/apps/shared/expression_model_handle.h index 08f1accf2..0f60adaf7 100644 --- a/apps/shared/expression_model_handle.h +++ b/apps/shared/expression_model_handle.h @@ -34,6 +34,7 @@ protected: mutable Poincare::Expression m_expression; mutable Poincare::Layout m_layout; private: + virtual void updateNewDataWithExpression(Ion::Storage::Record * record, Poincare::Expression & expressionToStore, void * expressionAddress, size_t expressionToStoreSize, size_t previousExpressionSize); virtual size_t expressionSize(const Ion::Storage::Record * record) const = 0; mutable int m_circular; };