cembedded

How can embedded systems be elegantly compatible with 32-bit and 64-bit timestamp content printing?


This project github url: https://github.com/armink/FlashDB

#ifdef FDB_USING_TIMESTAMP_64BIT
    typedef int64_t fdb_time_t;
#else
    typedef int32_t fdb_time_t;
#endif /* FDB_USING_TIMESTAMP_64BIT */


static fdb_err_t tsl_append(fdb_tsdb_t db, fdb_blob_t blob, fdb_time_t *timestamp)
{
    fdb_err_t result = FDB_NO_ERR;
    fdb_time_t cur_time = timestamp == NULL ? db->get_time() : *timestamp;

    /* check the append length, MUST less than the db->max_len */
    if(blob->size > db->max_len)
    {
        FDB_INFO("Warning: append length (%" PRIdMAX ") is more than the db->max_len (%" PRIdMAX "). This tsl will be dropped.\r\n", 
                (intmax_t)blob->size, (intmax_t)(db->max_len));
        return FDB_WRITE_ERR;
    }

    /* check the current timestamp, MUST more than the last save timestamp */
    if (cur_time <= db->last_time) {
        FDB_INFO("Warning: current timestamp (%" PRIdMAX ") is less than or equal to the last save timestamp (%" PRIdMAX "). This tsl will be dropped.\r\n",
                (intmax_t )cur_time, (intmax_t )(db->last_time));
        return FDB_WRITE_ERR;
    }

    result = update_sec_status(db, &db->cur_sec, blob, cur_time);
    if (result != FDB_NO_ERR) {
        FDB_INFO("Error: update the sector status failed (%d)", result);
        return result;
    }
    /* write the TSL node */
    result = write_tsl(db, blob, cur_time);
    if (result != FDB_NO_ERR) {
        FDB_INFO("Error: write tsl failed (%d)", result);
        return result;
    }

    /* recalculate the current using sector info */
    db->cur_sec.end_idx = db->cur_sec.empty_idx;
    db->cur_sec.end_time = cur_time;
    db->cur_sec.empty_idx += LOG_IDX_DATA_SIZE;
    db->cur_sec.empty_data -= FDB_WG_ALIGN(blob->size);
    db->cur_sec.remain -= LOG_IDX_DATA_SIZE + FDB_WG_ALIGN(blob->size);
    db->last_time = cur_time;

    return result;
}

If FDB_USING_TIMESTAMP_64BIT is not defined and when CUR_TIME <= DB-> Last_time, it will increase the 32-bit type to 64-bit due to the conversion of compulsory types, resulting in inconsistent printing and expectations.

if (cur_time <= db->last_time) {
    FDB_INFO("Warning: current timestamp (%" PRIdMAX ") is less than or equal to the last save timestamp (%" PRIdMAX "). This tsl will be dropped.\r\n",
            (intmax_t )cur_time, (intmax_t )(db->last_time));
    return FDB_WRITE_ERR;
}

question:

  1. I found that there is no good way to be compatible with 32-bit and 64-bit printing. Except write an if else!.
  2. The type is forced to be converted in this code. Where does the extra 32-bit content come from? Will there be other bugs caused by unexpected memory operations here?

I tested on the STM32 system and printed correct and incorrect examples at once:

static fdb_err_t tsl_append(fdb_tsdb_t db, fdb_blob_t blob, fdb_time_t *timestamp)
{
    fdb_err_t result = FDB_NO_ERR;
    fdb_time_t cur_time = timestamp == NULL ? db->get_time() : *timestamp;

    /* check the append length, MUST less than the db->max_len */
    if(blob->size > db->max_len)
    {
        FDB_INFO("Warning: (user PRIdMAX) append length (%" PRIdMAX ") is more than the db->max_len (%" PRIdMAX "). This tsl will be dropped.\r\n", 
                (intmax_t)blob->size, (intmax_t)(db->max_len));

        FDB_INFO("Warning: (user PRId32) append length (%" PRId32 ") is more than the db->max_len (%" PRId32 "). This tsl will be dropped.\r\n", 
                blob->size, (db->max_len));

        return FDB_WRITE_ERR;
    }

    /* check the current timestamp, MUST more than the last save timestamp */
    if (cur_time <= db->last_time) {
        FDB_INFO("Warning: current timestamp (%" PRIdMAX ") is less than or equal to the last save timestamp (%" PRIdMAX "). This tsl will be dropped.\r\n",
                (intmax_t )cur_time, (intmax_t )(db->last_time));
        return FDB_WRITE_ERR;
    }

    result = update_sec_status(db, &db->cur_sec, blob, cur_time);
    if (result != FDB_NO_ERR) {
        FDB_INFO("Error: update the sector status failed (%d)", result);
        return result;
    }
    /* write the TSL node */
    result = write_tsl(db, blob, cur_time);
    if (result != FDB_NO_ERR) {
        FDB_INFO("Error: write tsl failed (%d)", result);
        return result;
    }

    /* recalculate the current using sector info */
    db->cur_sec.end_idx = db->cur_sec.empty_idx;
    db->cur_sec.end_time = cur_time;
    db->cur_sec.empty_idx += LOG_IDX_DATA_SIZE;
    db->cur_sec.empty_data -= FDB_WG_ALIGN(blob->size);
    db->cur_sec.remain -= LOG_IDX_DATA_SIZE + FDB_WG_ALIGN(blob->size);
    db->last_time = cur_time;

    return result;
}

1.Incorrect

FDB_INFO("Warning: (user PRIdMAX) append length (%" PRIdMAX ") is more than the db->max_len (%" PRIdMAX "). This tsl will be dropped.\r\n", 
                (intmax_t)blob->size, (intmax_t)(db->max_len));

The corresponding printing information is as follows:

[FlashDB][tsl][LOG_TSDB][LOG_TSDB] Warning: (user PRIdMAX) append length (773094113460) is more than the db->max_len (549755813888). This tsl will be dropped.

2.correct

FDB_INFO("Warning: (user PRId32) append length (%" PRId32 ") is more than the db->max_len (%" PRId32 "). This tsl will be dropped.\r\n", 
                blob->size, (db->max_len));

The corresponding printing information is as follows:

[FlashDB][tsl][LOG_TSDB][LOG_TSDB] Warning: (user PRId32) append length (180) is more than the db->max_len (128). This tsl will be dropped.


Solution

  • You are already using a C preprocessor conditional to define the timestamp type. That makes it simple to mimic the PRI* standard implemented by <inttypes.h>: Just define a format conversion string literal preprocessor macro alongside the type itself and then use that.

    #ifdef FDB_USING_TIMESTAMP_64BIT
      typedef int64_t fdb_time_t;
    # define PRIdFDB_TIME PRId64
    #else
      typedef int32_t fdb_time_t;
    # define PRIdFDB_TIME PRId32
    #endif /* FDB_USING_TIMESTAMP_64BIT */
    
    …
    
    FDB_INFO("Warning: current timestamp (%" PRIdFDB_TIME ") is "
             "less than or equal to the last save timestamp "
             "(%" PRIdFDB_TIME "). This tsl will be dropped.\r\n",
             cur_time, db->last_time);
    

    This lets your code completely avoid guessing casts and format strings and just has the runtime do its job.

    In the same preprocessor conditional, you can also define other things which depend on that type definition like e.g. the maximum size of a formatted string.