mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-18 16:27:34 +01:00
[ion] Change Storage design (former kallax)
This commit is contained in:
committed by
EmilieNumworks
parent
1f351ddbd6
commit
b0ede47d55
@@ -5,13 +5,15 @@
|
||||
#include <apps/code/app.h>
|
||||
#include <escher/metric.h>
|
||||
|
||||
extern Ion::Storage storage;
|
||||
|
||||
namespace Code {
|
||||
|
||||
EditorController::EditorController(MenuController * menuController) :
|
||||
ViewController(nullptr),
|
||||
m_textArea(this),
|
||||
m_areaBuffer(nullptr),
|
||||
m_script(Ion::Record()),
|
||||
m_script(Ion::Storage::Record()),
|
||||
m_menuController(menuController)
|
||||
{
|
||||
m_textArea.setDelegate(this);
|
||||
@@ -26,7 +28,7 @@ void EditorController::setScript(Script script) {
|
||||
m_script = script;
|
||||
const char * scriptBody = m_script.readContent();
|
||||
size_t scriptBodySize = strlen(scriptBody)+1;
|
||||
size_t availableScriptSize = scriptBodySize + Ion::Kallax::sharedKallax()->availableSize();
|
||||
size_t availableScriptSize = scriptBodySize + storage.availableSize();
|
||||
assert(m_areaBuffer == nullptr);
|
||||
m_areaBuffer = new char[availableScriptSize];
|
||||
strlcpy(m_areaBuffer, scriptBody, scriptBodySize);
|
||||
@@ -37,7 +39,7 @@ void EditorController::setScript(Script script) {
|
||||
bool EditorController::handleEvent(Ion::Events::Event event) {
|
||||
if (event == Ion::Events::OK || event == Ion::Events::Back) {
|
||||
Script::ErrorStatus err = m_script.writeContent(m_areaBuffer, strlen(m_areaBuffer)+1);
|
||||
if (err == Script::ErrorStatus::NoEnoughSpaceAvailable) {
|
||||
if (err == Script::ErrorStatus::NoEnoughSpaceAvailable || err == Script::ErrorStatus::RecordDoesNotExist) {
|
||||
assert(false); // This should not happen as we set the text area according to the available space in the Kallax
|
||||
} else {
|
||||
stackController()->pop();
|
||||
|
||||
@@ -39,7 +39,6 @@ MenuController::MenuController(Responder * parentResponder, ScriptStore * script
|
||||
m_scriptCells[i].setParentResponder(&m_selectableTableView);
|
||||
m_scriptCells[i].editableTextCell()->textField()->setDelegate(this);
|
||||
m_scriptCells[i].editableTextCell()->textField()->setDraftTextBuffer(m_draftTextBuffer);
|
||||
m_scriptCells[i].editableTextCell()->textField()->setTextBufferSize(Script::k_nameSize);
|
||||
m_scriptCells[i].editableTextCell()->textField()->setAlignment(0.0f, 0.5f);
|
||||
m_scriptCells[i].editableTextCell()->setMargins(0, 0, 0, Metric::HistoryHorizontalMargin);
|
||||
}
|
||||
@@ -127,9 +126,8 @@ void MenuController::renameSelectedScript() {
|
||||
|
||||
void MenuController::deleteScript(Script script) {
|
||||
assert(!script.isNull());
|
||||
script.remove();
|
||||
script.destroy();
|
||||
updateAddScriptRowDisplay();
|
||||
m_selectableTableView.reloadData();
|
||||
}
|
||||
|
||||
void MenuController::reloadConsole() {
|
||||
@@ -302,7 +300,7 @@ bool MenuController::textFieldDidFinishEditing(TextField * textField, const char
|
||||
} else {
|
||||
newName = text;
|
||||
}
|
||||
Script::ErrorStatus error = m_scriptStore->scriptAtIndex(m_selectableTableView.selectedRow()).rename(newName);
|
||||
Script::ErrorStatus error = m_scriptStore->scriptAtIndex(m_selectableTableView.selectedRow()).setName(newName);
|
||||
if (error == Script::ErrorStatus::None) {
|
||||
updateAddScriptRowDisplay();
|
||||
textField->setText(newName);
|
||||
@@ -317,19 +315,15 @@ bool MenuController::textFieldDidFinishEditing(TextField * textField, const char
|
||||
app()->setFirstResponder(&m_selectableTableView);
|
||||
static_cast<AppsContainer *>(const_cast<Container *>(app()->container()))->setShiftAlphaStatus(Ion::Events::ShiftAlphaStatus::Default);
|
||||
return true;
|
||||
} else if (error == Script::ErrorStatus::NameTaken) {
|
||||
app()->displayWarning(I18n::Message::NameTaken);
|
||||
} else if (error == Script::ErrorStatus::NonCompliantName) {
|
||||
app()->displayWarning(I18n::Message::NonCompliantName);
|
||||
} else {
|
||||
assert(error == Script::ErrorStatus::NameTaken);
|
||||
// The name cannot be to long as the text field size was set accordingly
|
||||
|
||||
// TODO:
|
||||
// 2 solutions:
|
||||
// 1. display a warning with
|
||||
// app()->displayWarning(I18n::Message::ForbiddenValue);
|
||||
// But that have to be done in textFieldDidReceiveEvent when textFieldShouldFinishEditing to avoid losing edition
|
||||
// 2. Modify the name to take another one (add a number for instance).
|
||||
// This would raise issues about the available space? We can't always add a character...
|
||||
return false;
|
||||
assert(error == Script::ErrorStatus::NoEnoughSpaceAvailable);
|
||||
app()->displayWarning(I18n::Message::NameTooLong);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MenuController::textFieldDidAbortEditing(TextField * textField, const char * text) {
|
||||
@@ -337,10 +331,15 @@ bool MenuController::textFieldDidAbortEditing(TextField * textField, const char
|
||||
// The previous text was an empty name. Use a numbered default script name.
|
||||
char numberedDefaultName[k_defaultScriptNameMaxSize];
|
||||
numberedDefaultScriptName(numberedDefaultName);
|
||||
Script::ErrorStatus error = m_scriptStore->scriptAtIndex(m_selectableTableView.selectedRow()).rename(numberedDefaultName);
|
||||
Script::ErrorStatus error = m_scriptStore->scriptAtIndex(m_selectableTableView.selectedRow()).setName(numberedDefaultName);
|
||||
if (error != Script::ErrorStatus::None) {
|
||||
assert(false);
|
||||
/* Because we use the numbered default name, the name should not be
|
||||
* already taken. Plus, the script could be added only if the storage has
|
||||
* enough available space to add a script named 'script99.py' */
|
||||
}
|
||||
assert(error == Script::ErrorStatus::None);
|
||||
updateAddScriptRowDisplay();
|
||||
m_selectableTableView.reloadData();
|
||||
}
|
||||
m_selectableTableView.selectCellAtLocation(m_selectableTableView.selectedColumn(), m_selectableTableView.selectedRow());
|
||||
app()->setFirstResponder(&m_selectableTableView);
|
||||
@@ -357,13 +356,13 @@ bool MenuController::textFieldDidHandleEvent(TextField * textField, bool returnV
|
||||
}
|
||||
|
||||
void MenuController::addScript() {
|
||||
if (m_scriptStore->addNewScript()) {
|
||||
Script::ErrorStatus error = m_scriptStore->addNewScript();
|
||||
if (error == Script::ErrorStatus::None) {
|
||||
updateAddScriptRowDisplay();
|
||||
m_selectableTableView.reloadData();
|
||||
renameSelectedScript();
|
||||
return;
|
||||
}
|
||||
m_selectableTableView.reloadData();
|
||||
assert(false); // Adding a new script is called when !m_scriptStore.isFull() which guarantees that the available space in the storage is big enough
|
||||
}
|
||||
|
||||
void MenuController::configureScript() {
|
||||
@@ -419,6 +418,7 @@ void MenuController::intToText(int i, char * buffer) {
|
||||
|
||||
void MenuController::updateAddScriptRowDisplay() {
|
||||
m_shouldDisplayAddScriptRow = !m_scriptStore->isFull();
|
||||
m_selectableTableView.reloadData();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -90,6 +90,9 @@ private:
|
||||
class EvenOddEditableTextCell : public ::EvenOddEditableTextCell {
|
||||
public:
|
||||
Responder * responder() override {
|
||||
if (editableTextCell()->textField()->isEditing()) {
|
||||
return this;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
@@ -97,7 +100,7 @@ private:
|
||||
/* In the initializer list of the MenuController constructor, we initialize
|
||||
* m_scriptCells by copying k_maxNumberOfDisplayableScriptCells times the
|
||||
* constructor of an EvenOddEditableTextCell. */
|
||||
char m_draftTextBuffer[Script::k_nameSize];
|
||||
char m_draftTextBuffer[TextField::maxBufferSize()];
|
||||
EvenOddCellWithEllipsis m_scriptParameterCells[k_maxNumberOfDisplayableScriptCells];
|
||||
Shared::NewFunctionCell m_addNewScriptCell;
|
||||
EvenOddCell m_emptyCell;
|
||||
|
||||
@@ -9,29 +9,30 @@ Script::Script(Record f) :
|
||||
|
||||
bool Script::importationStatus() const {
|
||||
assert(!isNull());
|
||||
const char * body = read();
|
||||
return (body[0] == 1);
|
||||
Data d = value();
|
||||
return (((char *)d.buffer)[0] == 1);
|
||||
}
|
||||
|
||||
void Script::toggleImportationStatus() {
|
||||
assert(bodySize() >= 1);
|
||||
m_body[0] = importationStatus() ? 0 : 1;
|
||||
Data d = value();
|
||||
((char *)d.buffer)[0] = (((char *)d.buffer)[0] == 1 ? 0 : 1);
|
||||
setValue(d);
|
||||
}
|
||||
|
||||
const char * Script::readContent() const {
|
||||
const char * body = read();
|
||||
return body+k_importationStatusSize;
|
||||
assert(!isNull());
|
||||
Data d = value();
|
||||
return (const char *)d.buffer+k_importationStatusSize;
|
||||
}
|
||||
|
||||
Script::ErrorStatus Script::writeContent(const char * data, size_t size) {
|
||||
int deltaSize = (int)size+k_importationStatusSize - (int)bodySize();
|
||||
if (Ion::Kallax::sharedKallax()->moveNextRecord(start(), deltaSize)) {
|
||||
*m_size += deltaSize;
|
||||
strlcpy(m_body+k_importationStatusSize, data, size);
|
||||
return ErrorStatus::None;
|
||||
} else {
|
||||
return ErrorStatus::NoEnoughSpaceAvailable;
|
||||
}
|
||||
Ion::Storage::Record::ErrorStatus Script::writeContent(const char * data, size_t size) {
|
||||
// TODO: could we avoid this useless allocation?
|
||||
char * buffer = new char[size+k_importationStatusSize];
|
||||
strlcpy(buffer+1, data, size);
|
||||
buffer[0] = importationStatus() ? 1 : 0;
|
||||
ErrorStatus e = setValue({.buffer= buffer, .size = size+k_importationStatusSize});
|
||||
delete[] buffer;
|
||||
return e;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,13 +5,12 @@
|
||||
|
||||
namespace Code {
|
||||
|
||||
/* Record : | Total Size | Type | Name | Body |
|
||||
* Script: | AutoImportationStatus | Content |*/
|
||||
/* Record : | Total Size | Name | Body |
|
||||
* Script: | AutoImportationStatus | Content |*/
|
||||
|
||||
class Script : public Ion::Record {
|
||||
friend class ScriptStore;
|
||||
class Script : public Ion::Storage::Record {
|
||||
public:
|
||||
Script(Ion::Record f);
|
||||
Script(Ion::Storage::Record r);
|
||||
|
||||
bool importationStatus() const;
|
||||
void toggleImportationStatus();
|
||||
@@ -19,7 +18,6 @@ public:
|
||||
const char * readContent() const;
|
||||
ErrorStatus writeContent(const char * data, size_t size);
|
||||
|
||||
private:
|
||||
constexpr static size_t k_importationStatusSize = 1;
|
||||
};
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ ScriptParameterController::ScriptParameterController(Responder * parentResponder
|
||||
m_deleteScript(I18n::Message::DeleteScript),
|
||||
m_selectableTableView(this, this, 0, 1, Metric::CommonTopMargin, Metric::CommonRightMargin,
|
||||
Metric::CommonBottomMargin, Metric::CommonLeftMargin, this),
|
||||
m_script(Ion::Record()),
|
||||
m_script(Ion::Storage::Record()),
|
||||
m_menuController(menuController)
|
||||
{
|
||||
}
|
||||
@@ -22,7 +22,7 @@ void ScriptParameterController::setScript(Script script){
|
||||
}
|
||||
|
||||
void ScriptParameterController::dismissScriptParameterController() {
|
||||
m_script = Script(Ion::Record());
|
||||
m_script = Script(Ion::Storage::Record());
|
||||
stackViewController()->pop();
|
||||
}
|
||||
|
||||
|
||||
@@ -19,32 +19,14 @@ ScriptStore::ScriptStore()
|
||||
addScriptFromTemplate(ScriptTemplate::Polynomial());
|
||||
}
|
||||
|
||||
Script ScriptStore::scriptAtIndex(int index) {
|
||||
Ion::Record f = Ion::Kallax::sharedKallax()->recordOfTypeAtIndex(Ion::Record::Type::Script, index);
|
||||
return Script(f);
|
||||
}
|
||||
|
||||
Script ScriptStore::scriptNamed(const char * name) {
|
||||
Ion::Record f = Ion::Kallax::sharedKallax()->getRecord(Ion::Record::Type::Script, name);
|
||||
return Script(f);
|
||||
}
|
||||
|
||||
int ScriptStore::numberOfScripts() {
|
||||
return Ion::Kallax::sharedKallax()->numberOfRecordOfType(Ion::Record::Type::Script);
|
||||
}
|
||||
|
||||
bool ScriptStore::addNewScript() {
|
||||
return addScriptFromTemplate(ScriptTemplate::Empty());
|
||||
}
|
||||
|
||||
void ScriptStore::deleteAllScripts() {
|
||||
for (int i = 0; i < numberOfScripts(); i++) {
|
||||
scriptAtIndex(i).remove();
|
||||
scriptAtIndex(i).destroy();
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptStore::isFull() {
|
||||
return (numberOfScripts() >= k_maxNumberOfScripts || Ion::Kallax::sharedKallax()->availableSize() < k_fullFreeSpaceSizeLimit);
|
||||
return (numberOfScripts() >= k_maxNumberOfScripts || storage.availableSize() < k_fullFreeSpaceSizeLimit);
|
||||
}
|
||||
|
||||
void ScriptStore::scanScriptsForFunctionsAndVariables(void * context, ScanCallback storeFunction, ScanCallback storeVariable) {
|
||||
@@ -137,18 +119,15 @@ const char * ScriptStore::contentOfScript(const char * name) {
|
||||
return script.readContent();
|
||||
}
|
||||
|
||||
bool ScriptStore::addScriptFromTemplate(const ScriptTemplate * scriptTemplate) {
|
||||
Script::ErrorStatus ScriptStore::addScriptFromTemplate(const ScriptTemplate * scriptTemplate) {
|
||||
size_t scriptSize = strlen(scriptTemplate->content())+1;
|
||||
char * body = new char[scriptSize+Script::k_importationStatusSize];
|
||||
body[0] = 1;
|
||||
strlcpy(body+1, scriptTemplate->content(), scriptSize);
|
||||
bool result = false;
|
||||
if (Ion::Kallax::sharedKallax()->sizeOfRecordWithBody(body) <= Ion::Kallax::sharedKallax()->availableSize()) {
|
||||
Ion::Kallax::sharedKallax()->addRecord(scriptTemplate->name(), Ion::Record::Type::Script, body);
|
||||
result = true;
|
||||
}
|
||||
strlcpy(body+Script::k_importationStatusSize, scriptTemplate->content(), scriptSize);
|
||||
Script::ErrorStatus err = storage.createRecord(scriptTemplate->name(), body, scriptSize+Script::k_importationStatusSize);
|
||||
assert(err != Script::ErrorStatus::NonCompliantName);
|
||||
delete[] body;
|
||||
return result;
|
||||
return err;
|
||||
}
|
||||
|
||||
const char * ScriptStore::structID(mp_parse_node_struct_t *structNode) {
|
||||
|
||||
@@ -8,6 +8,8 @@ extern "C" {
|
||||
#include "py/parse.h"
|
||||
}
|
||||
|
||||
extern Ion::Storage storage;
|
||||
|
||||
namespace Code {
|
||||
|
||||
class ScriptStore : public MicroPython::ScriptProvider {
|
||||
@@ -17,10 +19,18 @@ public:
|
||||
static constexpr int k_maxNumberOfScripts = 8;
|
||||
|
||||
ScriptStore();
|
||||
Script scriptAtIndex(int index);
|
||||
Script scriptNamed(const char * name);
|
||||
int numberOfScripts();
|
||||
bool addNewScript();
|
||||
Script scriptAtIndex(int index) {
|
||||
return Script(storage.recordWithExtensionAtIndex(k_scriptExtension, index));
|
||||
}
|
||||
Script scriptNamed(const char * name) {
|
||||
return Script(storage.recordNamed(name));
|
||||
}
|
||||
int numberOfScripts() {
|
||||
return storage.numberOfRecordsWithExtension(k_scriptExtension);
|
||||
}
|
||||
Ion::Storage::Record::ErrorStatus addNewScript() {
|
||||
return addScriptFromTemplate(ScriptTemplate::Empty());
|
||||
}
|
||||
void deleteAllScripts();
|
||||
bool isFull();
|
||||
|
||||
@@ -31,11 +41,15 @@ public:
|
||||
/* MicroPython::ScriptProvider */
|
||||
const char * contentOfScript(const char * name) override;
|
||||
|
||||
bool addScriptFromTemplate(const ScriptTemplate * scriptTemplate);
|
||||
Ion::Storage::Record::ErrorStatus addScriptFromTemplate(const ScriptTemplate * scriptTemplate);
|
||||
private:
|
||||
// If the kallax free space has a size smaller than
|
||||
// k_fullFreeSpaceSizeLimit, we consider the script store as full.
|
||||
static constexpr int k_fullFreeSpaceSizeLimit = Ion::Record::k_sizeSize+Ion::Record::k_nameSize+Ion::Record::k_typeSize+10;
|
||||
/* If the storage available space has a smaller size than
|
||||
* k_fullFreeSpaceSizeLimit, we consider the script store as full.
|
||||
* To be able to add a new empty record, the available space should at least
|
||||
* stores a Script with default name "script99.py" (12 char), the importation
|
||||
* status (1 char), the default content "from math import *\n" (20 char) and
|
||||
* 10 char of free space. */
|
||||
static constexpr int k_fullFreeSpaceSizeLimit = sizeof(Ion::Storage::record_size_t)+12+1+20+10;
|
||||
static constexpr size_t k_fileInput2ParseNodeStructKind = 1;
|
||||
static constexpr size_t k_functionDefinitionParseNodeStructKind = 3;
|
||||
static constexpr size_t k_expressionStatementParseNodeStructKind = 5;
|
||||
|
||||
@@ -12,7 +12,6 @@ public:
|
||||
float verticalAlignment = 0.5f, KDColor textColor = KDColorBlack, KDColor = KDColorWhite);
|
||||
void setDelegate(TextFieldDelegate * delegate) { m_delegate = delegate; }
|
||||
void setDraftTextBuffer(char * draftTextBuffer);
|
||||
void setTextBufferSize(size_t size);
|
||||
bool isEditing() const;
|
||||
size_t draftTextLength() const;
|
||||
void setText(const char * text);
|
||||
@@ -31,7 +30,6 @@ protected:
|
||||
public:
|
||||
ContentView(char * textBuffer, char * draftTextBuffer, size_t textBufferSize, KDText::FontSize size, float horizontalAlignment = 0.0f, float verticalAlignment = 0.5f, KDColor textColor = KDColorBlack, KDColor = KDColorWhite);
|
||||
void setDraftTextBuffer(char * draftTextBuffer);
|
||||
void setTextBufferSize(size_t size);
|
||||
void drawRect(KDContext * ctx, KDRect rect) const override;
|
||||
bool isEditing() const { return m_isEditing; }
|
||||
const char * text() const override;
|
||||
|
||||
@@ -22,10 +22,6 @@ void TextField::ContentView::setDraftTextBuffer(char * draftTextBuffer) {
|
||||
m_draftTextBuffer = draftTextBuffer;
|
||||
}
|
||||
|
||||
void TextField::ContentView::setTextBufferSize(size_t size) {
|
||||
m_textBufferSize = size;
|
||||
}
|
||||
|
||||
void TextField::ContentView::drawRect(KDContext * ctx, KDRect rect) const {
|
||||
KDColor bckCol = m_backgroundColor;
|
||||
if (m_isEditing) {
|
||||
@@ -176,10 +172,6 @@ void TextField::setDraftTextBuffer(char * draftTextBuffer) {
|
||||
m_contentView.setDraftTextBuffer(draftTextBuffer);
|
||||
}
|
||||
|
||||
void TextField::setTextBufferSize(size_t size) {
|
||||
m_contentView.setTextBufferSize(size);
|
||||
}
|
||||
|
||||
bool TextField::isEditing() const {
|
||||
return m_contentView.isEditing();
|
||||
}
|
||||
|
||||
@@ -18,9 +18,8 @@ ion/src/shared/platform_info.o: SFLAGS += -DPATCH_LEVEL="$(call initializer_list
|
||||
|
||||
objs += $(addprefix ion/src/shared/, \
|
||||
events.o \
|
||||
kallax.o \
|
||||
record.o \
|
||||
platform_info.o \
|
||||
storage.o \
|
||||
)
|
||||
|
||||
tests += $(addprefix ion/test/,\
|
||||
|
||||
@@ -7,11 +7,10 @@
|
||||
#include <ion/console.h>
|
||||
#include <ion/display.h>
|
||||
#include <ion/events.h>
|
||||
#include <ion/kallax.h>
|
||||
#include <ion/keyboard.h>
|
||||
#include <ion/led.h>
|
||||
#include <ion/power.h>
|
||||
#include <ion/record.h>
|
||||
#include <ion/storage.h>
|
||||
#include <ion/usb.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
#ifndef ION_KALLAX_H
|
||||
#define ION_KALLAX_H
|
||||
|
||||
#include <ion/record.h>
|
||||
|
||||
/* Kallax : | Magic | Record1 | Record2 | ... | Magic |
|
||||
* | Magic | Size1 | Type1 | Name1 | BodySize1 | Body1 | Size2 | Type2 | Name2 | BodySize2 | Body2 | ... | Magic */
|
||||
|
||||
namespace Ion {
|
||||
|
||||
class Kallax {
|
||||
public:
|
||||
Kallax();
|
||||
static Kallax * sharedKallax();
|
||||
int numberOfRecordOfType(Record::Type type);
|
||||
Record recordOfTypeAtIndex(Record::Type type, int index);
|
||||
Record getRecord(Record::Type type, const char * name);
|
||||
|
||||
Record addRecord(const char * name, Record::Type type, const char * content);
|
||||
|
||||
// availableSize takes into account the the size of the last Record must be 0.
|
||||
size_t availableSize();
|
||||
|
||||
bool isNameTaken(const char * name, Record::Type type);
|
||||
bool moveNextRecord(char * start, int delta);
|
||||
size_t sizeOfRecordWithBody(const char * body) const;
|
||||
private:
|
||||
// lastUsedData takes into account the the size of the last Record must be 0.
|
||||
char * lastUsedData();
|
||||
size_t * sizeAddressOfRecordStarting(char * start) const;
|
||||
size_t sizeOfRecordStarting(char * start) const;
|
||||
Record::Type typeOfRecordStarting(char * start) const;
|
||||
char * nameOfRecordStarting(char * start);
|
||||
char * bodyOfRecordStarting(char * start);
|
||||
constexpr static size_t k_totalSize = 4096;
|
||||
constexpr static uint32_t Magic = 0xDECA0DF0;
|
||||
uint32_t m_dataHeader;
|
||||
char m_data[k_totalSize];
|
||||
uint32_t m_dataFooter;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,51 +0,0 @@
|
||||
#ifndef ION_RECORD_H
|
||||
#define ION_RECORD_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/* Record : | Total Size | Type | Name | Body | */
|
||||
|
||||
namespace Ion {
|
||||
|
||||
class Record {
|
||||
public:
|
||||
enum class Type : uint8_t {
|
||||
Null,
|
||||
Script
|
||||
};
|
||||
enum class ErrorStatus {
|
||||
None = 0,
|
||||
NameTaken = 1,
|
||||
NameTooLong = 2,
|
||||
NoEnoughSpaceAvailable = 3
|
||||
};
|
||||
Record(size_t * totalSize = nullptr, char * name = nullptr, Type type = Type::Null, char * body = nullptr);
|
||||
|
||||
bool isNull() const;
|
||||
|
||||
const char * name() const;
|
||||
ErrorStatus rename(const char * newName); // May fail if name taken or name too long
|
||||
|
||||
const char * read() const;
|
||||
ErrorStatus write(const char * data, size_t size = 0); // May fail if no more space is available
|
||||
|
||||
Type type();
|
||||
|
||||
void remove(); // Will always succeed
|
||||
constexpr static size_t k_nameSize = 50;
|
||||
constexpr static size_t k_sizeSize = sizeof(size_t);
|
||||
constexpr static size_t k_typeSize = sizeof(Type);
|
||||
protected:
|
||||
size_t bodySize() const;
|
||||
char * start();
|
||||
char * m_body;
|
||||
size_t * m_size;
|
||||
private:
|
||||
char * m_name;
|
||||
Type m_type;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
119
ion/include/ion/storage.h
Normal file
119
ion/include/ion/storage.h
Normal file
@@ -0,0 +1,119 @@
|
||||
#ifndef ION_STORAGE_H
|
||||
#define ION_STORAGE_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace Ion {
|
||||
|
||||
class Storage;
|
||||
|
||||
}
|
||||
|
||||
extern Ion::Storage storage;
|
||||
|
||||
namespace Ion {
|
||||
|
||||
/* Storage : | Magic | Record1 | Record2 | ... | Magic |
|
||||
* | Magic | Size1(uint16_t) | Name1 | Body1 | Size2(uint16_t) | Name2 | Body2 | ... | Magic */
|
||||
|
||||
class Storage {
|
||||
public:
|
||||
class Record {
|
||||
friend class Storage;
|
||||
public:
|
||||
enum class ErrorStatus {
|
||||
None = 0,
|
||||
NameTaken = 1,
|
||||
NonCompliantName = 2,
|
||||
NoEnoughSpaceAvailable = 3,
|
||||
RecordDoesNotExist = 4
|
||||
};
|
||||
struct Data
|
||||
{
|
||||
const void * buffer;
|
||||
size_t size;
|
||||
};
|
||||
Record(const char * name = nullptr);
|
||||
bool operator==(const Record & other) const {
|
||||
return m_nameCRC32 == other.m_nameCRC32;
|
||||
}
|
||||
bool isNull() const {
|
||||
return m_nameCRC32 == 0;
|
||||
}
|
||||
const char * name() const {
|
||||
return storage.nameOfRecord(*this);
|
||||
}
|
||||
ErrorStatus setName(const char * name) {
|
||||
return storage.setNameOfRecord(*this, name);
|
||||
}
|
||||
Data value() const {
|
||||
return storage.valueOfRecord(*this);
|
||||
}
|
||||
ErrorStatus setValue(Data data) {
|
||||
return storage.setValueOfRecord(*this, data);
|
||||
}
|
||||
void destroy() {
|
||||
return storage.destroyRecord(*this);
|
||||
}
|
||||
private:
|
||||
uint32_t m_nameCRC32;
|
||||
};
|
||||
Storage();
|
||||
size_t availableSize();
|
||||
Record::ErrorStatus createRecord(const char * name, const void * data, size_t size);
|
||||
int numberOfRecordsWithExtension(const char * extension);
|
||||
Record recordWithExtensionAtIndex(const char * extension, int index);
|
||||
Record recordNamed(const char * name);
|
||||
typedef uint16_t record_size_t;
|
||||
private:
|
||||
constexpr static uint32_t Magic = 0xDECA0DF0;
|
||||
constexpr static size_t k_storageSize = 4096;
|
||||
constexpr static size_t k_maxRecordSize = (1 << sizeof(record_size_t)*8);
|
||||
|
||||
/* Getters/Setters on recordID */
|
||||
const char * nameOfRecord(const Record record);
|
||||
Record::ErrorStatus setNameOfRecord(const Record record, const char * name);
|
||||
Record::Data valueOfRecord(const Record record);
|
||||
Record::ErrorStatus setValueOfRecord(const Record record, Record::Data data);
|
||||
void destroyRecord(const Record record);
|
||||
|
||||
/* Getters on address in buffer */
|
||||
record_size_t sizeOfRecordStarting(char * start) const;
|
||||
const char * nameOfRecordStarting(char * start) const;
|
||||
const void * valueOfRecordStarting(char * start) const;
|
||||
|
||||
/* Overriders */
|
||||
size_t overrideSizeAtPosition(char * position, record_size_t size);
|
||||
size_t overrideNameAtPosition(char * position, const char * name);
|
||||
size_t overrideValueAtPosition(char * position, const void * data, record_size_t size);
|
||||
|
||||
bool isNameTaken(const char * name, Record * recordToExclude = nullptr);
|
||||
bool nameCompliant(const char * name) const;
|
||||
char * endBuffer();
|
||||
size_t sizeOfRecord(const char * name, size_t size) const;
|
||||
bool slideBuffer(char * position, int delta);
|
||||
class RecordIterator {
|
||||
public:
|
||||
RecordIterator(char * start) : m_recordStart(start) {}
|
||||
char * operator*() { return m_recordStart; }
|
||||
RecordIterator& operator++();
|
||||
bool operator!=(const RecordIterator& it) const { return m_recordStart != it.m_recordStart; }
|
||||
private:
|
||||
char * m_recordStart;
|
||||
};
|
||||
RecordIterator begin() const {
|
||||
if (sizeOfRecordStarting((char *)m_buffer) == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return RecordIterator((char *)m_buffer);
|
||||
};
|
||||
RecordIterator end() const { return RecordIterator(nullptr); };
|
||||
|
||||
uint32_t m_magicHeader;
|
||||
char m_buffer[k_storageSize];
|
||||
uint32_t m_magicFooter;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,156 +0,0 @@
|
||||
#include <ion/kallax.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
Ion::Kallax kallax;
|
||||
|
||||
namespace Ion {
|
||||
|
||||
Kallax::Kallax() :
|
||||
m_dataHeader(Magic),
|
||||
m_data(),
|
||||
m_dataFooter(Magic)
|
||||
{
|
||||
size_t * p = (size_t *)m_data;
|
||||
p[0] = 0;
|
||||
}
|
||||
|
||||
Kallax * Kallax::sharedKallax() {
|
||||
return &kallax;
|
||||
}
|
||||
|
||||
int Kallax::numberOfRecordOfType(Record::Type type) {
|
||||
assert(m_dataHeader == Magic);
|
||||
assert(m_dataFooter == Magic);
|
||||
int count = 0;
|
||||
char * currentPointer = m_data;
|
||||
size_t size = sizeOfRecordStarting(currentPointer);
|
||||
while (size != 0 && currentPointer < m_data + k_totalSize) {
|
||||
if (typeOfRecordStarting(currentPointer) == type) {
|
||||
count++;
|
||||
}
|
||||
currentPointer += size;
|
||||
size = sizeOfRecordStarting(currentPointer);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
Record Kallax::recordOfTypeAtIndex(Record::Type type, int index) {
|
||||
int currentIndex = -1;
|
||||
char * currentPointer = m_data;
|
||||
size_t size = sizeOfRecordStarting(currentPointer);
|
||||
while (size != 0 && currentPointer < m_data + k_totalSize) {
|
||||
if (typeOfRecordStarting(currentPointer) == type) {
|
||||
currentIndex++;
|
||||
if (currentIndex == index) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
currentPointer += size;
|
||||
size = sizeOfRecordStarting(currentPointer);
|
||||
}
|
||||
return Record(sizeAddressOfRecordStarting(currentPointer), nameOfRecordStarting(currentPointer), type, bodyOfRecordStarting(currentPointer));
|
||||
}
|
||||
|
||||
Record Kallax::getRecord(Record::Type type, const char * name) {
|
||||
for (int i = 0; i < numberOfRecordOfType(type); i++) {
|
||||
Record currentRecord = recordOfTypeAtIndex(type, i);
|
||||
if (strcmp(currentRecord.name(), name) == 0) {
|
||||
return currentRecord;
|
||||
}
|
||||
}
|
||||
return Record();
|
||||
}
|
||||
|
||||
Record Kallax::addRecord(const char * name, Record::Type type, const char * body) {
|
||||
// assert name is short enough and there is enough space to add the record
|
||||
assert(strlen(name) < Record::k_nameSize);
|
||||
assert(availableSize() >= sizeOfRecordWithBody(body));
|
||||
// Find the end of data
|
||||
char * currentPointer = m_data;
|
||||
size_t size = sizeOfRecordStarting(currentPointer);
|
||||
while (size != 0 && currentPointer < m_data + k_totalSize) {
|
||||
currentPointer += size;
|
||||
size = sizeOfRecordStarting(currentPointer);
|
||||
}
|
||||
size_t recordSize = sizeOfRecordWithBody(body);
|
||||
// Fill totalSize
|
||||
*((size_t *)currentPointer) = recordSize;
|
||||
// Fill type
|
||||
*(currentPointer+Record::k_sizeSize) = (uint8_t)type;
|
||||
// Fill name
|
||||
strlcpy(currentPointer+Record::k_sizeSize+Record::k_typeSize, name, Record::k_nameSize);
|
||||
// Fill body
|
||||
strlcpy(currentPointer+Record::k_sizeSize+Record::k_typeSize+Record::k_nameSize, body, strlen(body)+1);
|
||||
char * nextPointer = currentPointer + recordSize;
|
||||
*((size_t *)nextPointer) = 0;
|
||||
return Record(sizeAddressOfRecordStarting(currentPointer), nameOfRecordStarting(currentPointer), type, bodyOfRecordStarting(currentPointer));
|
||||
}
|
||||
|
||||
char * Kallax::lastUsedData() {
|
||||
size_t usedSize = 0;
|
||||
char * currentPointer = m_data;
|
||||
size_t size = sizeOfRecordStarting(currentPointer);
|
||||
while (size != 0 && currentPointer < m_data + k_totalSize) {
|
||||
usedSize += size;
|
||||
currentPointer += size;
|
||||
size = sizeOfRecordStarting(currentPointer);
|
||||
}
|
||||
return currentPointer + Record::k_sizeSize;
|
||||
}
|
||||
|
||||
|
||||
size_t Kallax::availableSize() {
|
||||
return k_totalSize-(lastUsedData()-m_data);
|
||||
}
|
||||
|
||||
bool Kallax::isNameTaken(const char * name, Record::Type type) {
|
||||
char * currentPointer = m_data;
|
||||
size_t size = sizeOfRecordStarting(currentPointer);
|
||||
while (size != 0 && currentPointer < m_data + k_totalSize) {
|
||||
if (typeOfRecordStarting(currentPointer) == type && strcmp(nameOfRecordStarting(currentPointer), name) == 0) {
|
||||
return true;
|
||||
}
|
||||
currentPointer += size;
|
||||
size = sizeOfRecordStarting(currentPointer);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Kallax::moveNextRecord(char * start, int delta) {
|
||||
if (delta > (int)availableSize()) {
|
||||
return false;
|
||||
}
|
||||
char * nextRecord = start + sizeOfRecordStarting(start);
|
||||
memmove(nextRecord+delta, nextRecord, lastUsedData()-nextRecord);
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t * Kallax::sizeAddressOfRecordStarting(char * start) const {
|
||||
return (size_t *)start;
|
||||
}
|
||||
|
||||
size_t Kallax::sizeOfRecordStarting(char * start) const {
|
||||
if (start >= m_data + k_totalSize) {
|
||||
return 0;
|
||||
}
|
||||
return *(sizeAddressOfRecordStarting(start));
|
||||
}
|
||||
|
||||
Record::Type Kallax::typeOfRecordStarting(char * start) const {
|
||||
return (Record::Type)*((uint8_t *)start+Record::k_sizeSize);
|
||||
}
|
||||
|
||||
char * Kallax::nameOfRecordStarting(char * start) {
|
||||
return start+Record::k_sizeSize+Record::k_typeSize;
|
||||
}
|
||||
|
||||
char * Kallax::bodyOfRecordStarting(char * start) {
|
||||
return start+Record::k_sizeSize+Record::k_typeSize+Record::k_nameSize;
|
||||
}
|
||||
|
||||
size_t Kallax::sizeOfRecordWithBody(const char * body) const {
|
||||
return Record::k_sizeSize+Record::k_typeSize+Record::k_nameSize+strlen(body)+1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,7 +20,7 @@
|
||||
#define FORCE_LINK
|
||||
#endif
|
||||
|
||||
extern Ion::Kallax kallax;
|
||||
extern Ion::Storage storage;
|
||||
|
||||
class PlatformInfo {
|
||||
public:
|
||||
@@ -28,14 +28,16 @@ public:
|
||||
m_header(Magic),
|
||||
m_version{EPSILON_VERSION},
|
||||
m_patchLevel{PATCH_LEVEL},
|
||||
m_storageAddress(&kallax),
|
||||
m_storageAddress(&storage),
|
||||
m_footer(Magic) { }
|
||||
const char * version() const {
|
||||
assert(m_storageAddress != nullptr);
|
||||
assert(m_header == Magic);
|
||||
assert(m_footer == Magic);
|
||||
return m_version;
|
||||
}
|
||||
const char * patchLevel() const {
|
||||
assert(m_storageAddress != nullptr);
|
||||
assert(m_header == Magic);
|
||||
assert(m_footer == Magic);
|
||||
return m_patchLevel;
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
#include <ion/record.h>
|
||||
#include <ion/kallax.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
namespace Ion {
|
||||
|
||||
Record::Record(size_t * size, char * name, Type type, char * body) :
|
||||
m_body(body),
|
||||
m_size(size),
|
||||
m_name(name),
|
||||
m_type(type)
|
||||
{
|
||||
}
|
||||
|
||||
bool Record::isNull() const {
|
||||
if (m_type == Type::Null) {
|
||||
assert(m_size == nullptr);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
const char * Record::name() const {
|
||||
return m_name;
|
||||
}
|
||||
|
||||
Record::ErrorStatus Record::rename(const char * newName) {
|
||||
if (Kallax::sharedKallax()->isNameTaken(newName, m_type)) {
|
||||
return ErrorStatus::NameTaken;
|
||||
}
|
||||
if (strlen(newName) >= k_nameSize) {
|
||||
return ErrorStatus::NameTooLong;
|
||||
}
|
||||
strlcpy(m_name, newName, k_nameSize);
|
||||
return ErrorStatus::None;
|
||||
}
|
||||
|
||||
const char * Record::read() const {
|
||||
return m_body;
|
||||
}
|
||||
|
||||
Record::ErrorStatus Record::write(const char * data, size_t size) {
|
||||
int deltaSize = (int)size - (int)bodySize();
|
||||
// TODO: if this fails because deltaSize is too big, return an error?
|
||||
if (Kallax::sharedKallax()->moveNextRecord(start(), deltaSize)) {
|
||||
*m_size += deltaSize;
|
||||
strlcpy(m_body, data, size);
|
||||
return ErrorStatus::None;
|
||||
}
|
||||
return ErrorStatus::NoEnoughSpaceAvailable;
|
||||
}
|
||||
|
||||
Record::Type Record::type() {
|
||||
return m_type;
|
||||
}
|
||||
|
||||
void Record::remove() {
|
||||
Kallax::sharedKallax()->moveNextRecord(start(), -*(m_size));
|
||||
}
|
||||
|
||||
char * Record::start() {
|
||||
return m_name - k_typeSize - k_sizeSize;
|
||||
}
|
||||
|
||||
size_t Record::bodySize() const {
|
||||
return *m_size - k_nameSize - k_typeSize - k_sizeSize;
|
||||
}
|
||||
|
||||
}
|
||||
270
ion/src/shared/storage.cpp
Normal file
270
ion/src/shared/storage.cpp
Normal file
@@ -0,0 +1,270 @@
|
||||
#include <ion.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
Ion::Storage storage;
|
||||
|
||||
namespace Ion {
|
||||
|
||||
Storage::Record::Record(const char * name) {
|
||||
if (name == nullptr) {
|
||||
m_nameCRC32 = 0;
|
||||
return;
|
||||
}
|
||||
size_t lenght = strlen(name);
|
||||
size_t crc32InputSize = lenght*sizeof(char)/sizeof(uint32_t)+1;
|
||||
uint32_t * crc32Input = new uint32_t[crc32InputSize];
|
||||
memset(crc32Input, 0, crc32InputSize*sizeof(uint32_t));
|
||||
strlcpy((char *)crc32Input, name, lenght+1);
|
||||
assert((crc32InputSize*sizeof(uint32_t) & 0x3) == 0); // Assert that dataLengthInBytes is a multiple of 4
|
||||
m_nameCRC32 = Ion::crc32(crc32Input, crc32InputSize);
|
||||
}
|
||||
|
||||
Storage::Storage() :
|
||||
m_magicHeader(Magic),
|
||||
m_buffer(),
|
||||
m_magicFooter(Magic)
|
||||
{
|
||||
assert(m_magicHeader == Magic);
|
||||
assert(m_magicFooter == Magic);
|
||||
// Set the size of the first record to 0
|
||||
overrideSizeAtPosition(m_buffer, 0);
|
||||
}
|
||||
|
||||
size_t Storage::availableSize() {
|
||||
return k_storageSize-(endBuffer()-m_buffer)-sizeof(record_size_t);
|
||||
}
|
||||
|
||||
Storage::Record::ErrorStatus Storage::createRecord(const char * name, const void * data, size_t size) {
|
||||
if (!nameCompliant(name)) {
|
||||
return Record::ErrorStatus::NonCompliantName;
|
||||
}
|
||||
size_t recordSize = sizeOfRecord(name, size);
|
||||
if (recordSize >= k_maxRecordSize || recordSize > availableSize()) {
|
||||
return Record::ErrorStatus::NoEnoughSpaceAvailable;
|
||||
}
|
||||
if (isNameTaken(name)) {
|
||||
return Record::ErrorStatus::NameTaken;
|
||||
}
|
||||
// Find the end of data
|
||||
char * newRecord = endBuffer();
|
||||
// Fill totalSize
|
||||
newRecord += overrideSizeAtPosition(newRecord, (record_size_t)recordSize);
|
||||
// Fill name
|
||||
newRecord += overrideNameAtPosition(newRecord, name);
|
||||
// Fill data
|
||||
newRecord += overrideValueAtPosition(newRecord, data, size);
|
||||
// Next Record is null-sized
|
||||
overrideSizeAtPosition(newRecord, 0);
|
||||
return Record::ErrorStatus::None;
|
||||
}
|
||||
|
||||
int Storage::numberOfRecordsWithExtension(const char * extension) {
|
||||
int count = 0;
|
||||
for (char * p : *this) {
|
||||
const char * name = nameOfRecordStarting(p);
|
||||
const char * ext = name+strlen(name)-strlen(extension);
|
||||
if (strcmp(ext, extension) == 0) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
Storage::Record Storage::recordWithExtensionAtIndex(const char * extension, int index) {
|
||||
int currentIndex = -1;
|
||||
const char * name = nullptr;
|
||||
for (char * p : *this) {
|
||||
const char * currentName = nameOfRecordStarting(p);
|
||||
const char * currentExtension = currentName+strlen(currentName)-strlen(extension);
|
||||
if (strcmp(currentExtension, extension) == 0) {
|
||||
currentIndex++;
|
||||
}
|
||||
if (currentIndex == index) {
|
||||
name = currentName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (name == nullptr) {
|
||||
return Record();
|
||||
}
|
||||
return Record(name);
|
||||
}
|
||||
|
||||
Storage::Record Storage::recordNamed(const char * name) {
|
||||
for (char * p : *this) {
|
||||
const char * currentName = nameOfRecordStarting(p);
|
||||
if (strcmp(currentName, name) == 0) {
|
||||
return Record(name);
|
||||
}
|
||||
}
|
||||
return Record();
|
||||
}
|
||||
|
||||
const char * Storage::nameOfRecord(const Record record) {
|
||||
for (char * p : *this) {
|
||||
Record currentRecord(nameOfRecordStarting(p));
|
||||
if (record == currentRecord) {
|
||||
return nameOfRecordStarting(p);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Storage::Record::ErrorStatus Storage::setNameOfRecord(Record record, const char * name) {
|
||||
if (!nameCompliant(name)) {
|
||||
return Record::ErrorStatus::NonCompliantName;
|
||||
}
|
||||
if (isNameTaken(name, &record)) {
|
||||
return Record::ErrorStatus::NameTaken;
|
||||
}
|
||||
size_t nameSize = strlen(name)+1;
|
||||
for (char * p : *this) {
|
||||
Record currentRecord(nameOfRecordStarting(p));
|
||||
if (record == currentRecord) {
|
||||
size_t previousNameSize = strlen(nameOfRecordStarting(p))+1;
|
||||
record_size_t previousRecordSize = sizeOfRecordStarting(p);
|
||||
size_t newRecordSize = previousRecordSize-previousNameSize+nameSize;
|
||||
if (newRecordSize >= k_maxRecordSize || !slideBuffer(p+sizeof(record_size_t)+previousNameSize, nameSize-previousNameSize)) {
|
||||
return Record::ErrorStatus::NoEnoughSpaceAvailable;
|
||||
}
|
||||
overrideSizeAtPosition(p, newRecordSize);
|
||||
overrideNameAtPosition(p+sizeof(record_size_t), name);
|
||||
return Record::ErrorStatus::None;
|
||||
}
|
||||
}
|
||||
return Record::ErrorStatus::RecordDoesNotExist;
|
||||
}
|
||||
|
||||
Storage::Record::Data Storage::valueOfRecord(const Record record) {
|
||||
for (char * p : *this) {
|
||||
Record currentRecord(nameOfRecordStarting(p));
|
||||
if (record == currentRecord) {
|
||||
const char * name = nameOfRecordStarting(p);
|
||||
record_size_t size = sizeOfRecordStarting(p);
|
||||
const void * value = valueOfRecordStarting(p);
|
||||
return {.buffer= value, .size= size-strlen(name)-1-sizeof(record_size_t)};
|
||||
}
|
||||
}
|
||||
return {.buffer= nullptr, .size= 0};
|
||||
}
|
||||
|
||||
Storage::Record::ErrorStatus Storage::setValueOfRecord(Record record, Record::Data data) {
|
||||
for (char * p : *this) {
|
||||
Record currentRecord(nameOfRecordStarting(p));
|
||||
if (record == currentRecord) {
|
||||
record_size_t previousRecordSize = sizeOfRecordStarting(p);
|
||||
const char * name = nameOfRecordStarting(p);
|
||||
size_t newRecordSize = sizeOfRecord(name, data.size);
|
||||
if (newRecordSize >= k_maxRecordSize || !slideBuffer(p+previousRecordSize, newRecordSize-previousRecordSize)) {
|
||||
return Record::ErrorStatus::NoEnoughSpaceAvailable;
|
||||
}
|
||||
record_size_t nameSize = strlen(name)+1;
|
||||
overrideSizeAtPosition(p, newRecordSize);
|
||||
overrideValueAtPosition(p+sizeof(record_size_t)+nameSize, data.buffer, data.size);
|
||||
return Record::ErrorStatus::None;
|
||||
}
|
||||
}
|
||||
return Record::ErrorStatus::RecordDoesNotExist;
|
||||
}
|
||||
|
||||
void Storage::destroyRecord(Record record) {
|
||||
for (char * p : *this) {
|
||||
Record currentRecord(nameOfRecordStarting(p));
|
||||
if (record == currentRecord) {
|
||||
record_size_t previousRecordSize = sizeOfRecordStarting(p);
|
||||
slideBuffer(p+previousRecordSize, -previousRecordSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Storage::record_size_t Storage::sizeOfRecordStarting(char * start) const {
|
||||
return *((record_size_t *)start);
|
||||
}
|
||||
|
||||
const char * Storage::nameOfRecordStarting(char * start) const {
|
||||
return start+sizeof(record_size_t);
|
||||
}
|
||||
|
||||
const void * Storage::valueOfRecordStarting(char * start) const {
|
||||
char * currentChar = start+sizeof(record_size_t);
|
||||
while (*currentChar != 0) {
|
||||
currentChar++;
|
||||
}
|
||||
return currentChar+1;
|
||||
}
|
||||
|
||||
size_t Storage::overrideSizeAtPosition(char * position, record_size_t size) {
|
||||
*((record_size_t *)position) = size;
|
||||
return sizeof(record_size_t);
|
||||
}
|
||||
|
||||
size_t Storage::overrideNameAtPosition(char * position, const char * name) {
|
||||
return strlcpy(position, name, strlen(name)+1)+1;
|
||||
}
|
||||
|
||||
size_t Storage::overrideValueAtPosition(char * position, const void * data, record_size_t size) {
|
||||
memcpy(position, data, size);
|
||||
return size;
|
||||
}
|
||||
|
||||
bool Storage::isNameTaken(const char * name, Record * recordToExclude) {
|
||||
Record r = Record(name);
|
||||
if (r == Record()) {
|
||||
return true;
|
||||
}
|
||||
for (char * p : *this) {
|
||||
Record s(nameOfRecordStarting(p));
|
||||
if (recordToExclude && s == *recordToExclude) {
|
||||
continue;
|
||||
}
|
||||
if (s == r) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Storage::nameCompliant(const char * name) const {
|
||||
/* The name format is [a-zA-Z0-9.]+ */
|
||||
const char * currentChar = name;
|
||||
while (*currentChar != 0) {
|
||||
if ((*currentChar >= 'a' && *currentChar <= 'z') || (*currentChar >= 'A' && *currentChar <= 'Z') || (*currentChar >= '0' && *currentChar <= '9') || *currentChar == '.') {
|
||||
currentChar++;
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
char * Storage::endBuffer() {
|
||||
char * currentBuffer = m_buffer;
|
||||
for (char * p : *this) {
|
||||
currentBuffer += sizeOfRecordStarting(p);
|
||||
}
|
||||
return currentBuffer;
|
||||
}
|
||||
|
||||
size_t Storage::sizeOfRecord(const char * name, size_t dataSize) const {
|
||||
size_t nameSize = strlen(name)+1;
|
||||
return nameSize+dataSize+sizeof(record_size_t);
|
||||
}
|
||||
|
||||
bool Storage::slideBuffer(char * position, int delta) {
|
||||
if (delta > (int)availableSize()) {
|
||||
return false;
|
||||
}
|
||||
memmove(position+delta, position, endBuffer()+sizeof(record_size_t)-position);
|
||||
return true;
|
||||
}
|
||||
|
||||
Storage::RecordIterator & Storage::RecordIterator::operator++() {
|
||||
record_size_t size = *((record_size_t *)m_recordStart);
|
||||
char * nextRecord = m_recordStart+size;
|
||||
record_size_t newRecordSize = *((record_size_t *)nextRecord);
|
||||
m_recordStart = (newRecordSize == 0 ? nullptr : nextRecord);
|
||||
return *this;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user