cJSON_Parse() causing parse error for JSON files with more than 50 lines on Renesas MCU with NextX module

77 views Asked by At

I'm working on a project using a Renesas MCU with the NetX module, where I'm utilizing the cJSON library to parse JSON data from a file. On my PC, the parsing works perfectly fine regardless of the size of the JSON file. However, when I deploy the same code to the Renesas MCU, cJSON_Parse() starts throwing parse errors for JSON files with more than 50 lines.

Here's a simplified version of my code:

#include <stdio.h>
#include <stdlib.h>
#include "cJSON.h"

int main() {
    FILE *fp;
    char buffer[10240];
    cJSON *json;

    strcpy(buffer, "valid json string");

    json = cJSON_Parse(buffer); // if buffer too long, it can not parse correctly.
    if (json == NULL) {
        const char *error_ptr = cJSON_GetErrorPtr();
        if (error_ptr != NULL) {
            printf("Error before: %s\n", error_ptr);
        }
        cJSON_Delete(json);
        return 1;
    }

    // Process JSON data here...

    cJSON_Delete(json);
    return 0;
}

Despite working fine on my PC, cJSON_Parse() starts failing when the JSON string exceeds(if in a file, around 50 lines) on the Renesas MCU with the NetX module. Are there any specific limitations or considerations I should be aware of when using cJSON library on this platform? How can I ensure proper parsing for larger JSON files on this MCU?

PS: suggestions are MCU stack size limitation, but how to configure it?

Any insights or suggestions would be greatly appreciated. Thanks in advance!

2

There are 2 answers

7
dontoronto On

MCUs typically have much more limited RAM and storage compared to a PC. The buffer size of 1024 bytes may not be sufficient to hold larger JSON files in their entirety, leading to incomplete data being passed to cJSON_Parse(), which could cause parsing errors.

Try out this:

#include <stdio.h>
#include <stdlib.h>
#include "cJSON.h"

int main() {
    FILE *fp;
    long file_size;
    char *buffer;
    cJSON *json;

    fp = fopen("data.json", "r");
    if (fp == NULL) {
        printf("Error opening file.\n");
        return 1;
    }

    // Determine file size
    fseek(fp, 0, SEEK_END);
    file_size = ftell(fp);
    rewind(fp);

    // Allocate memory for the entire content
    buffer = (char *)malloc(sizeof(char) * file_size);
    if (buffer == NULL) {
        printf("Memory allocation failed.\n");
        fclose(fp);
        return 1;
    }

    // Copy the file into the buffer
    fread(buffer, 1, file_size, fp);
    fclose(fp);

    // Ensure the string is null-terminated
    buffer[file_size] = '\0';

    json = cJSON_Parse(buffer);
    if (json == NULL) {
        const char *error_ptr = cJSON_GetErrorPtr();
        if (error_ptr != NULL) {
            printf("Error before: %s\n", error_ptr);
        }
        free(buffer); // Free the allocated memory
        cJSON_Delete(json);
        return 1;
    }

    // Process JSON data here...

    cJSON_Delete(json);
    free(buffer); // Free the allocated memory
    return 0;
}
3
John Bollinger On

With acknowledgement to @Gerhardh, who pointed it out in comments, your code does nothing to ensure that the JSON data read into buffer are zero terminated. Specifically,

  • local variables, including those of main(), are initialized only if you provide an explicit initializer. You provide none for the buffer variable, so all its contents are initially indeterminate. These can be zero, but they don't have to be.

  • the fread() function does not add a terminator after the data it reads.

  • you do not manually add a terminator, either.

However, cJSON_Parse() requires its input to be a conventional nul-terminated string. You might happen to get that incidentally, on either your PC or on your MCU, but it is not safe to rely on it.

The number of lines of the input is probably a red herring, except inasmuch as it correlates with the length of the input in bytes. It is plausible that the MCU happens to ensure, for some unspecified reason, that some number of initial bytes of the stack are zeroed. Files that happen to be shorter would likely, then, be parsed correctly, but longer files probably would not be. It is plausible that your PC zero-initializes a larger region of the stack, such that the program's flaw does not manifest misbehavior there.

Since you need to check the return value of fread() anyway, I'd just use that (on success) to add the needed terminator:

        size_t bytes_read = fread(buffer, 1, sizeof(buffer), fp);
        if (bytes_read < 1) {
            // handle error (or empty file) ...
        } else if (bytes_read >= sizeof(buffer)) {
            // handle overlength input ...
        }
        buffer[bytes_read] = '\0';