Ver código fonte

[FL-2312] Flipper format: insert OR update (#1009)

* Flipper format: seek_to_end, key_exist
* Flipper Format: insert_or_update
SG 3 anos atrás
pai
commit
c42cce3c6c

+ 110 - 0
applications/tests/flipper_format/flipper_format_string_test.c

@@ -11,22 +11,30 @@ static const uint32_t test_version = 666;
 static const char* test_string_key = "String data";
 static const char* test_string_data = "String";
 static const char* test_string_updated_data = "New string";
+static const char* test_string_updated_2_data = "And some more";
 
 static const char* test_int_key = "Int32 data";
 static const int32_t test_int_data[] = {1234, -6345, 7813, 0};
 static const int32_t test_int_updated_data[] = {-1337, 69};
+static const int32_t test_int_updated_2_data[] = {-3, -2, -1, 0, 1, 2, 3};
 
 static const char* test_uint_key = "Uint32 data";
 static const uint32_t test_uint_data[] = {1234, 0, 5678, 9098, 7654321};
 static const uint32_t test_uint_updated_data[] = {8, 800, 555, 35, 35};
+static const uint32_t test_uint_updated_2_data[] = {20, 21};
 
 static const char* test_float_key = "Float data";
 static const float test_float_data[] = {1.5f, 1000.0f};
 static const float test_float_updated_data[] = {1.2f};
+static const float test_float_updated_2_data[] = {0.01f, 0.0f, -51.6f};
 
 static const char* test_hex_key = "Hex data";
 static const uint8_t test_hex_data[] = {0xDE, 0xAD, 0xBE};
 static const uint8_t test_hex_updated_data[] = {0xFE, 0xCA};
+static const uint8_t test_hex_updated_2_data[] = {0xCA, 0xCA, 0x05};
+
+static const char* test_hex_new_key = "New Hex data";
+static const uint8_t test_hex_new_data[] = {0xFF, 0x6A, 0x91};
 
 static const char* test_data_nix = "Filetype: Flipper Format test\n"
                                    "Version: 666\n"
@@ -59,7 +67,40 @@ MU_TEST_1(flipper_format_read_and_update_test, FlipperFormat* flipper_format) {
 
     uint32_t count;
 
+    // key exist test
+    size_t position_before = stream_tell(flipper_format_get_raw_stream(flipper_format));
+    mu_check(flipper_format_key_exist(flipper_format, test_hex_key));
+    mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format)));
+
+    mu_check(!flipper_format_key_exist(flipper_format, "invalid key"));
+    mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format)));
+
+    // stream seek to end test
+    mu_check(flipper_format_seek_to_end(flipper_format));
+    mu_assert_int_eq(
+        stream_size(flipper_format_get_raw_stream(flipper_format)),
+        stream_tell(flipper_format_get_raw_stream(flipper_format)));
+
+    // key exist test
+    position_before = stream_tell(flipper_format_get_raw_stream(flipper_format));
+    mu_check(flipper_format_key_exist(flipper_format, test_hex_key));
+    mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format)));
+
+    mu_check(!flipper_format_key_exist(flipper_format, "invalid key"));
+    mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format)));
+
+    // rewind
     mu_check(flipper_format_rewind(flipper_format));
+
+    // key exist test
+    position_before = stream_tell(flipper_format_get_raw_stream(flipper_format));
+    mu_check(flipper_format_key_exist(flipper_format, test_hex_key));
+    mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format)));
+
+    mu_check(!flipper_format_key_exist(flipper_format, "invalid key"));
+    mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format)));
+
+    // read test
     string_init(tmpstr);
 
     mu_check(flipper_format_read_header(flipper_format, tmpstr, &version));
