[poincare] Ion::Storage handles fullnames and basenames with extensions

This commit is contained in:
Léa Saviot
2018-09-27 11:06:36 +02:00
committed by Émilie Feral
parent a6663f524f
commit a4868c8b09
2 changed files with 219 additions and 107 deletions

View File

@@ -5,19 +5,24 @@
namespace Ion {
/* Storage : | Magic | Record1 | Record2 | ... | Magic |
* | Magic | Size1(uint16_t) | Name1 | Body1 | Size2(uint16_t) | Name2 | Body2 | ... | Magic */
/* Storage : | Magic | Record1 | Record2 | ... | Magic |
* | Magic | Size1(uint16_t) | FullName1 | Body1 | Size2(uint16_t) | FullName2 | Body2 | ... | Magic |
*
* A record's fullName is baseName.extension. */
class Storage {
public:
typedef uint16_t record_size_t;
constexpr static size_t k_storageSize = 16384;
static Storage * sharedStorage();
class Record {
/* A Record is identified by the CRC32 on his name because:
* - a record is identified by its name which is unique
* - we cannot keep the address pointing to the name because if another
* record is modified, it might alter our record name address and keeping
* a buffer with the name will waste memory as we cannot forsee the size
* of the name. */
/* A Record is identified by the CRC32 on its fullName because:
* - A record is identified by its fullName, which is unique
* - We cannot keep the address pointing to the fullName because if another
* record is modified, it might alter our record's fullName address.
* Keeping a buffer with the fullNames will waste memory as we cannot
* forsee the size of the fullNames. */
friend class Storage;
public:
enum class ErrorStatus {
@@ -31,18 +36,19 @@ public:
const void * buffer;
size_t size;
};
Record(const char * name = nullptr);
Record(const char * fullName = nullptr);
Record(const char * basename, const char * extension);
bool operator==(const Record & other) const {
return m_nameCRC32 == other.m_nameCRC32;
return m_fullNameCRC32 == other.m_fullNameCRC32;
}
bool isNull() const {
return m_nameCRC32 == 0;
return m_fullNameCRC32 == 0;
}
const char * name() const {
return Storage::sharedStorage()->nameOfRecord(*this);
const char * fullName() const {
return Storage::sharedStorage()->fullNameOfRecord(*this);
}
ErrorStatus setName(const char * name) {
return Storage::sharedStorage()->setNameOfRecord(*this, name);
ErrorStatus setName(const char * fullName) {
return Storage::sharedStorage()->setFullNameOfRecord(*this, fullName);
}
Data value() const {
return Storage::sharedStorage()->valueOfRecord(*this);
@@ -54,41 +60,55 @@ public:
return Storage::sharedStorage()->destroyRecord(*this);
}
private:
uint32_t m_nameCRC32;
Record(const char * basename, int basenameLength, const char * extension, int extensionLength);
uint32_t m_fullNameCRC32;
};
Storage();
size_t availableSize();
Record::ErrorStatus createRecord(const char * name, const void * data, size_t size);
int numberOfRecordsWithExtension(const char * extension);
// Record creation
Record::ErrorStatus createRecordWithExtension(const char * baseName, const char * extension, const void * data, size_t size);
// Record getters
Record recordWithExtensionAtIndex(const char * extension, int index);
Record recordNamed(const char * name);
typedef uint16_t record_size_t;
constexpr static size_t k_storageSize = 16384;
Record recordNamed(const char * fullName);
Record recordBaseNamedWithExtension(const char * baseName, const char * extension);
Record recordBaseNamedWithExtensions(const char * baseName, const char * extension[], size_t numberOfExtensions);
private:
constexpr static uint32_t Magic = 0xEE0BDDBA;
constexpr static size_t k_maxRecordSize = (1 << sizeof(record_size_t)*8);
constexpr static char k_dotChar = '.';
/* Getters/Setters on recordID */
const char * nameOfRecord(const Record record);
Record::ErrorStatus setNameOfRecord(const Record record, const char * name);
const char * fullNameOfRecord(const Record record);
Record::ErrorStatus setFullNameOfRecord(const Record record, const char * fullName);
Record::ErrorStatus setBaseNameWithExtensionOfRecord(const Record record, const char * baseName, const char * extension);
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 char * fullNameOfRecordStarting(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 overrideFullNameAtPosition(char * position, const char * fullName);
size_t overrideBaseNameWithExtensionAtPosition(char * position, const char * baseName, const char * extension);
size_t overrideValueAtPosition(char * position, const void * data, record_size_t size);
bool isNameTaken(const char * name, Record * recordToExclude = nullptr);
static bool nameCompliant(const char * name);
bool isFullNameTaken(const char * fullName, const Record * recordToExclude = nullptr);
bool isBaseNameWithExtensionTaken(const char * baseName, const char * extension, Record * recordToExclude = nullptr);
static bool fullNameCompliant(const char * name);
bool fullNameHasExtension(const char * fullName, const char * extension, size_t extensionLength);
char * endBuffer();
size_t sizeOfRecord(const char * name, size_t size) const;
size_t sizeOfBaseNameAndExtension(const char * baseName, const char * extension) const;
size_t sizeOfRecordWithBaseNameAndExtension(const char * baseName, const char * extension, size_t size) const;
size_t sizeOfRecordWithFullName(const char * fullName, size_t size) const;
bool slideBuffer(char * position, int delta);
class RecordIterator {
public:

View File

@@ -27,32 +27,60 @@ Storage * Storage::sharedStorage() {
return storage;
}
Storage::Record::Record(const char * name) {
if (name == nullptr) {
m_nameCRC32 = 0;
// RECORD
Storage::Record::Record(const char * fullName) {
if (fullName == nullptr) {
m_fullNameCRC32 = 0;
return;
}
/* name is a char table. Its length in Bytes is not necessarilly a multiple
* of 4. However, crc32 method awaits input in a form of uint32_t table. To
* limit the use of additional memory, we compute 2 crc32:
* - one corresponding to name with a byte length truncated to be a multiple
* of 4
* - the other corresponds to the remaining chars in name padded with 0 to be
* 4 bytes length
* The name CRC32 is the crc32 of both. */
uint32_t crc32Results[2];
// CRC32 of the truncated name
size_t length = strlen(name);
size_t crc32TruncatedInputSize = length*sizeof(char)/sizeof(uint32_t);
crc32Results[0] = Ion::crc32((const uint32_t *)name, crc32TruncatedInputSize);
// CRC32 of the tail of name
uint32_t tailName = 0;
strlcpy((char *)&tailName, name+crc32TruncatedInputSize*sizeof(uint32_t), sizeof(uint32_t)/sizeof(char));
crc32Results[1] = Ion::crc32(&tailName, 1);
m_nameCRC32 = Ion::crc32(crc32Results, 2);
const char * dotChar = strchr(fullName, k_dotChar);
assert(dotChar != nullptr);
assert(*(dotChar+1) != 0); // Assert there is an extension
new (this) Record(fullName, dotChar - fullName + 1, dotChar+1, (fullName + strlen(fullName)) - (dotChar+1) +1);
}
Storage::Record::Record(const char * baseName, const char * extension) {
if (baseName == nullptr) {
assert(extension == nullptr);
m_fullNameCRC32 = 0;
return;
}
new (this) Record(baseName, strlen(baseName), extension, strlen(extension));
}
void Crc32PaddedString(const char * s, int length, uint32_t * crcStore) {
/* s is a char array. Its length in Bytes is not necessarily a multiple of 4.
* However, crc32 method awaits input in a form of uint32_t table. To limit
* the use of additional memory, we compute 2 crc32:
* - one corresponding to s with a byte length truncated to be a multiple of 4
* - the other corresponds to the remaining chars in s padded with 0 to be 4
* bytes long
* The CRC32 of s is the crc32 of both. */
// CRC32 of the truncated string
size_t crc32TruncatedInputSize = length*sizeof(char)/sizeof(uint32_t);
*crcStore = Ion::crc32((const uint32_t *)s, crc32TruncatedInputSize);
// CRC32 of the tail of the string
uint32_t tailName = 0;
strlcpy((char *)&tailName, s+crc32TruncatedInputSize*sizeof(uint32_t), sizeof(uint32_t)/sizeof(char));
*(crcStore+1) = Ion::crc32(&tailName, 1);
}
Storage::Record::Record(const char * basename, int basenameLength, const char * extension, int extensionLength) {
assert(basename != nullptr);
assert(extension != nullptr);
// We compute the CRC32 of the CRC32s of the basename and the extension
uint32_t crc32Results[4];
Crc32PaddedString(basename, basenameLength, &crc32Results[0]);
Crc32PaddedString(extension, extensionLength, &crc32Results[2]);
m_fullNameCRC32 = Ion::crc32(crc32Results, 4);
}
// STORAGE
Storage::Storage() :
m_magicHeader(Magic),
m_buffer(),
@@ -68,15 +96,12 @@ 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);
Storage::Record::ErrorStatus Storage::createRecordWithExtension(const char * baseName, const char * extension, const void * data, size_t size) {
size_t recordSize = sizeOfRecordWithBaseNameAndExtension(baseName, extension, size);
if (recordSize >= k_maxRecordSize || recordSize > availableSize()) {
return Record::ErrorStatus::NotEnoughSpaceAvailable;
}
if (isNameTaken(name)) {
if (isBaseNameWithExtensionTaken(baseName, extension)) {
return Record::ErrorStatus::NameTaken;
}
// Find the end of data
@@ -84,7 +109,7 @@ Storage::Record::ErrorStatus Storage::createRecord(const char * name, const void
// Fill totalSize
newRecord += overrideSizeAtPosition(newRecord, (record_size_t)recordSize);
// Fill name
newRecord += overrideNameAtPosition(newRecord, name);
newRecord += overrideBaseNameWithExtensionAtPosition(newRecord, baseName, extension);
// Fill data
newRecord += overrideValueAtPosition(newRecord, data, size);
// Next Record is null-sized
@@ -94,10 +119,10 @@ Storage::Record::ErrorStatus Storage::createRecord(const char * name, const void
int Storage::numberOfRecordsWithExtension(const char * extension) {
int count = 0;
size_t extensionLength = strlen(extension);
for (char * p : *this) {
const char * name = nameOfRecordStarting(p);
const char * ext = name+strlen(name)-strlen(extension);
if (strcmp(ext, extension) == 0) {
const char * name = fullNameOfRecordStarting(p);
if (fullNameHasExtension(name, extension, extensionLength)) {
count++;
}
}
@@ -107,10 +132,10 @@ int Storage::numberOfRecordsWithExtension(const char * extension) {
Storage::Record Storage::recordWithExtensionAtIndex(const char * extension, int index) {
int currentIndex = -1;
const char * name = nullptr;
size_t extensionLength = strlen(extension);
for (char * p : *this) {
const char * currentName = nameOfRecordStarting(p);
const char * currentExtension = currentName+strlen(currentName)-strlen(extension);
if (strcmp(currentExtension, extension) == 0) {
const char * currentName = fullNameOfRecordStarting(p);
if (fullNameHasExtension(currentName, extension, extensionLength)) {
currentIndex++;
}
if (currentIndex == index) {
@@ -124,45 +149,87 @@ Storage::Record Storage::recordWithExtensionAtIndex(const char * extension, int
return Record(name);
}
Storage::Record Storage::recordNamed(const char * name) {
Storage::Record Storage::recordNamed(const char * fullName) {
for (char * p : *this) {
const char * currentName = nameOfRecordStarting(p);
if (strcmp(currentName, name) == 0) {
return Record(name);
const char * currentName = fullNameOfRecordStarting(p);
if (strcmp(currentName, fullName) == 0) {
return Record(fullName);
}
}
return Record();
}
const char * Storage::nameOfRecord(const Record record) {
Storage::Record Storage::recordBaseNamedWithExtension(const char * baseName, const char * extension) {
const char * extensions[1] = {extension};
return recordBaseNamedWithExtensions(baseName, extensions, 1);
}
Storage::Record Storage::recordBaseNamedWithExtensions(const char * baseName, const char * extensions[], size_t numberOfExtensions) {
size_t nameLength = strlen(baseName);
for (char * p : *this) {
Record currentRecord(nameOfRecordStarting(p));
const char * currentName = fullNameOfRecordStarting(p);
if (strncmp(currentName, baseName, nameLength) == 0) {
for (size_t i = 0; i < numberOfExtensions; i++) {
if (strcmp(currentName+nameLength+1 /*+1 to pass the dot*/, extensions[i]) == 0) {
return Record(currentName);
}
}
}
}
return Record();
}
const char * Storage::fullNameOfRecord(const Record record) {
for (char * p : *this) {
Record currentRecord(fullNameOfRecordStarting(p));
if (record == currentRecord) {
return nameOfRecordStarting(p);
return fullNameOfRecordStarting(p);
}
}
return nullptr;
}
Storage::Record::ErrorStatus Storage::setNameOfRecord(Record record, const char * name) {
if (!nameCompliant(name)) {
Storage::Record::ErrorStatus Storage::setFullNameOfRecord(const Record record, const char * fullName) {
if (!fullNameCompliant(fullName)) {
return Record::ErrorStatus::NonCompliantName;
}
if (isNameTaken(name, &record)) {
if (isFullNameTaken(fullName, &record)) {
return Record::ErrorStatus::NameTaken;
}
size_t nameSize = strlen(name)+1;
size_t nameSize = strlen(fullName) + 1;
for (char * p : *this) {
Record currentRecord(nameOfRecordStarting(p));
Record currentRecord(fullNameOfRecordStarting(p));
if (record == currentRecord) {
size_t previousNameSize = strlen(nameOfRecordStarting(p))+1;
size_t previousNameSize = strlen(fullNameOfRecordStarting(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::NotEnoughSpaceAvailable;
}
overrideSizeAtPosition(p, newRecordSize);
overrideNameAtPosition(p+sizeof(record_size_t), name);
overrideFullNameAtPosition(p+sizeof(record_size_t), fullName);
return Record::ErrorStatus::None;
}
}
return Record::ErrorStatus::RecordDoesNotExist;
}
Storage::Record::ErrorStatus Storage::setBaseNameWithExtensionOfRecord(Record record, const char * baseName, const char * extension) {
if (isBaseNameWithExtensionTaken(baseName, extension, &record)) {
return Record::ErrorStatus::NameTaken;
}
size_t nameSize = sizeOfBaseNameAndExtension(baseName, extension);
for (char * p : *this) {
Record currentRecord(fullNameOfRecordStarting(p));
if (record == currentRecord) {
size_t previousNameSize = strlen(fullNameOfRecordStarting(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::NotEnoughSpaceAvailable;
}
overrideSizeAtPosition(p, newRecordSize);
overrideBaseNameWithExtensionAtPosition(p+sizeof(record_size_t), baseName, extension);
return Record::ErrorStatus::None;
}
}
@@ -171,12 +238,12 @@ Storage::Record::ErrorStatus Storage::setNameOfRecord(Record record, const char
Storage::Record::Data Storage::valueOfRecord(const Record record) {
for (char * p : *this) {
Record currentRecord(nameOfRecordStarting(p));
Record currentRecord(fullNameOfRecordStarting(p));
if (record == currentRecord) {
const char * name = nameOfRecordStarting(p);
const char * fullName = fullNameOfRecordStarting(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= value, .size= size-strlen(fullName)-1-sizeof(record_size_t)};
}
}
return {.buffer= nullptr, .size= 0};
@@ -184,17 +251,17 @@ Storage::Record::Data Storage::valueOfRecord(const Record record) {
Storage::Record::ErrorStatus Storage::setValueOfRecord(Record record, Record::Data data) {
for (char * p : *this) {
Record currentRecord(nameOfRecordStarting(p));
Record currentRecord(fullNameOfRecordStarting(p));
if (record == currentRecord) {
record_size_t previousRecordSize = sizeOfRecordStarting(p);
const char * name = nameOfRecordStarting(p);
size_t newRecordSize = sizeOfRecord(name, data.size);
const char * fullName = fullNameOfRecordStarting(p);
size_t newRecordSize = sizeOfRecordWithFullName(fullName, data.size);
if (newRecordSize >= k_maxRecordSize || !slideBuffer(p+previousRecordSize, newRecordSize-previousRecordSize)) {
return Record::ErrorStatus::NotEnoughSpaceAvailable;
}
record_size_t nameSize = strlen(name)+1;
record_size_t fullNameSize = strlen(fullName)+1;
overrideSizeAtPosition(p, newRecordSize);
overrideValueAtPosition(p+sizeof(record_size_t)+nameSize, data.buffer, data.size);
overrideValueAtPosition(p+sizeof(record_size_t)+fullNameSize, data.buffer, data.size);
return Record::ErrorStatus::None;
}
}
@@ -203,7 +270,7 @@ Storage::Record::ErrorStatus Storage::setValueOfRecord(Record record, Record::Da
void Storage::destroyRecord(Record record) {
for (char * p : *this) {
Record currentRecord(nameOfRecordStarting(p));
Record currentRecord(fullNameOfRecordStarting(p));
if (record == currentRecord) {
record_size_t previousRecordSize = sizeOfRecordStarting(p);
slideBuffer(p+previousRecordSize, -previousRecordSize);
@@ -235,7 +302,7 @@ Storage::record_size_t Storage::sizeOfRecordStarting(char * start) const {
return unalignedShort(start);
}
const char * Storage::nameOfRecordStarting(char * start) const {
const char * Storage::fullNameOfRecordStarting(char * start) const {
return start+sizeof(record_size_t);
}
@@ -252,8 +319,16 @@ size_t Storage::overrideSizeAtPosition(char * position, record_size_t 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::overrideFullNameAtPosition(char * position, const char * fullName) {
return strlcpy(position, fullName, strlen(fullName)+1) + 1;
}
size_t Storage::overrideBaseNameWithExtensionAtPosition(char * position, const char * baseName, const char * extension) {
size_t result = strlcpy(position, baseName, strlen(baseName));
*(position+result) = k_dotChar;
result++;
result += strlcpy(position+result, extension, strlen(extension)+1);
return result+1;
}
size_t Storage::overrideValueAtPosition(char * position, const void * data, record_size_t size) {
@@ -261,13 +336,16 @@ size_t Storage::overrideValueAtPosition(char * position, const void * data, reco
return size;
}
bool Storage::isNameTaken(const char * name, Record * recordToExclude) {
Record r = Record(name);
bool Storage::isFullNameTaken(const char * fullName, const Record * recordToExclude) {
Record r = Record(fullName);
if (r == Record()) {
/* If the CRC32 of fullName is 0, we want to refuse the name as it would
* interfere with our escape case in the Record contructor, when the given
* name is nullptr. */
return true;
}
for (char * p : *this) {
Record s(nameOfRecordStarting(p));
Record s(fullNameOfRecordStarting(p));
if (recordToExclude && s == *recordToExclude) {
continue;
}
@@ -278,22 +356,27 @@ bool Storage::isNameTaken(const char * name, Record * recordToExclude) {
return false;
}
bool Storage::nameCompliant(const char * name) {
/* The name format is [^.]*\.[a-z]* */
bool dot = false;
const char * currentChar = name;
while (*currentChar != 0) {
if (*currentChar == '.') {
dot = true;
currentChar++;
}
if (!dot || (*currentChar >= 'a' && *currentChar <= 'z')) {
currentChar++;
continue;
}
bool Storage::fullNameCompliant(const char * fullName) {
// We check that there is one dot and one dot only.
const char * dotChar = strchr(fullName, k_dotChar);
if (dotChar == nullptr) {
return false;
}
return name != currentChar && dot;
if (strchr(dotChar, k_dotChar) == nullptr) {
return true;
}
return false;
}
bool Storage::fullNameHasExtension(const char * fullName, const char * extension, size_t extensionLength) {
size_t fullNameLength = strlen(fullName);
if (fullNameLength > extensionLength) {
const char * ext = fullName + fullNameLength - extensionLength;
if (*(ext-1) == k_dotChar && strcmp(ext, extension) == 0) {
return true;
}
}
return false;
}
char * Storage::endBuffer() {
@@ -304,8 +387,17 @@ char * Storage::endBuffer() {
return currentBuffer;
}
size_t Storage::sizeOfRecord(const char * name, size_t dataSize) const {
size_t nameSize = strlen(name)+1;
size_t Storage::sizeOfBaseNameAndExtension(const char * baseName, const char * extension) const {
// +1 for the dot and +1 for the null terminating char
return strlen(baseName)+1+strlen(extension)+1;
}
size_t Storage::sizeOfRecordWithBaseNameAndExtension(const char * baseName, const char * extension, size_t dataSize) const {
return sizeOfBaseNameAndExtension(baseName, extension) + dataSize + sizeof(record_size_t);
}
size_t Storage::sizeOfRecordWithFullName(const char * fullName, size_t dataSize) const {
size_t nameSize = strlen(fullName)+1;
return nameSize+dataSize+sizeof(record_size_t);
}