Description
Issue
Calls to the following may result in truncated values:
void JSONVar::operator=(unsigned int i)
void JSONVar::operator=(long l)
void JSONVar::operator=(unsigned long ul)
All these methods wrap a call to CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num)
shown below which sets valuedouble
correctly but truncates valueint
for values outside the bounds of INT_MIN
- INT_MAX
.
CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num)
{
cJSON *item = cJSON_New_Item(&global_hooks);
if(item)
{
item->type = cJSON_Number;
item->valuedouble = num;
/* use saturation in case of overflow */
if (num >= INT_MAX)
{
item->valueint = INT_MAX;
}
else if (num <= (double)INT_MIN)
{
item->valueint = INT_MIN;
}
else
{
item->valueint = (int)num;
}
}
return item;
}
The corresponding getters all access the valueint
.
JSONVar::operator unsigned int () const
{
return cJSON_IsNumber (_json) ? _json->valueint : 0;
}
JSONVar::operator long () const
{
return cJSON_IsNumber (_json) ? _json->valueint : 0;
}
JSONVar::operator unsigned long () const
{
return cJSON_IsNumber (_json) ? _json->valueint : 0;
}
Steps to reproduce
Sample code:
#include <Arduino_JSON.h>
void setup() {
Serial.begin(115200);
JSONVar jsonVar;
jsonVar["unsignedInt"] = UINT_MAX;
jsonVar["longMax"] = LONG_MAX;
jsonVar["longMin"] = LONG_MIN;
jsonVar["unsignedLong"] = ULONG_MAX;
Serial.println("Expected: " + String((double) jsonVar["unsignedInt"]) + " got: " + String((unsigned int) jsonVar["unsignedInt"]));
Serial.println("Expected: " + String((double) jsonVar["longMax"]) + " got: " + String((long) jsonVar["longMax"]));
Serial.println("Expected: " + String((double) jsonVar["longMin"]) + " got: " + String((long) jsonVar["longMin"]));
Serial.println("Expected: " + String((double) jsonVar["unsignedLong"]) + " got: " + String((unsigned long) jsonVar["unsignedLong"]));
}
void loop() {
}
Output:
Expected: 4294967295.00 got: 2147483647
Expected: 2147483647.00 got: 2147483647
Expected: -2147483648.00 got: -k
Expected: 4294967295.00 got: 2147483647
Potential fixes
- Fix the implementation of cJSON_CreateNumber (unlikely see Clean up the cJSON struct DaveGamble/cJSON#63) OR
- Use
valuedouble
and static_cast to the correct type.
JSONVar::operator unsigned int () const
{
return cJSON_IsNumber (_json) ? static_cast<unsigned int>_json->valuedouble: 0;
}
JSONVar::operator long () const
{
return cJSON_IsNumber (_json) ? static_cast<long>_json->valuedouble: 0;
}
JSONVar::operator unsigned long () const
{
return cJSON_IsNumber (_json) ? static_cast<unsigned long>_json->valuedouble: 0;
}
Workaround:
When reading values of the types listed above use cast to double
to get the valuedouble
and then cast to the desired type.
e.g. static_cast((double) jsonVar["unsignedLong"])