@@ -94,6 +135,7 @@ MU_TEST_1(flipper_format_read_and_update_test, FlipperFormat* flipper_format) {
 
     string_clear(tmpstr);
 
+    // update data
     mu_check(flipper_format_rewind(flipper_format));
     mu_check(flipper_format_update_string_cstr(
         flipper_format, test_string_key, test_string_updated_data));
@@ -106,6 +148,7 @@ MU_TEST_1(flipper_format_read_and_update_test, FlipperFormat* flipper_format) {
     mu_check(flipper_format_update_hex(
         flipper_format, test_hex_key, ARRAY_W_COUNT(test_hex_updated_data)));
 
+    // read updated data test
     uint32_t uint32_updated_data[COUNT_OF(test_uint_updated_data)];
     int32_t int32_updated_data[COUNT_OF(test_int_updated_data)];
     float float_updated_data[COUNT_OF(test_float_updated_data)];
@@ -149,9 +192,76 @@ MU_TEST_1(flipper_format_read_and_update_test, FlipperFormat* flipper_format) {
 
     string_clear(tmpstr);
 
+    // update data
+    mu_check(flipper_format_rewind(flipper_format));
+    mu_check(flipper_format_insert_or_update_string_cstr(
+        flipper_format, test_string_key, test_string_updated_2_data));
+    mu_check(flipper_format_insert_or_update_int32(
+        flipper_format, test_int_key, ARRAY_W_COUNT(test_int_updated_2_data)));
+    mu_check(flipper_format_insert_or_update_uint32(
+        flipper_format, test_uint_key, ARRAY_W_COUNT(test_uint_updated_2_data)));
+    mu_check(flipper_format_insert_or_update_float(
+        flipper_format, test_float_key, ARRAY_W_COUNT(test_float_updated_2_data)));
+    mu_check(flipper_format_insert_or_update_hex(
+        flipper_format, test_hex_key, ARRAY_W_COUNT(test_hex_updated_2_data)));
+    mu_check(flipper_format_insert_or_update_hex(
+        flipper_format, test_hex_new_key, ARRAY_W_COUNT(test_hex_new_data)));
+
+    uint32_t uint32_updated_2_data[COUNT_OF(test_uint_updated_2_data)];
+    int32_t int32_updated_2_data[COUNT_OF(test_int_updated_2_data)];
+    float float_updated_2_data[COUNT_OF(test_float_updated_2_data)];
+    uint8_t hex_updated_2_data[COUNT_OF(test_hex_updated_2_data)];
+    uint8_t hex_new_data[COUNT_OF(test_hex_new_data)];
+
+    mu_check(flipper_format_rewind(flipper_format));
+    string_init(tmpstr);
+
+    mu_check(flipper_format_read_header(flipper_format, tmpstr, &version));
+    mu_assert_string_eq(test_filetype, string_get_cstr(tmpstr));
+    mu_assert_int_eq(test_version, version);
+
+    mu_check(flipper_format_read_string(flipper_format, test_string_key, tmpstr));
+    mu_assert_string_eq(test_string_updated_2_data, string_get_cstr(tmpstr));
+
+    mu_check(flipper_format_get_value_count(flipper_format, test_int_key, &count));
+    mu_assert_int_eq(COUNT_OF(test_int_updated_2_data), count);
+    mu_check(flipper_format_read_int32(
+        flipper_format, test_int_key, ARRAY_W_COUNT(int32_updated_2_data)));
+    mu_check(memcmp(test_int_updated_2_data, ARRAY_W_BSIZE(int32_updated_2_data)) == 0);
+
+    mu_check(flipper_format_get_value_count(flipper_format, test_uint_key, &count));
+    mu_assert_int_eq(COUNT_OF(test_uint_updated_2_data), count);
+    mu_check(flipper_format_read_uint32(
+        flipper_format, test_uint_key, ARRAY_W_COUNT(uint32_updated_2_data)));
+    mu_check(memcmp(test_uint_updated_2_data, ARRAY_W_BSIZE(uint32_updated_2_data)) == 0);
+
+    mu_check(flipper_format_get_value_count(flipper_format, test_float_key, &count));
+    mu_assert_int_eq(COUNT_OF(test_float_updated_2_data), count);
+    mu_check(flipper_format_read_float(
+        flipper_format, test_float_key, ARRAY_W_COUNT(float_updated_2_data)));
+    mu_check(memcmp(test_float_updated_2_data, ARRAY_W_BSIZE(float_updated_2_data)) == 0);
+
+    mu_check(flipper_format_get_value_count(flipper_format, test_hex_key, &count));
+    mu_assert_int_eq(COUNT_OF(test_hex_updated_2_data), count);
+    mu_check(
+        flipper_format_read_hex(flipper_format, test_hex_key, ARRAY_W_COUNT(hex_updated_2_data)));
+    mu_check(memcmp(test_hex_updated_2_data, ARRAY_W_BSIZE(hex_updated_2_data)) == 0);
+
+    mu_check(flipper_format_get_value_count(flipper_format, test_hex_new_key, &count));
+    mu_assert_int_eq(COUNT_OF(test_hex_new_data), count);
+    mu_check(
+        flipper_format_read_hex(flipper_format, test_hex_new_key, ARRAY_W_COUNT(hex_new_data)));
+    mu_check(memcmp(test_hex_new_data, ARRAY_W_BSIZE(hex_new_data)) == 0);
+
+    mu_check(!flipper_format_read_string(flipper_format, "Key that doesn't exist", tmpstr));
+
+    string_clear(tmpstr);
+
+    // delete key test
     mu_check(flipper_format_rewind(flipper_format));
     mu_check(flipper_format_delete_key(flipper_format, test_uint_key));
 
+    // deleted key read test
     mu_check(flipper_format_rewind(flipper_format));
     mu_check(!flipper_format_read_uint32(
         flipper_format, test_uint_key, ARRAY_W_COUNT(uint32_updated_data)));

+ 115 - 1
lib/flipper_format/flipper_format.c

@@ -102,6 +102,20 @@ bool flipper_format_rewind(FlipperFormat* flipper_format) {
     return stream_rewind(flipper_format->stream);
 }
 
+bool flipper_format_seek_to_end(FlipperFormat* flipper_format) {
+    furi_assert(flipper_format);
+    return stream_seek(flipper_format->stream, 0, StreamOffsetFromEnd);
+}
+
+bool flipper_format_key_exist(FlipperFormat* flipper_format, const char* key) {
+    size_t pos = stream_tell(flipper_format->stream);
+    stream_seek(flipper_format->stream, 0, StreamOffsetFromStart);
+    bool result = flipper_format_stream_seek_to_key(flipper_format->stream, key, false);
+    stream_seek(flipper_format->stream, pos, StreamOffsetFromStart);
+
+    return result;
+}
+
 bool flipper_format_read_header(
     FlipperFormat* flipper_format,
     string_t filetype,
@@ -320,7 +334,7 @@ bool flipper_format_update_string(FlipperFormat* flipper_format, const char* key
     FlipperStreamWriteData write_data = {
         .key = key,
         .type = FlipperStreamValueStr,
-        .data = data,
+        .data = string_get_cstr(data),
         .data_size = 1,
     };
     bool result = flipper_format_stream_delete_key_and_write(
@@ -408,3 +422,103 @@ bool flipper_format_update_hex(
         flipper_format->stream, &write_data, flipper_format->strict_mode);
     return result;
 }
+
+bool flipper_format_insert_or_update_string(
+    FlipperFormat* flipper_format,
+    const char* key,
+    string_t data) {
+    bool result = false;
+
+    if(!flipper_format_key_exist(flipper_format, key)) {
+        flipper_format_seek_to_end(flipper_format);
+        result = flipper_format_write_string(flipper_format, key, data);
+    } else {
+        result = flipper_format_update_string(flipper_format, key, data);
+    }
+
+    return result;
+}
+
+bool flipper_format_insert_or_update_string_cstr(
+    FlipperFormat* flipper_format,
+    const char* key,
+    const char* data) {
+    bool result = false;
+
+    if(!flipper_format_key_exist(flipper_format, key)) {
+        flipper_format_seek_to_end(flipper_format);
+        result = flipper_format_write_string_cstr(flipper_format, key, data);
+    } else {
+        result = flipper_format_update_string_cstr(flipper_format, key, data);
+    }
+
+    return result;
+}
+
+bool flipper_format_insert_or_update_uint32(
+    FlipperFormat* flipper_format,
+    const char* key,
+    const uint32_t* data,
+    const uint16_t data_size) {
+    bool result = false;
+
+    if(!flipper_format_key_exist(flipper_format, key)) {
+        flipper_format_seek_to_end(flipper_format);
+        result = flipper_format_write_uint32(flipper_format, key, data, data_size);
+    } else {
+        result = flipper_format_update_uint32(flipper_format, key, data, data_size);
+    }
+
+    return result;
+}
+
+bool flipper_format_insert_or_update_int32(
+    FlipperFormat* flipper_format,
+    const char* key,
+    const int32_t* data,
+    const uint16_t data_size) {
+    bool result = false;
+
+    if(!flipper_format_key_exist(flipper_format, key)) {
+        flipper_format_seek_to_end(flipper_format);
+        result = flipper_format_write_int32(flipper_format, key, data, data_size);
+    } else {
+        result = flipper_format_update_int32(flipper_format, key, data, data_size);
+    }
+
+    return result;
+}
+
+bool flipper_format_insert_or_update_float(
+    FlipperFormat* flipper_format,
+    const char* key,
+    const float* data,
+    const uint16_t data_size) {
+    bool result = false;
+
+    if(!flipper_format_key_exist(flipper_format, key)) {
+        flipper_format_seek_to_end(flipper_format);
+        result = flipper_format_write_float(flipper_format, key, data, data_size);
+    } else {
+        result = flipper_format_update_float(flipper_format, key, data, data_size);
+    }
+
+    return result;
+}
+
+bool flipper_format_insert_or_update_hex(
+    FlipperFormat* flipper_format,
+    const char* key,
+    const uint8_t* data,
+    const uint16_t data_size) {
+    bool result = false;
+
+    if(!flipper_format_key_exist(flipper_format, key)) {
+        flipper_format_seek_to_end(flipper_format);
+        result = flipper_format_write_hex(flipper_format, key, data, data_size);
+    } else {
+        result = flipper_format_update_hex(flipper_format, key, data, data_size);
+    }
+
+    return result;
+}

+ 99 - 0
lib/flipper_format/flipper_format.h

@@ -179,6 +179,22 @@ void flipper_format_set_strict_mode(FlipperFormat* flipper_format, bool strict_m
  */
 bool flipper_format_rewind(FlipperFormat* flipper_format);
 
+/**
+ * Move the RW pointer at the end. Can be useful if you want to add some data after reading.
+ * @param flipper_format Pointer to a FlipperFormat instance
+ * @return True on success
+ */
+bool flipper_format_seek_to_end(FlipperFormat* flipper_format);
+
+/**
+ * Check if the key exists.
+ * @param flipper_format Pointer to a FlipperFormat instance
+ * @param key Key
+ * @return true key exists
+ * @return false key is not exists
+ */
+bool flipper_format_key_exist(FlipperFormat* flipper_format, const char* key);
+
 /**
  * Read the header (file type and version).
  * @param flipper_format Pointer to a FlipperFormat instance
@@ -466,6 +482,89 @@ bool flipper_format_update_hex(
     const uint8_t* data,
     const uint16_t data_size);
 
+/**
+ * Updates the value of the first matching key to a string value, or adds the key and value if the key did not exist. 
+ * Sets the RW pointer to a position at the end of inserted data.
+ * @param flipper_format Pointer to a FlipperFormat instance 
+ * @param key Key
+ * @param data Value
+ * @return True on success
+ */
+bool flipper_format_insert_or_update_string(
+    FlipperFormat* flipper_format,
+    const char* key,
+    string_t data);
+
+/**
+ * Updates the value of the first matching key to a string value, or adds the key and value if the key did not exist.  
+ * Plain C version. 
+ * Sets the RW pointer to a position at the end of inserted data.
+ * @param flipper_format Pointer to a FlipperFormat instance 
+ * @param key Key
+ * @param data Value
+ * @return True on success
+ */
+bool flipper_format_insert_or_update_string_cstr(
+    FlipperFormat* flipper_format,
+    const char* key,
+    const char* data);
+
+/**
+ * Updates the value of the first matching key to a uint32 array value, or adds the key and value if the key did not exist. 
+ *  Sets the RW pointer to a position at the end of inserted data.
+ * @param flipper_format Pointer to a FlipperFormat instance 
+ * @param key Key
+ * @param data Value
+ * @return True on success
+ */
+bool flipper_format_insert_or_update_uint32(
+    FlipperFormat* flipper_format,
+    const char* key,
+    const uint32_t* data,
+    const uint16_t data_size);
+
+/**
+ * Updates the value of the first matching key to a int32 array value, or adds the key and value if the key did not exist. 
+ * Sets the RW pointer to a position at the end of inserted data.
+ * @param flipper_format Pointer to a FlipperFormat instance 
+ * @param key Key
+ * @param data Value
+ * @return True on success
+ */
+bool flipper_format_insert_or_update_int32(
+    FlipperFormat* flipper_format,
+    const char* key,
+    const int32_t* data,
+    const uint16_t data_size);
+
+/**
+ * Updates the value of the first matching key to a float array value, or adds the key and value if the key did not exist. 
+ * Sets the RW pointer to a position at the end of inserted data.
+ * @param flipper_format Pointer to a FlipperFormat instance 
+ * @param key Key
+ * @param data Value
+ * @return True on success
+ */
+bool flipper_format_insert_or_update_float(
+    FlipperFormat* flipper_format,
+    const char* key,
+    const float* data,
+    const uint16_t data_size);
+
+/**
+ * Updates the value of the first matching key to an array of hex-formatted bytes, or adds the key and value if the key did not exist.  
+ *Sets the RW pointer to a position at the end of inserted data.
+ * @param flipper_format Pointer to a FlipperFormat instance 
+ * @param key Key
+ * @param data Value
+ * @return True on success
+ */
+bool flipper_format_insert_or_update_hex(
+    FlipperFormat* flipper_format,
+    const char* key,
+    const uint8_t* data,
+    const uint16_t data_size);
+
 #ifdef __cplusplus
 }
 #endif

+ 1 - 1
lib/flipper_format/flipper_format_stream.c

@@ -93,7 +93,7 @@ static bool flipper_format_stream_read_valid_key(Stream* stream, string_t key) {
     return found;
 }
 
-static bool flipper_format_stream_seek_to_key(Stream* stream, const char* key, bool strict_mode) {
+bool flipper_format_stream_seek_to_key(Stream* stream, const char* key, bool strict_mode) {
     bool found = false;
     string_t read_key;
 

+ 11 - 0
lib/flipper_format/flipper_format_stream_i.h

@@ -18,6 +18,17 @@ extern "C" {
  */
 bool flipper_format_stream_write_eol(Stream* stream);
 
+/**
+ * Seek to the key from the current position of the stream.
+ * Position will be at the beginning of the value corresponding to the key, if the key is found,, or at the end of the stream.
+ * @param stream 
+ * @param key 
+ * @param strict_mode 
+ * @return true key is found
+ * @return false key is not found
+ */
+bool flipper_format_stream_seek_to_key(Stream* stream, const char* key, bool strict_mode);
+
 #ifdef __cplusplus
 }
 #endif