Numworks Epsilon  1.4.1
Graphing Calculator Operating System
storage.cpp
Go to the documentation of this file.
1 #include <ion.h>
2 #include <string.h>
3 #include <assert.h>
4 #include <new>
5 
6 namespace Ion {
7 
8 /* We want to implement a simple singleton pattern, to make sure the storage is
9  * initialized on first use, therefore preventing the static init order fiasco.
10  * That being said, we rely on knowing where the storage resides in the device's
11  * memory at compile time. Indeed, we want to advertise the static storage's
12  * memory address in the PlatformInfo structure (so that we can read and write
13  * it in DFU).
14  * Using a "static Storage storage;" variable makes it a local symbol at best,
15  * preventing the PlatformInfo from retrieving its address. And making the
16  * Storage variable global yields the static init fiasco issue. We're working
17  * around both issues by creating a global staticStorageArea buffer, and by
18  * placement-newing the Storage into that area on first use. */
19 
20 uint32_t staticStorageArea[sizeof(Storage)/sizeof(uint32_t)] = {0};
21 
23  static Storage * storage = nullptr;
24  if (storage == nullptr) {
25  storage = new (staticStorageArea) Storage();
26  }
27  return storage;
28 }
29 
30 Storage::Record::Record(const char * name) {
31  if (name == nullptr) {
32  m_nameCRC32 = 0;
33  return;
34  }
35  size_t lenght = strlen(name);
36  size_t crc32InputSize = lenght*sizeof(char)/sizeof(uint32_t)+1;
37  uint32_t * crc32Input = new uint32_t[crc32InputSize];
38  memset(crc32Input, 0, crc32InputSize*sizeof(uint32_t));
39  strlcpy((char *)crc32Input, name, lenght+1);
40  assert((crc32InputSize*sizeof(uint32_t) & 0x3) == 0); // Assert that dataLengthInBytes is a multiple of 4
41  m_nameCRC32 = Ion::crc32(crc32Input, crc32InputSize);
42  delete[] crc32Input;
43 }
44 
46  m_magicHeader(Magic),
47  m_buffer(),
48  m_magicFooter(Magic)
49 {
50  assert(m_magicHeader == Magic);
51  assert(m_magicFooter == Magic);
52  // Set the size of the first record to 0
53  overrideSizeAtPosition(m_buffer, 0);
54 }
55 
57  return k_storageSize-(endBuffer()-m_buffer)-sizeof(record_size_t);
58 }
59 
60 Storage::Record::ErrorStatus Storage::createRecord(const char * name, const void * data, size_t size) {
61  if (!nameCompliant(name)) {
63  }
64  size_t recordSize = sizeOfRecord(name, size);
65  if (recordSize >= k_maxRecordSize || recordSize > availableSize()) {
67  }
68  if (isNameTaken(name)) {
70  }
71  // Find the end of data
72  char * newRecord = endBuffer();
73  // Fill totalSize
74  newRecord += overrideSizeAtPosition(newRecord, (record_size_t)recordSize);
75  // Fill name
76  newRecord += overrideNameAtPosition(newRecord, name);
77  // Fill data
78  newRecord += overrideValueAtPosition(newRecord, data, size);
79  // Next Record is null-sized
80  overrideSizeAtPosition(newRecord, 0);
82 }
83 
84 int Storage::numberOfRecordsWithExtension(const char * extension) {
85  int count = 0;
86  for (char * p : *this) {
87  const char * name = nameOfRecordStarting(p);
88  const char * ext = name+strlen(name)-strlen(extension);
89  if (strcmp(ext, extension) == 0) {
90  count++;
91  }
92  }
93  return count;
94 }
95 
96 Storage::Record Storage::recordWithExtensionAtIndex(const char * extension, int index) {
97  int currentIndex = -1;
98  const char * name = nullptr;
99  for (char * p : *this) {
100  const char * currentName = nameOfRecordStarting(p);
101  const char * currentExtension = currentName+strlen(currentName)-strlen(extension);
102  if (strcmp(currentExtension, extension) == 0) {
103  currentIndex++;
104  }
105  if (currentIndex == index) {
106  name = currentName;
107  break;
108  }
109  }
110  if (name == nullptr) {
111  return Record();
112  }
113  return Record(name);
114 }
115 
117  for (char * p : *this) {
118  const char * currentName = nameOfRecordStarting(p);
119  if (strcmp(currentName, name) == 0) {
120  return Record(name);
121  }
122  }
123  return Record();
124 }
125 
126 const char * Storage::nameOfRecord(const Record record) {
127  for (char * p : *this) {
128  Record currentRecord(nameOfRecordStarting(p));
129  if (record == currentRecord) {
130  return nameOfRecordStarting(p);
131  }
132  }
133  return nullptr;
134 }
135 
136 Storage::Record::ErrorStatus Storage::setNameOfRecord(Record record, const char * name) {
137  if (!nameCompliant(name)) {
139  }
140  if (isNameTaken(name, &record)) {
142  }
143  size_t nameSize = strlen(name)+1;
144  for (char * p : *this) {
145  Record currentRecord(nameOfRecordStarting(p));
146  if (record == currentRecord) {
147  size_t previousNameSize = strlen(nameOfRecordStarting(p))+1;
148  record_size_t previousRecordSize = sizeOfRecordStarting(p);
149  size_t newRecordSize = previousRecordSize-previousNameSize+nameSize;
150  if (newRecordSize >= k_maxRecordSize || !slideBuffer(p+sizeof(record_size_t)+previousNameSize, nameSize-previousNameSize)) {
152  }
153  overrideSizeAtPosition(p, newRecordSize);
154  overrideNameAtPosition(p+sizeof(record_size_t), name);
156  }
157  }
159 }
160 
161 Storage::Record::Data Storage::valueOfRecord(const Record record) {
162  for (char * p : *this) {
163  Record currentRecord(nameOfRecordStarting(p));
164  if (record == currentRecord) {
165  const char * name = nameOfRecordStarting(p);
166  record_size_t size = sizeOfRecordStarting(p);
167  const void * value = valueOfRecordStarting(p);
168  return {.buffer= value, .size= size-strlen(name)-1-sizeof(record_size_t)};
169  }
170  }
171  return {.buffer= nullptr, .size= 0};
172 }
173 
174 Storage::Record::ErrorStatus Storage::setValueOfRecord(Record record, Record::Data data) {
175  for (char * p : *this) {
176  Record currentRecord(nameOfRecordStarting(p));
177  if (record == currentRecord) {
178  record_size_t previousRecordSize = sizeOfRecordStarting(p);
179  const char * name = nameOfRecordStarting(p);
180  size_t newRecordSize = sizeOfRecord(name, data.size);
181  if (newRecordSize >= k_maxRecordSize || !slideBuffer(p+previousRecordSize, newRecordSize-previousRecordSize)) {
183  }
184  record_size_t nameSize = strlen(name)+1;
185  overrideSizeAtPosition(p, newRecordSize);
186  overrideValueAtPosition(p+sizeof(record_size_t)+nameSize, data.buffer, data.size);
188  }
189  }
191 }
192 
193 void Storage::destroyRecord(Record record) {
194  for (char * p : *this) {
195  Record currentRecord(nameOfRecordStarting(p));
196  if (record == currentRecord) {
197  record_size_t previousRecordSize = sizeOfRecordStarting(p);
198  slideBuffer(p+previousRecordSize, -previousRecordSize);
199  }
200  }
201 }
202 
203 static inline uint16_t unalignedShort(char * address) {
204 #if __EMSCRIPTEN__
205  uint8_t f1 = *(address);
206  uint8_t f2 = *(address+1);
207  uint16_t f = (uint16_t)f1 + (((uint16_t)f2)<<8);
208  return f;
209 #else
210  return *(uint16_t *)address;
211 #endif
212 }
213 
214 static inline void writeUnalignedShort(uint16_t value, char * address) {
215 #if __EMSCRIPTEN__
216  *((uint8_t *)address) = (uint8_t)(value & ((1 << 8) - 1));
217  *((uint8_t *)address+1) = (uint8_t)(value >> 8);
218 #else
219  *((uint16_t *)address) = value;
220 #endif
221 }
222 
223 Storage::record_size_t Storage::sizeOfRecordStarting(char * start) const {
224  return unalignedShort(start);
225 }
226 
227 const char * Storage::nameOfRecordStarting(char * start) const {
228  return start+sizeof(record_size_t);
229 }
230 
231 const void * Storage::valueOfRecordStarting(char * start) const {
232  char * currentChar = start+sizeof(record_size_t);
233  while (*currentChar != 0) {
234  currentChar++;
235  }
236  return currentChar+1;
237 }
238 
239 size_t Storage::overrideSizeAtPosition(char * position, record_size_t size) {
240  writeUnalignedShort(size, position);
241  return sizeof(record_size_t);
242 }
243 
244 size_t Storage::overrideNameAtPosition(char * position, const char * name) {
245  return strlcpy(position, name, strlen(name)+1)+1;
246 }
247 
248 size_t Storage::overrideValueAtPosition(char * position, const void * data, record_size_t size) {
249  memcpy(position, data, size);
250  return size;
251 }
252 
253 bool Storage::isNameTaken(const char * name, Record * recordToExclude) {
254  Record r = Record(name);
255  if (r == Record()) {
256  return true;
257  }
258  for (char * p : *this) {
259  Record s(nameOfRecordStarting(p));
260  if (recordToExclude && s == *recordToExclude) {
261  continue;
262  }
263  if (s == r) {
264  return true;
265  }
266  }
267  return false;
268 }
269 
270 bool Storage::nameCompliant(const char * name) const {
271  /* The name format is [a-z0-9_]*(\.)?[a-z0-9_]+ */
272  bool dot = false;
273  const char * currentChar = name;
274  while (*currentChar != 0) {
275  if (*currentChar == '.') {
276  if (dot) {
277  return false;
278  } else {
279  dot = true;
280  }
281  }
282  if ((*currentChar >= 'a' && *currentChar <= 'z') || *currentChar == '_' || (*currentChar >= '0' && *currentChar <= '9') || *currentChar == '.') {
283  currentChar++;
284  continue;
285  }
286  return false;
287  }
288  return name != currentChar;
289 }
290 
291 char * Storage::endBuffer() {
292  char * currentBuffer = m_buffer;
293  for (char * p : *this) {
294  currentBuffer += sizeOfRecordStarting(p);
295  }
296  return currentBuffer;
297 }
298 
299 size_t Storage::sizeOfRecord(const char * name, size_t dataSize) const {
300  size_t nameSize = strlen(name)+1;
301  return nameSize+dataSize+sizeof(record_size_t);
302 }
303 
304 bool Storage::slideBuffer(char * position, int delta) {
305  if (delta > (int)availableSize()) {
306  return false;
307  }
308  memmove(position+delta, position, endBuffer()+sizeof(record_size_t)-position);
309  return true;
310 }
311 
312 Storage::RecordIterator & Storage::RecordIterator::operator++() {
313  record_size_t size = unalignedShort(m_recordStart);
314  char * nextRecord = m_recordStart+size;
315  record_size_t newRecordSize = unalignedShort(nextRecord);
316  m_recordStart = (newRecordSize == 0 ? nullptr : nextRecord);
317  return *this;
318 }
319 
320 }
Record recordWithExtensionAtIndex(const char *extension, int index)
Definition: storage.cpp:96
void * memset(void *b, int c, size_t len)
Definition: memset.c:3
char staticStorageArea[]
Definition: storage.cpp:20
#define assert(e)
Definition: assert.h:9
def data
Definition: i18n.py:176
Record::ErrorStatus createRecord(const char *name, const void *data, size_t size)
Definition: storage.cpp:60
size_t strlcpy(char *dst, const char *src, size_t len)
Definition: strlcpy.c:3
unsigned short uint16_t
Definition: stdint.h:5
unsigned char uint8_t
Definition: stdint.h:4
int numberOfRecordsWithExtension(const char *extension)
Definition: storage.cpp:84
Record recordNamed(const char *name)
Definition: storage.cpp:116
static Storage * sharedStorage()
Definition: storage.cpp:22
size_t strlen(const char *s)
Definition: strlen.c:3
unsigned int uint32_t
Definition: stdint.h:6
void * memmove(void *dst, const void *src, size_t n)
Definition: memmove.c:3
Record(const char *name=nullptr)
Definition: storage.cpp:30
const char * name() const
Definition: storage.h:41
void start()
Definition: rt0.cpp:31
uint32_t crc32(const uint32_t *data, size_t length)
Definition: device.cpp:37
int strcmp(const char *s1, const char *s2)
Definition: strcmp.c:3
size_t availableSize()
Definition: storage.cpp:56
Definition: backlight.h:6
uint16_t record_size_t
Definition: storage.h:65
void * memcpy(void *dst, const void *src, size_t n)
Definition: memcpy.c:3