Buffer gets overwritten

2019-08-19 09:22发布

问题:

I'm facing this issue on an ESP8266 (Arduino like board), but this problem is regarding c/c++, so I'm asking this here.

I have not that much experience with native languages like c/c++ and I'm facing a strange issue, which drives me crazy. So I'm using an Wemos D1 mini (ESP8266) which uses a class calles ConfigManager to read a configuration file from eeprom. The config file is formatted as json, so I'm using ArduinoJson to parse the content. I have declared a StaticJsonBuffer and pointer to a JsonObject in the header, like you can see in the code example:

//FILE: ConfigManager.h
#ifndef ConfigManager_H
#define ConfigManager_H

#include <Arduino.h>
#include <ArduinoJson.h>
#include <FS.h>
#include "Logger.h"

class ConfigManager {
public:

    Settings *settings;  

    ConfigManager();

    void read_from_eeprom();

private:

    File configFile;
    JsonObject *json;
    StaticJsonBuffer<200> jsonBuffer;

    void open_file(const char *permission);

    void read_json();

    void recreate_file();

    void create_json();

    void check_success();

    void populate_settings();

    void clean_up();
};

#endif

When the function read_from_eeprom is invoked, it opens the file and invokes the functionread_json:

void ConfigManager::read_json() {
    size_t size = configFile.size();
    Log.verbose("[ConfigManager] Config file size: %d", size);

    std::unique_ptr<char[]> buf(new char[size]);
    configFile.readBytes(buf.get(), size);
    Log.verbose("[ConfigManager] File content: %s", buf.get());

    Log.verbose("[ConfigManager] Parsing json");
    json = &jsonBuffer.parseObject(buf.get());

    Log.notice("[ConfigManager] Json is:");
    json->printTo(Serial);
    Serial.println();
}

Which is followed by a call to check_success()

void ConfigManager::check_success() {
    Log.notice("[ConfigManager] Json is:");
    json->printTo(Serial);
    Serial.println();


    bool should_recreate = true;
    if (json->success()) {
        Log.notice("[ConfigManager] Parsed json successfully");

        auto version = json->get<const char*>("version");
        if (version) {
            if (strcmp(version, Settings::current_version) == 0) {
                Log.notice("[ConfigManager] Config version is up2date");
                should_recreate = false;
            } else {
                Log.warning("[ConfigManager] Config version outdated");
            }
        } else {
            Log.warning("[ConfigManager] Invalid config file");
        }
    } else {
        Log.warning("[ConfigManager] Config file is not valid json");
    }

    if (should_recreate) {
        Log.notice("[ConfigManager] Recreating config file");
        recreate_file();
        create_json();
    }

    Log.notice("JSON IS: ");
    json->prettyPrintTo(Serial);

    Log.notice("[ConfigManager] Sucessfully read json");
}

So what I noticed is that the file content is fine. E.g. {"version":"0.2","led_count":"64"}.

Then the json is parsed, which succeeds and logs the json object, which is again {"version":"0.2","led_count":"64"}.

Afterwards the function returns, and calls check_success, which again prints the content of the json object to the log, but this time it seems that something has overwritten the JsonBuffer, which causes the json object to be corrupted. This time the logged content is {"v␂":"0.2","led_count":"64"} (with some strange unicorn characters that change as the source code changes). I'm trying to figure out whats going on for many hours now, but I'm stuck. Can someone please point me in the right direction to solve this problem? Thank you!

The full Log can be found HERE, as well as ConfigManager.h and ConfigManager.cpp

回答1:

*I'd prefer write that in comments, because I don't have arduino and can't verify that my advice 100% helpful. But I can't use comments with "my reputation" :). So please don't press "minus button" if my answer didn't help... *

According to that it seems you need to keep original json string while you using json buffer.

  1. Keep the JSON string in memory long enough

The library never make memory duplication. This has an important implication on string values, it means that the library will return pointer to chunks of the string.

For instance, let’s imagine that you parse ["hello","world"], like this:

char[] json = "[\"hello\",\"world\"]";

StaticJsonBuffer<32> buffer;

JsonArray& array = buffer.parseArray(json);

const char* first = array[0];

const char* second = array[1];

In that case, both first and second are pointers to the content of the original string json. So this will only work if json is still in memory.

So, it make sense try make std::unique_ptr buf a class member (same as StaticJsonBuffer) and check how it works.

BTW, IMO std::vector will be more suitable there... And I'm not sure that unique_ptr deletes arrays properly.



标签: c++ buffer