Various improvements: Toolbox, Updater and Unit Tests. (#2250)

* Toolbox: add seek to character stream method. UpdateUtils: reverse manifest iterator. UnitTests: more unit tests.
* Target: bump API version. Updater: delete empty folders from manifest before resource deployment.
* UnitTests: use manifest from unit_tests folder instead of global one
* Make PVS happy
* sector cache: allocate always
* Better PVS config for manifest.c
* PVS: Move exception outside of condition
* PVS: remove confusing condition

Co-authored-by: SG <who.just.the.doctor@gmail.com>
This commit is contained in:
あく
2023-01-06 15:31:17 +09:00
committed by GitHub
parent b8dd75884c
commit 41c43f4805
11 changed files with 398 additions and 24 deletions

View File

@@ -4,6 +4,8 @@
#include <core/check.h>
#include <core/common_defines.h>
#define STREAM_BUFFER_SIZE (32U)
void stream_free(Stream* stream) {
furi_assert(stream);
stream->vtable->free(stream);
@@ -24,6 +26,82 @@ bool stream_seek(Stream* stream, int32_t offset, StreamOffset offset_type) {
return stream->vtable->seek(stream, offset, offset_type);
}
static bool stream_seek_to_char_forward(Stream* stream, char c) {
// Search is starting from seconds character
if(!stream_seek(stream, 1, StreamOffsetFromCurrent)) {
return false;
}
// Search character in a stream
bool result = false;
while(!result) {
uint8_t buffer[STREAM_BUFFER_SIZE] = {0};
size_t ret = stream_read(stream, buffer, STREAM_BUFFER_SIZE);
for(size_t i = 0; i < ret; i++) {
if(buffer[i] == c) {
stream_seek(stream, (int32_t)i - ret, StreamOffsetFromCurrent);
result = true;
break;
}
}
if(ret != STREAM_BUFFER_SIZE) break;
}
return result;
}
static bool stream_seek_to_char_backward(Stream* stream, char c) {
size_t anchor = stream_tell(stream);
// Special case, no previous characters
if(anchor == 0) {
return false;
}
bool result = false;
while(!result) {
// Seek back
uint8_t buffer[STREAM_BUFFER_SIZE] = {0};
size_t to_read = STREAM_BUFFER_SIZE;
if(to_read > anchor) {
to_read = anchor;
}
anchor -= to_read;
furi_check(stream_seek(stream, anchor, StreamOffsetFromStart));
size_t ret = stream_read(stream, buffer, to_read);
for(size_t i = 0; i < ret; i++) {
size_t cursor = ret - i - 1;
if(buffer[cursor] == c) {
result = true;
furi_check(stream_seek(stream, anchor + cursor, StreamOffsetFromStart));
break;
} else {
}
}
if(ret != STREAM_BUFFER_SIZE) break;
}
return result;
}
bool stream_seek_to_char(Stream* stream, char c, StreamDirection direction) {
const size_t old_position = stream_tell(stream);
bool result = false;
if(direction == StreamDirectionForward) {
result = stream_seek_to_char_forward(stream, c);
} else if(direction == StreamDirectionBackward) {
result = stream_seek_to_char_backward(stream, c);
}
// Rollback
if(!result) {
stream_seek(stream, old_position, StreamOffsetFromStart);
}
return result;
}
size_t stream_tell(Stream* stream) {
furi_assert(stream);
return stream->vtable->tell(stream);
@@ -69,11 +147,10 @@ static bool stream_write_struct(Stream* stream, const void* context) {
bool stream_read_line(Stream* stream, FuriString* str_result) {
furi_string_reset(str_result);
const uint8_t buffer_size = 32;
uint8_t buffer[buffer_size];
uint8_t buffer[STREAM_BUFFER_SIZE];
do {
uint16_t bytes_were_read = stream_read(stream, buffer, buffer_size);
uint16_t bytes_were_read = stream_read(stream, buffer, STREAM_BUFFER_SIZE);
if(bytes_were_read == 0) break;
bool result = false;

View File

@@ -16,6 +16,11 @@ typedef enum {
StreamOffsetFromEnd,
} StreamOffset;
typedef enum {
StreamDirectionForward,
StreamDirectionBackward,
} StreamDirection;
typedef bool (*StreamWriteCB)(Stream* stream, const void* context);
/**
@@ -31,15 +36,15 @@ void stream_free(Stream* stream);
void stream_clean(Stream* stream);
/**
* Indicates that the rw pointer is at the end of the stream
* Indicates that the RW pointer is at the end of the stream
* @param stream Stream instance
* @return true if rw pointer is at the end of the stream
* @return false if rw pointer is not at the end of the stream
* @return true if RW pointer is at the end of the stream
* @return false if RW pointer is not at the end of the stream
*/
bool stream_eof(Stream* stream);
/**
* Moves the rw pointer.
* Moves the RW pointer.
* @param stream Stream instance
* @param offset how much to move the pointer
* @param offset_type starting from what
@@ -48,10 +53,20 @@ bool stream_eof(Stream* stream);
*/
bool stream_seek(Stream* stream, int32_t offset, StreamOffset offset_type);
/** Seek to next occurrence of the character
*
* @param stream Pointer to the stream instance
* @param[in] c The Character
* @param[in] direction The Direction
*
* @return true on success
*/
bool stream_seek_to_char(Stream* stream, char c, StreamDirection direction);
/**
* Gets the value of the rw pointer
* Gets the value of the RW pointer
* @param stream Stream instance
* @return size_t value of the rw pointer
* @return size_t value of the RW pointer
*/
size_t stream_tell(Stream* stream);
@@ -101,13 +116,13 @@ bool stream_delete_and_insert(
* Read line from a stream (supports LF and CRLF line endings)
* @param stream
* @param str_result
* @return true if line lenght is not zero
* @return true if line length is not zero
* @return false otherwise
*/
bool stream_read_line(Stream* stream, FuriString* str_result);
/**
* Moves the rw pointer to the start
* Moves the RW pointer to the start
* @param stream Stream instance
*/
bool stream_rewind(Stream* stream);
@@ -157,7 +172,7 @@ size_t stream_write_vaformat(Stream* stream, const char* format, va_list args);
/**
* Insert N chars to the stream, starting at the current pointer.
* Data will be inserted, not overwritteт, so the stream will be increased in size.
* Data will be inserted, not overwritten, so the stream will be increased in size.
* @param stream Stream instance
* @param data data to be inserted
* @param size size of data to be inserted
@@ -273,7 +288,7 @@ bool stream_delete_and_insert_vaformat(
/**
* Remove N chars from the stream, starting at the current pointer.
* The size may be larger than stream size, the stream will be cleared from current rw pointer to the end.
* The size may be larger than stream size, the stream will be cleared from current RW pointer to the end.
* @param stream Stream instance
* @param size how many chars need to be deleted
* @return true if the operation was successful
@@ -282,7 +297,7 @@ bool stream_delete_and_insert_vaformat(
bool stream_delete(Stream* stream, size_t size);
/**
* Copy data from one stream to another. Data will be copied from current rw pointer and to current rw pointer.
* Copy data from one stream to another. Data will be copied from current RW pointer and to current RW pointer.
* @param stream_from
* @param stream_to
* @param size
@@ -328,7 +343,7 @@ size_t stream_load_from_file(Stream* stream, Storage* storage, const char* path)
size_t stream_save_to_file(Stream* stream, Storage* storage, const char* path, FS_OpenMode mode);
/**
* Dump stream inner data (size, RW positiot, content)
* Dump stream inner data (size, RW position, content)
* @param stream Stream instance
*/
void stream_dump_data(Stream* stream);

View File

@@ -60,6 +60,12 @@ ResourceManifestEntry* resource_manifest_reader_next(ResourceManifestReader* res
char type_code = furi_string_get_char(resource_manifest->linebuf, 0);
switch(type_code) {
case 'V':
resource_manifest->entry.type = ResourceManifestEntryTypeVersion;
break;
case 'T':
resource_manifest->entry.type = ResourceManifestEntryTypeTimestamp;
break;
case 'F':
resource_manifest->entry.type = ResourceManifestEntryTypeFile;
break;
@@ -98,9 +104,9 @@ ResourceManifestEntry* resource_manifest_reader_next(ResourceManifestReader* res
furi_string_right(resource_manifest->linebuf, offs + 1);
furi_string_set(resource_manifest->entry.name, resource_manifest->linebuf);
} else if(resource_manifest->entry.type == ResourceManifestEntryTypeDirectory) { //-V547
/* Parse directory entry
D:<name> */
} else { //-V547
/* Everything else is plain key value. Parse version, timestamp or directory entry
<Type>:<Value> */
/* Remove entry type code */
furi_string_right(resource_manifest->linebuf, 2);
@@ -113,3 +119,45 @@ ResourceManifestEntry* resource_manifest_reader_next(ResourceManifestReader* res
return NULL;
}
ResourceManifestEntry*
resource_manifest_reader_previous(ResourceManifestReader* resource_manifest) {
furi_assert(resource_manifest);
// Snapshot position for rollback
const size_t previous_position = stream_tell(resource_manifest->stream);
// We need to jump 2 lines back
size_t jumps = 2;
// Special case: end of the file.
const bool was_eof = stream_eof(resource_manifest->stream);
if(was_eof) {
jumps = 1;
}
while(jumps) {
if(!stream_seek_to_char(resource_manifest->stream, '\n', StreamDirectionBackward)) {
break;
}
if(stream_tell(resource_manifest->stream) < (previous_position - 1)) {
jumps--;
}
}
// Special case: first line. Force seek to zero
if(jumps == 1) {
jumps = 0;
stream_seek(resource_manifest->stream, 0, StreamOffsetFromStart);
}
if(jumps == 0) {
ResourceManifestEntry* entry = resource_manifest_reader_next(resource_manifest);
// Special case: was end of the file, prevent loop
if(was_eof) {
stream_seek(resource_manifest->stream, -1, StreamOffsetFromCurrent);
}
return entry;
} else {
stream_seek(resource_manifest->stream, previous_position, StreamOffsetFromStart);
return NULL;
}
}

View File

@@ -11,6 +11,8 @@ extern "C" {
typedef enum {
ResourceManifestEntryTypeUnknown = 0,
ResourceManifestEntryTypeVersion,
ResourceManifestEntryTypeTimestamp,
ResourceManifestEntryTypeDirectory,
ResourceManifestEntryTypeFile,
} ResourceManifestEntryType;
@@ -52,6 +54,18 @@ bool resource_manifest_reader_open(ResourceManifestReader* resource_manifest, co
*/
ResourceManifestEntry* resource_manifest_reader_next(ResourceManifestReader* resource_manifest);
/** Read previous file/dir entry from manifest
*
* You must be at the end of the manifest to use this function.
* Intended to be used after reaching end with resource_manifest_reader_next
*
* @param resource_manifest Pointer to the ResourceManifestReader instance
*
* @return entry or NULL if end of file
*/
ResourceManifestEntry*
resource_manifest_reader_previous(ResourceManifestReader* resource_manifest);
#ifdef __cplusplus
} // extern "C"
#endif