diff --git a/CHANGELOG.md b/CHANGELOG.md index d342cb1ba..f37e14000 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ ### Development versions after the 0.10.2 release +#### Build 2009200 + +- Increased available heap memory by 4kB +- Use F() macro for the majority of strings +- Restructure timezone code +- Restructured settings saved code +- Updated ArduinoJSON to 6.16.1 + #### Build 2009170 - New WLED logo on Welcome screen (#1164) diff --git a/wled00/FX.h b/wled00/FX.h index 86238c2e3..3a1a99e4f 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -49,7 +49,11 @@ /* each segment uses 52 bytes of SRAM memory, so if you're application fails because of insufficient memory, decreasing MAX_NUM_SEGMENTS may help */ -#define MAX_NUM_SEGMENTS 10 +#ifdef ESP8266 + #define MAX_NUM_SEGMENTS 10 +#else + #define MAX_NUM_SEGMENTS 10 +#endif /* How much data bytes all segments combined may allocate */ #ifdef ESP8266 diff --git a/wled00/const.h b/wled00/const.h index 9fac43396..97bcb3fdf 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -126,4 +126,11 @@ #define ABL_MILLIAMPS_DEFAULT 850; // auto lower brightness to stay close to milliampere limit +// Size of buffer for API JSON object (increase for more segments) +#ifdef ESP8266 + #define JSON_BUFFER_SIZE 9216 +#else + #define JSON_BUFFER_SIZE 16384 +#endif + #endif diff --git a/wled00/e131.cpp b/wled00/e131.cpp index c734d4392..2656f9e2d 100644 --- a/wled00/e131.cpp +++ b/wled00/e131.cpp @@ -144,7 +144,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, bool isArtnet){ break; } default: - DEBUG_PRINTLN("unknown E1.31 DMX mode"); + DEBUG_PRINTLN(F("unknown E1.31 DMX mode")); return; // nothing to do break; } diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 0982fd62b..9cc285cb9 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -193,7 +193,7 @@ bool applyPreset(byte index, bool loadBri = true); void savePreset(byte index, bool persist = true); void loadMacro(byte index, char* m); void applyMacro(byte index); -void saveMacro(byte index, String mc, bool persist = true); //only commit on single save, not in settings +void saveMacro(byte index, const String& mc, bool persist = true); //only commit on single save, not in settings //wled_serial.cpp void handleSerial(); @@ -205,10 +205,10 @@ void initServer(); void serveIndexOrWelcome(AsyncWebServerRequest *request); void serveIndex(AsyncWebServerRequest* request); String msgProcessor(const String& var); -void serveMessage(AsyncWebServerRequest* request, uint16_t code, String headl, String subl="", byte optionT=255); +void serveMessage(AsyncWebServerRequest* request, uint16_t code, const String& headl, const String& subl="", byte optionT=255); String settingsProcessor(const String& var); String dmxProcessor(const String& var); -void serveSettings(AsyncWebServerRequest* request); +void serveSettings(AsyncWebServerRequest* request, bool post = false); //ws.cpp void handleWs(); diff --git a/wled00/hue.cpp b/wled00/hue.cpp index 77091f63e..607aa9a2c 100644 --- a/wled00/hue.cpp +++ b/wled00/hue.cpp @@ -34,7 +34,7 @@ void handleHue() void reconnectHue() { if (!WLED_CONNECTED || !huePollingEnabled) return; - DEBUG_PRINTLN("Hue reconnect"); + DEBUG_PRINTLN(F("Hue reconnect")); if (hueClient == nullptr) { hueClient = new AsyncClient(); hueClient->onConnect(&onHueConnect, hueClient); @@ -47,13 +47,13 @@ void reconnectHue() void onHueError(void* arg, AsyncClient* client, int8_t error) { - DEBUG_PRINTLN("Hue err"); + DEBUG_PRINTLN(F("Hue err")); hueError = HUE_ERROR_TIMEOUT; } void onHueConnect(void* arg, AsyncClient* client) { - DEBUG_PRINTLN("Hue connect"); + DEBUG_PRINTLN(F("Hue connect")); sendHuePoll(); } @@ -68,9 +68,10 @@ void sendHuePoll() req += F("\r\nContent-Length: 25\r\n\r\n{\"devicetype\":\"wled#esp\"}"); } else { - req += "GET /api/"; + req += F("GET /api/"); req += hueApiKey; - req += "/lights/" + String(huePollLightId); + req += F("/lights/"); + req += String(huePollLightId); req += F(" HTTP/1.1\r\nHost: "); req += hueIP.toString(); req += "\r\n\r\n"; @@ -100,7 +101,7 @@ void onHueData(void* arg, AsyncClient* client, void *data, size_t len) hueError = HUE_ERROR_JSON_PARSING; return; } - int hueErrorCode = root[0]["error"]["type"]; + int hueErrorCode = root[0][F("error")][F("type")]; if (hueErrorCode)//hue bridge returned error { hueError = hueErrorCode; @@ -115,7 +116,7 @@ void onHueData(void* arg, AsyncClient* client, void *data, size_t len) if (hueAuthRequired) { - const char* apikey = root[0]["success"]["username"]; + const char* apikey = root[0][F("success")][F("username")]; if (apikey != nullptr && strlen(apikey) < sizeof(hueApiKey)) { strcpy(hueApiKey, apikey); @@ -146,10 +147,10 @@ void onHueData(void* arg, AsyncClient* client, void *data, size_t len) { hueBri = root["bri"]; hueBri++; - const char* cm =root["colormode"]; + const char* cm =root[F("colormode")]; if (cm != nullptr) //Color device { - if (strstr(cm,"ct") != nullptr) //ct mode + if (strstr(cm,("ct")) != nullptr) //ct mode { hueCt = root["ct"]; hueColormode = 3; @@ -160,8 +161,8 @@ void onHueData(void* arg, AsyncClient* client, void *data, size_t len) hueColormode = 1; } else //hs mode { - hueHue = root["hue"]; - hueSat = root["sat"]; + hueHue = root[F("hue")]; + hueSat = root[F("sat")]; hueColormode = 2; } } diff --git a/wled00/json.cpp b/wled00/json.cpp index c18d92503..2b90c5775 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -6,19 +6,19 @@ void deserializeSegment(JsonObject elem, byte it) { - byte id = elem["id"] | it; + byte id = elem[F("id")] | it; if (id < strip.getMaxSegments()) { WS2812FX::Segment& seg = strip.getSegment(id); - uint16_t start = elem["start"] | seg.start; - int stop = elem["stop"] | -1; + uint16_t start = elem[F("start")] | seg.start; + int stop = elem[F("stop")] | -1; if (stop < 0) { - uint16_t len = elem["len"]; + uint16_t len = elem[F("len")]; stop = (len > 0) ? start + len : seg.stop; } - uint16_t grp = elem["grp"] | seg.grouping; - uint16_t spc = elem["spc"] | seg.spacing; + uint16_t grp = elem[F("grp")] | seg.grouping; + uint16_t spc = elem[F("spc")] | seg.spacing; strip.setSegment(id, start, stop, grp, spc); int segbri = elem["bri"] | -1; @@ -31,7 +31,7 @@ void deserializeSegment(JsonObject elem, byte it) seg.setOption(SEG_OPTION_ON, elem["on"] | seg.getOption(SEG_OPTION_ON)); - JsonArray colarr = elem["col"]; + JsonArray colarr = elem[F("col")]; if (!colarr.isNull()) { for (uint8_t i = 0; i < 3; i++) @@ -57,25 +57,25 @@ void deserializeSegment(JsonObject elem, byte it) } //if (pal != seg.palette && pal < strip.getPaletteCount()) strip.setPalette(pal); - seg.setOption(SEG_OPTION_SELECTED, elem["sel"] | seg.getOption(SEG_OPTION_SELECTED)); - seg.setOption(SEG_OPTION_REVERSED, elem["rev"] | seg.getOption(SEG_OPTION_REVERSED)); - seg.setOption(SEG_OPTION_MIRROR , elem["mi"] | seg.getOption(SEG_OPTION_MIRROR )); + seg.setOption(SEG_OPTION_SELECTED, elem[F("sel")] | seg.getOption(SEG_OPTION_SELECTED)); + seg.setOption(SEG_OPTION_REVERSED, elem[F("rev")] | seg.getOption(SEG_OPTION_REVERSED)); + seg.setOption(SEG_OPTION_MIRROR , elem[F("mi")] | seg.getOption(SEG_OPTION_MIRROR )); //temporary, strip object gets updated via colorUpdated() if (id == strip.getMainSegmentId()) { - effectCurrent = elem["fx"] | effectCurrent; - effectSpeed = elem["sx"] | effectSpeed; - effectIntensity = elem["ix"] | effectIntensity; - effectPalette = elem["pal"] | effectPalette; + effectCurrent = elem[F("fx")] | effectCurrent; + effectSpeed = elem[F("sx")] | effectSpeed; + effectIntensity = elem[F("ix")] | effectIntensity; + effectPalette = elem[F("pal")] | effectPalette; } else { //permanent - byte fx = elem["fx"] | seg.mode; + byte fx = elem[F("fx")] | seg.mode; if (fx != seg.mode && fx < strip.getModeCount()) strip.setMode(id, fx); - seg.speed = elem["sx"] | seg.speed; - seg.intensity = elem["ix"] | seg.intensity; - seg.palette = elem["pal"] | seg.palette; + seg.speed = elem[F("sx")] | seg.speed; + seg.intensity = elem[F("ix")] | seg.intensity; + seg.palette = elem[F("pal")] | seg.palette; } - JsonArray iarr = elem["i"]; //set individual LEDs + JsonArray iarr = elem[F("i")]; //set individual LEDs if (!iarr.isNull()) { strip.setPixelSegment(id); @@ -127,9 +127,9 @@ void deserializeSegment(JsonObject elem, byte it) bool deserializeState(JsonObject root) { strip.applyToAllSelected = false; - bool stateResponse = root["v"] | false; + bool stateResponse = root[F("v")] | false; - int ps = root["ps"] | -1; + int ps = root[F("ps")] | -1; if (ps >= 0) applyPreset(ps); bri = root["bri"] | bri; @@ -137,14 +137,14 @@ bool deserializeState(JsonObject root) bool on = root["on"] | (bri > 0); if (!on != !bri) toggleOnOff(); - int tr = root["transition"] | -1; + int tr = root[F("transition")] | -1; if (tr >= 0) { transitionDelay = tr; transitionDelay *= 100; } - tr = root["tt"] | -1; + tr = root[F("tt")] | -1; if (tr >= 0) { transitionDelayTemp = tr; @@ -152,42 +152,42 @@ bool deserializeState(JsonObject root) jsonTransitionOnce = true; } - int cy = root["pl"] | -2; + int cy = root[F("pl")] | -2; if (cy > -2) presetCyclingEnabled = (cy >= 0); - JsonObject ccnf = root["ccnf"]; - presetCycleMin = ccnf["min"] | presetCycleMin; - presetCycleMax = ccnf["max"] | presetCycleMax; - tr = ccnf["time"] | -1; + JsonObject ccnf = root[F("ccnf")]; + presetCycleMin = ccnf[F("min")] | presetCycleMin; + presetCycleMax = ccnf[F("max")] | presetCycleMax; + tr = ccnf[F("time")] | -1; if (tr >= 2) presetCycleTime = tr; - JsonObject nl = root["nl"]; + JsonObject nl = root[F("nl")]; nightlightActive = nl["on"] | nightlightActive; - nightlightDelayMins = nl["dur"] | nightlightDelayMins; - nightlightMode = nl["fade"] | nightlightMode; //deprecated - nightlightMode = nl["mode"] | nightlightMode; - nightlightTargetBri = nl["tbri"] | nightlightTargetBri; + nightlightDelayMins = nl[F("dur")] | nightlightDelayMins; + nightlightMode = nl[F("fade")] | nightlightMode; //deprecated + nightlightMode = nl[F("mode")] | nightlightMode; + nightlightTargetBri = nl[F("tbri")] | nightlightTargetBri; - JsonObject udpn = root["udpn"]; - notifyDirect = udpn["send"] | notifyDirect; - receiveNotifications = udpn["recv"] | receiveNotifications; - bool noNotification = udpn["nn"]; //send no notification just for this request + JsonObject udpn = root[F("udpn")]; + notifyDirect = udpn[F("send")] | notifyDirect; + receiveNotifications = udpn[F("recv")] | receiveNotifications; + bool noNotification = udpn[F("nn")]; //send no notification just for this request - int timein = root["time"] | -1; + int timein = root[F("time")] | -1; if (timein != -1) setTime(timein); - doReboot = root["rb"] | doReboot; + doReboot = root[F("rb")] | doReboot; - realtimeOverride = root["lor"] | realtimeOverride; + realtimeOverride = root[F("lor")] | realtimeOverride; if (realtimeOverride > 2) realtimeOverride = REALTIME_OVERRIDE_ALWAYS; byte prevMain = strip.getMainSegmentId(); - strip.mainSegment = root["mainseg"] | prevMain; + strip.mainSegment = root[F("mainseg")] | prevMain; if (strip.getMainSegmentId() != prevMain) setValuesFromMainSeg(); int it = 0; - JsonVariant segVar = root["seg"]; + JsonVariant segVar = root[F("seg")]; if (segVar.is()) { - int id = segVar["id"] | -1; + int id = segVar[F("id")] | -1; if (id < 0) { //set all selected segments bool didSet = false; @@ -222,9 +222,9 @@ bool deserializeState(JsonObject root) colorUpdated(noNotification ? NOTIFIER_CALL_MODE_NO_NOTIFY : NOTIFIER_CALL_MODE_DIRECT_CHANGE); //write presets to flash directly? - bool persistSaves = !(root["np"] | false); + bool persistSaves = !(root[F("np")] | false); - ps = root["psave"] | -1; + ps = root[F("psave")] | -1; if (ps >= 0) savePreset(ps, persistSaves); return stateResponse; @@ -232,12 +232,12 @@ bool deserializeState(JsonObject root) void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id) { - root["id"] = id; - root["start"] = seg.start; - root["stop"] = seg.stop; - root["len"] = seg.stop - seg.start; - root["grp"] = seg.grouping; - root["spc"] = seg.spacing; + root[F("id")] = id; + root[F("start")] = seg.start; + root[F("stop")] = seg.stop; + root[F("len")] = seg.stop - seg.start; + root[F("grp")] = seg.grouping; + root[F("spc")] = seg.spacing; root["on"] = seg.getOption(SEG_OPTION_ON); byte segbri = seg.opacity; root["bri"] = (segbri) ? segbri : 255; @@ -263,50 +263,50 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id) } } - root["fx"] = seg.mode; - root["sx"] = seg.speed; - root["ix"] = seg.intensity; - root["pal"] = seg.palette; - root["sel"] = seg.isSelected(); - root["rev"] = seg.getOption(SEG_OPTION_REVERSED); - root["mi"] = seg.getOption(SEG_OPTION_MIRROR); + root[F("fx")] = seg.mode; + root[F("sx")] = seg.speed; + root[F("ix")] = seg.intensity; + root[F("pal")] = seg.palette; + root[F("sel")] = seg.isSelected(); + root[F("rev")] = seg.getOption(SEG_OPTION_REVERSED); + root[F("mi")] = seg.getOption(SEG_OPTION_MIRROR); } void serializeState(JsonObject root) { - if (errorFlag) root["error"] = errorFlag; + if (errorFlag) root[F("error")] = errorFlag; root["on"] = (bri > 0); root["bri"] = briLast; - root["transition"] = transitionDelay/100; //in 100ms + root[F("transition")] = transitionDelay/100; //in 100ms - root["ps"] = currentPreset; - root["pss"] = savedPresets; - root["pl"] = (presetCyclingEnabled) ? 0: -1; + root[F("ps")] = currentPreset; + root[F("pss")] = savedPresets; + root[F("pl")] = (presetCyclingEnabled) ? 0: -1; usermods.addToJsonState(root); //temporary for preset cycle JsonObject ccnf = root.createNestedObject("ccnf"); - ccnf["min"] = presetCycleMin; - ccnf["max"] = presetCycleMax; - ccnf["time"] = presetCycleTime; + ccnf[F("min")] = presetCycleMin; + ccnf[F("max")] = presetCycleMax; + ccnf[F("time")] = presetCycleTime; JsonObject nl = root.createNestedObject("nl"); nl["on"] = nightlightActive; - nl["dur"] = nightlightDelayMins; - nl["fade"] = (nightlightMode > NL_MODE_SET); //deprecated - nl["mode"] = nightlightMode; - nl["tbri"] = nightlightTargetBri; + nl[F("dur")] = nightlightDelayMins; + nl[F("fade")] = (nightlightMode > NL_MODE_SET); //deprecated + nl[F("mode")] = nightlightMode; + nl[F("tbri")] = nightlightTargetBri; JsonObject udpn = root.createNestedObject("udpn"); - udpn["send"] = notifyDirect; - udpn["recv"] = receiveNotifications; + udpn[F("send")] = notifyDirect; + udpn[F("recv")] = receiveNotifications; - root["lor"] = realtimeOverride; + root[F("lor")] = realtimeOverride; - root["mainseg"] = strip.getMainSegmentId(); + root[F("mainseg")] = strip.getMainSegmentId(); JsonArray seg = root.createNestedArray("seg"); for (byte s = 0; s < strip.getMaxSegments(); s++) @@ -342,87 +342,87 @@ int getSignalQuality(int rssi) void serializeInfo(JsonObject root) { - root["ver"] = versionString; - root["vid"] = VERSION; - //root["cn"] = WLED_CODENAME; + root[F("ver")] = versionString; + root[F("vid")] = VERSION; + //root[F("cn")] = WLED_CODENAME; JsonObject leds = root.createNestedObject("leds"); - leds["count"] = ledCount; - leds["rgbw"] = useRGBW; - leds["wv"] = useRGBW && (strip.rgbwMode == RGBW_MODE_MANUAL_ONLY || strip.rgbwMode == RGBW_MODE_DUAL); //should a white channel slider be displayed? + leds[F("count")] = ledCount; + leds[F("rgbw")] = useRGBW; + leds[F("wv")] = useRGBW && (strip.rgbwMode == RGBW_MODE_MANUAL_ONLY || strip.rgbwMode == RGBW_MODE_DUAL); //should a white channel slider be displayed? JsonArray leds_pin = leds.createNestedArray("pin"); leds_pin.add(LEDPIN); - leds["pwr"] = strip.currentMilliamps; - leds["maxpwr"] = (strip.currentMilliamps)? strip.ablMilliampsMax : 0; - leds["maxseg"] = strip.getMaxSegments(); - leds["seglock"] = false; //will be used in the future to prevent modifications to segment config + leds[F("pwr")] = strip.currentMilliamps; + leds[F("maxpwr")] = (strip.currentMilliamps)? strip.ablMilliampsMax : 0; + leds[F("maxseg")] = strip.getMaxSegments(); + leds[F("seglock")] = false; //will be used in the future to prevent modifications to segment config - root["str"] = syncToggleReceive; + root[F("str")] = syncToggleReceive; - root["name"] = serverDescription; - root["udpport"] = udpPort; - root["live"] = (bool)realtimeMode; + root[F("name")] = serverDescription; + root[F("udpport")] = udpPort; + root[F("live")] = (bool)realtimeMode; switch (realtimeMode) { case REALTIME_MODE_INACTIVE: root["lm"] = ""; break; case REALTIME_MODE_GENERIC: root["lm"] = ""; break; - case REALTIME_MODE_UDP: root["lm"] = "UDP"; break; - case REALTIME_MODE_HYPERION: root["lm"] = "Hyperion"; break; - case REALTIME_MODE_E131: root["lm"] = "E1.31"; break; + case REALTIME_MODE_UDP: root["lm"] = F("UDP"); break; + case REALTIME_MODE_HYPERION: root["lm"] = F("Hyperion"); break; + case REALTIME_MODE_E131: root["lm"] = F("E1.31"); break; case REALTIME_MODE_ADALIGHT: root["lm"] = F("USB Adalight/TPM2"); break; - case REALTIME_MODE_ARTNET: root["lm"] = "Art-Net"; break; + case REALTIME_MODE_ARTNET: root["lm"] = F("Art-Net"); break; case REALTIME_MODE_TPM2NET: root["lm"] = F("tpm2.net"); break; } if (realtimeIP[0] == 0) { - root["lip"] = ""; + root[F("lip")] = ""; } else { - root["lip"] = realtimeIP.toString(); + root[F("lip")] = realtimeIP.toString(); } #ifdef WLED_ENABLE_WEBSOCKETS - root["ws"] = ws.count(); + root[F("ws")] = ws.count(); #else - root["ws"] = -1; + root[F("ws")] = -1; #endif - root["fxcount"] = strip.getModeCount(); - root["palcount"] = strip.getPaletteCount(); + root[F("fxcount")] = strip.getModeCount(); + root[F("palcount")] = strip.getPaletteCount(); JsonObject wifi_info = root.createNestedObject("wifi"); - wifi_info["bssid"] = WiFi.BSSIDstr(); + wifi_info[F("bssid")] = WiFi.BSSIDstr(); int qrssi = WiFi.RSSI(); - wifi_info["rssi"] = qrssi; - wifi_info["signal"] = getSignalQuality(qrssi); - wifi_info["channel"] = WiFi.channel(); + wifi_info[F("rssi")] = qrssi; + wifi_info[F("signal")] = getSignalQuality(qrssi); + wifi_info[F("channel")] = WiFi.channel(); #ifdef ARDUINO_ARCH_ESP32 #ifdef WLED_DEBUG - wifi_info["txPower"] = (int) WiFi.getTxPower(); - wifi_info["sleep"] = (bool) WiFi.getSleep(); + wifi_info[F("txPower")] = (int) WiFi.getTxPower(); + wifi_info[F("sleep")] = (bool) WiFi.getSleep(); #endif - root["arch"] = "esp32"; - root["core"] = ESP.getSdkVersion(); - //root["maxalloc"] = ESP.getMaxAllocHeap(); + root[F("arch")] = "esp32"; + root[F("core")] = ESP.getSdkVersion(); + //root[F("maxalloc")] = ESP.getMaxAllocHeap(); #ifdef WLED_DEBUG - root["resetReason0"] = (int)rtc_get_reset_reason(0); - root["resetReason1"] = (int)rtc_get_reset_reason(1); + root[F("resetReason0")] = (int)rtc_get_reset_reason(0); + root[F("resetReason1")] = (int)rtc_get_reset_reason(1); #endif - root["lwip"] = 0; + root[F("lwip")] = 0; #else - root["arch"] = "esp8266"; - root["core"] = ESP.getCoreVersion(); - //root["maxalloc"] = ESP.getMaxFreeBlockSize(); + root[F("arch")] = "esp8266"; + root[F("core")] = ESP.getCoreVersion(); + //root[F("maxalloc")] = ESP.getMaxFreeBlockSize(); #ifdef WLED_DEBUG - root["resetReason"] = (int)ESP.getResetInfoPtr()->reason; + root[F("resetReason")] = (int)ESP.getResetInfoPtr()->reason; #endif - root["lwip"] = LWIP_VERSION_MAJOR; + root[F("lwip")] = LWIP_VERSION_MAJOR; #endif - root["freeheap"] = ESP.getFreeHeap(); - root["uptime"] = millis()/1000 + rolloverMillis*4294967; + root[F("freeheap")] = ESP.getFreeHeap(); + root[F("uptime")] = millis()/1000 + rolloverMillis*4294967; usermods.addToJsonInfo(root); @@ -451,11 +451,11 @@ void serializeInfo(JsonObject root) #ifndef WLED_DISABLE_OTA os += 0x01; #endif - root["opt"] = os; + root[F("opt")] = os; - root["brand"] = "WLED"; - root["product"] = "FOSS"; - root["mac"] = escapedMac; + root[F("brand")] = "WLED"; + root[F("product")] = F("FOSS"); + root[F("mac")] = escapedMac; } void serveJson(AsyncWebServerRequest* request) @@ -469,11 +469,11 @@ void serveJson(AsyncWebServerRequest* request) serveLiveLeds(request); return; } - else if (url.indexOf("eff") > 0) { + else if (url.indexOf(F("eff")) > 0) { request->send_P(200, "application/json", JSON_mode_names); return; } - else if (url.indexOf("pal") > 0) { + else if (url.indexOf(F("pal")) > 0) { request->send_P(200, "application/json", JSON_palette_names); return; } @@ -482,7 +482,7 @@ void serveJson(AsyncWebServerRequest* request) return; } - AsyncJsonResponse* response = new AsyncJsonResponse(); + AsyncJsonResponse* response = new AsyncJsonResponse(JSON_BUFFER_SIZE); JsonObject doc = response->getRoot(); switch (subJson) @@ -498,8 +498,8 @@ void serveJson(AsyncWebServerRequest* request) serializeInfo(info); if (subJson != 3) { - doc["effects"] = serialized((const __FlashStringHelper*)JSON_mode_names); - doc["palettes"] = serialized((const __FlashStringHelper*)JSON_palette_names); + doc[F("effects")] = serialized((const __FlashStringHelper*)JSON_mode_names); + doc[F("palettes")] = serialized((const __FlashStringHelper*)JSON_palette_names); } } @@ -521,7 +521,8 @@ bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient) uint16_t used = ledCount; uint16_t n = (used -1) /MAX_LIVE_LEDS +1; //only serve every n'th LED if count over MAX_LIVE_LEDS - char buffer[2000] = "{\"leds\":["; + char buffer[2000]; + strcpy_P(buffer, PSTR("{\"leds\":[")); obuf = buffer; olen = 9; @@ -530,7 +531,7 @@ bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient) olen += sprintf(obuf + olen, "\"%06X\",", strip.getPixelColor(i)); } olen -= 1; - oappend("],\"n\":"); + oappend((const char*)F("],\"n\":")); oappendi(n); oappend("}"); if (request) { diff --git a/wled00/mqtt.cpp b/wled00/mqtt.cpp index 7234297c3..6d8a3b95a 100644 --- a/wled00/mqtt.cpp +++ b/wled00/mqtt.cpp @@ -48,18 +48,18 @@ void onMqttConnect(bool sessionPresent) } doPublishMqtt = true; - DEBUG_PRINTLN("MQTT ready"); + DEBUG_PRINTLN(F("MQTT ready")); } void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) { - DEBUG_PRINT("MQTT msg: "); + DEBUG_PRINT(F("MQTT msg: ")); DEBUG_PRINTLN(topic); // paranoia check to avoid npe if no payload if (payload==nullptr) { - DEBUG_PRINTLN("no payload -> leave"); + DEBUG_PRINTLN(F("no payload -> leave")); return; } DEBUG_PRINTLN(payload); @@ -83,7 +83,7 @@ void publishMqtt() { doPublishMqtt = false; if (!WLED_MQTT_CONNECTED) return; - DEBUG_PRINTLN("Publish MQTT"); + DEBUG_PRINTLN(F("Publish MQTT")); char s[10]; char subuf[38]; @@ -100,7 +100,7 @@ void publishMqtt() strcpy(subuf, mqttDeviceTopic); strcat(subuf, "/status"); - mqtt->publish(subuf, 0, true, "online"); + mqtt->publish(subuf, 0, true, (const char*)F("online")); char apires[1024]; XML_response(nullptr, apires); @@ -124,7 +124,7 @@ bool initMqtt() } if (mqtt->connected()) return true; - DEBUG_PRINTLN("Reconnecting MQTT"); + DEBUG_PRINTLN(F("Reconnecting MQTT")); IPAddress mqttIP; if (mqttIP.fromString(mqttServer)) //see if server is IP or domain { @@ -137,7 +137,7 @@ bool initMqtt() strcpy(mqttStatusTopic, mqttDeviceTopic); strcat(mqttStatusTopic, "/status"); - mqtt->setWill(mqttStatusTopic, 0, true, "offline"); + mqtt->setWill(mqttStatusTopic, 0, true, (const char*)F("offline")); mqtt->setKeepAlive(MQTT_KEEP_ALIVE_TIME); mqtt->connect(); return true; diff --git a/wled00/ntp.cpp b/wled00/ntp.cpp index 5a7962967..51d52d91d 100644 --- a/wled00/ntp.cpp +++ b/wled00/ntp.cpp @@ -4,69 +4,127 @@ /* * Acquires time from NTP server */ +Timezone* tz; -TimeChangeRule UTCr = {Last, Sun, Mar, 1, 0}; // UTC -Timezone tzUTC(UTCr, UTCr); +#define TZ_UTC 0 +#define TZ_UK 1 +#define TZ_EUROPE_CENTRAL 2 +#define TZ_EUROPE_EASTERN 3 +#define TZ_US_EASTERN 4 +#define TZ_US_CENTRAL 5 +#define TZ_US_MOUNTAIN 6 +#define TZ_US_ARIZONA 7 +#define TZ_US_PACIFIC 8 +#define TZ_CHINA 9 +#define TZ_JAPAN 10 +#define TZ_AUSTRALIA_EASTERN 11 +#define TZ_NEW_ZEALAND 12 +#define TZ_NORTH_KOREA 13 +#define TZ_INDIA 14 +#define TZ_SASKACHEWAN 15 +#define TZ_AUSTRALIA_NORTHERN 16 +#define TZ_AUSTRALIA_SOUTHERN 17 +#define TZ_INIT 255 -TimeChangeRule BST = {Last, Sun, Mar, 1, 60}; // British Summer Time -TimeChangeRule GMT = {Last, Sun, Oct, 2, 0}; // Standard Time -Timezone tzUK(BST, GMT); +byte tzCurrent = TZ_INIT; //uninitialized -TimeChangeRule CEST = {Last, Sun, Mar, 2, 120}; //Central European Summer Time -TimeChangeRule CET = {Last, Sun, Oct, 3, 60}; //Central European Standard Time -Timezone tzEUCentral(CEST, CET); +void updateTimezone() { + delete tz; + TimeChangeRule tcrDaylight = {Last, Sun, Mar, 1, 0}; //UTC + TimeChangeRule tcrStandard = tcrDaylight; //UTC -TimeChangeRule EEST = {Last, Sun, Mar, 3, 180}; //Central European Summer Time -TimeChangeRule EET = {Last, Sun, Oct, 4, 120}; //Central European Standard Time -Timezone tzEUEastern(EEST, EET); + switch (currentTimezone) { + case TZ_UK : { + tcrDaylight = {Last, Sun, Mar, 1, 60}; //British Summer Time + tcrStandard = {Last, Sun, Oct, 2, 0}; //Standard Time + break; + } + case TZ_EUROPE_CENTRAL : { + tcrDaylight = {Last, Sun, Mar, 2, 120}; //Central European Summer Time + tcrStandard = {Last, Sun, Oct, 3, 60}; //Central European Standard Time + break; + } + case TZ_EUROPE_EASTERN : { + tcrDaylight = {Last, Sun, Mar, 3, 180}; //East European Summer Time + tcrStandard = {Last, Sun, Oct, 4, 120}; //East European Standard Time + break; + } + case TZ_US_EASTERN : { + tcrDaylight = {Second, Sun, Mar, 2, -240}; //EDT = UTC - 4 hours + tcrStandard = {First, Sun, Nov, 2, -300}; //EST = UTC - 5 hours + break; + } + case TZ_US_CENTRAL : { + tcrDaylight = {Second, Sun, Mar, 2, -300}; //CDT = UTC - 5 hours + tcrStandard = {First, Sun, Nov, 2, -360}; //CST = UTC - 6 hours + break; + } + case TZ_US_MOUNTAIN : { + tcrDaylight = {Second, Sun, Mar, 2, -360}; //MDT = UTC - 6 hours + tcrStandard = {First, Sun, Nov, 2, -420}; //MST = UTC - 7 hours + break; + } + case TZ_US_ARIZONA : { + tcrDaylight = {First, Sun, Nov, 2, -420}; //MST = UTC - 7 hours + tcrStandard = {First, Sun, Nov, 2, -420}; //MST = UTC - 7 hours + break; + } + case TZ_US_PACIFIC : { + tcrDaylight = {Second, Sun, Mar, 2, -420}; //PDT = UTC - 7 hours + tcrStandard = {First, Sun, Nov, 2, -480}; //PST = UTC - 8 hours + break; + } + case TZ_CHINA : { + tcrDaylight = {Last, Sun, Mar, 1, 480}; //CST = UTC + 8 hours + tcrStandard = tcrDaylight; + break; + } + case TZ_JAPAN : { + tcrDaylight = {Last, Sun, Mar, 1, 540}; //JST = UTC + 9 hours + tcrStandard = tcrDaylight; + break; + } + case TZ_AUSTRALIA_EASTERN : { + tcrDaylight = {Second, Sun, Oct, 2, 660}; //AEDT = UTC + 11 hours + tcrStandard = {First, Sun, Apr, 3, 600}; //AEST = UTC + 10 hours + break; + } + case TZ_NEW_ZEALAND : { + tcrDaylight = {Second, Sun, Sep, 2, 780}; //NZDT = UTC + 13 hours + tcrStandard = {First, Sun, Apr, 3, 720}; //NZST = UTC + 12 hours + break; + } + case TZ_NORTH_KOREA : { + tcrDaylight = {Last, Sun, Mar, 1, 510}; //Pyongyang Time = UTC + 8.5 hours + tcrStandard = tcrDaylight; + break; + } + case TZ_INDIA : { + tcrDaylight = {Last, Sun, Mar, 1, 330}; //India Standard Time = UTC + 5.5 hours + tcrStandard = tcrDaylight; + break; + } + case TZ_SASKACHEWAN : { + tcrDaylight = {First, Sun, Nov, 2, -360}; //CST = UTC - 6 hours + tcrStandard = tcrDaylight; + break; + } + case TZ_AUSTRALIA_NORTHERN : { + tcrStandard = {First, Sun, Apr, 3, 570}; //ACST = UTC + 9.5 hours + tcrStandard = tcrDaylight; + break; + } + case TZ_AUSTRALIA_SOUTHERN : { + tcrDaylight = {First, Sun, Oct, 2, 630}; //ACDT = UTC + 10.5 hours + tcrStandard = {First, Sun, Apr, 3, 570}; //ACST = UTC + 9.5 hours + break; + } + } -TimeChangeRule EDT = {Second, Sun, Mar, 2, -240 }; //Daylight time = UTC - 4 hours -TimeChangeRule EST = {First, Sun, Nov, 2, -300 }; //Standard time = UTC - 5 hours -Timezone tzUSEastern(EDT, EST); + tzCurrent = currentTimezone; -TimeChangeRule CDT = {Second, Sun, Mar, 2, -300 }; //Daylight time = UTC - 5 hours -TimeChangeRule CST = {First, Sun, Nov, 2, -360 }; //Standard time = UTC - 6 hours -Timezone tzUSCentral(CDT, CST); - -Timezone tzCASaskatchewan(CST, CST); //Central without DST - -TimeChangeRule MDT = {Second, Sun, Mar, 2, -360 }; //Daylight time = UTC - 6 hours -TimeChangeRule MST = {First, Sun, Nov, 2, -420 }; //Standard time = UTC - 7 hours -Timezone tzUSMountain(MDT, MST); - -Timezone tzUSArizona(MST, MST); //Mountain without DST - -TimeChangeRule PDT = {Second, Sun, Mar, 2, -420 }; //Daylight time = UTC - 7 hours -TimeChangeRule PST = {First, Sun, Nov, 2, -480 }; //Standard time = UTC - 8 hours -Timezone tzUSPacific(PDT, PST); - -TimeChangeRule ChST = {Last, Sun, Mar, 1, 480}; // China Standard Time = UTC + 8 hours -Timezone tzChina(ChST, ChST); - -TimeChangeRule JST = {Last, Sun, Mar, 1, 540}; // Japan Standard Time = UTC + 9 hours -Timezone tzJapan(JST, JST); - -TimeChangeRule AEDT = {Second, Sun, Oct, 2, 660 }; //Daylight time = UTC + 11 hours -TimeChangeRule AEST = {First, Sun, Apr, 3, 600 }; //Standard time = UTC + 10 hours -Timezone tzAUEastern(AEDT, AEST); - -TimeChangeRule NZDT = {Second, Sun, Sep, 2, 780 }; //Daylight time = UTC + 13 hours -TimeChangeRule NZST = {First, Sun, Apr, 3, 720 }; //Standard time = UTC + 12 hours -Timezone tzNZ(NZDT, NZST); - -TimeChangeRule NKST = {Last, Sun, Mar, 1, 510}; //Pyongyang Time = UTC + 8.5 hours -Timezone tzNK(NKST, NKST); - -TimeChangeRule IST = {Last, Sun, Mar, 1, 330}; // India Standard Time = UTC + 5.5 hours -Timezone tzIndia(IST, IST); - -TimeChangeRule ACST = {First, Sun, Apr, 3, 570}; //Australian Central Standard = UTC + 9.5 hours -TimeChangeRule ACDT = {First, Sun, Oct, 2, 630}; //Australian Central Daylight = UTC + 10.5 hours -Timezone tzAUNorthern(ACST, ACST); -Timezone tzAUSouthern(ACDT, ACST); - -// Pick your timezone from here. -Timezone* timezones[] = {&tzUTC, &tzUK, &tzEUCentral, &tzEUEastern, &tzUSEastern, &tzUSCentral, &tzUSMountain, &tzUSArizona, &tzUSPacific, &tzChina, &tzJapan, &tzAUEastern, &tzNZ, &tzNK, &tzIndia, &tzCASaskatchewan, &tzAUNorthern, &tzAUSouthern}; + tz = new Timezone(tcrDaylight, tcrStandard); +} void handleNetworkTime() { @@ -95,7 +153,7 @@ void sendNTPPacket() #endif } - DEBUG_PRINTLN("send NTP"); + DEBUG_PRINTLN(F("send NTP")); byte pbuf[NTP_PACKET_SIZE]; memset(pbuf, 0, NTP_PACKET_SIZE); @@ -118,7 +176,7 @@ bool checkNTPResponse() { int cb = ntpUdp.parsePacket(); if (cb) { - DEBUG_PRINT("NTP recv, l="); + DEBUG_PRINT(F("NTP recv, l=")); DEBUG_PRINTLN(cb); byte pbuf[NTP_PACKET_SIZE]; ntpUdp.read(pbuf, NTP_PACKET_SIZE); // read the packet into the buffer @@ -129,7 +187,7 @@ bool checkNTPResponse() unsigned long secsSince1900 = highWord << 16 | lowWord; - DEBUG_PRINT("Unix time = "); + DEBUG_PRINT(F("Unix time = ")); unsigned long epoch = secsSince1900 - 2208988799UL; //subtract 70 years -1sec (on avg. more precision) setTime(epoch); DEBUG_PRINTLN(epoch); @@ -141,8 +199,9 @@ bool checkNTPResponse() void updateLocalTime() { + if (currentTimezone != tzCurrent) updateTimezone(); unsigned long tmc = now()+ utcOffsetSecs; - localTime = timezones[currentTimezone]->toLocal(tmc); + localTime = tz->toLocal(tmc); } void getTimeString(char* out) @@ -165,7 +224,8 @@ void getTimeString(char* out) void setCountdown() { - countdownTime = timezones[currentTimezone]->toUTC(getUnixTime(countdownHour, countdownMin, countdownSec, countdownDay, countdownMonth, countdownYear)); + if (currentTimezone != tzCurrent) updateTimezone(); + countdownTime = tz->toUTC(getUnixTime(countdownHour, countdownMin, countdownSec, countdownDay, countdownMonth, countdownYear)); if (countdownTime - now() > 0) countdownOverTriggered = false; } diff --git a/wled00/set.cpp b/wled00/set.cpp index 415258316..617e2357c 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -37,20 +37,20 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) //WIFI SETTINGS if (subPage == 1) { - strlcpy(clientSSID,request->arg("CS").c_str(), 33); + strlcpy(clientSSID,request->arg(F("CS")).c_str(), 33); - if (!isAsterisksOnly(request->arg("CP").c_str(), 65)) strlcpy(clientPass, request->arg("CP").c_str(), 65); + if (!isAsterisksOnly(request->arg(F("CP")).c_str(), 65)) strlcpy(clientPass, request->arg(F("CP")).c_str(), 65); - strlcpy(cmDNS, request->arg("CM").c_str(), 33); + strlcpy(cmDNS, request->arg(F("CM")).c_str(), 33); - apBehavior = request->arg("AB").toInt(); - strlcpy(apSSID, request->arg("AS").c_str(), 33); - apHide = request->hasArg("AH"); - int passlen = request->arg("AP").length(); - if (passlen == 0 || (passlen > 7 && !isAsterisksOnly(request->arg("AP").c_str(), 65))) strlcpy(apPass, request->arg("AP").c_str(), 65); - int t = request->arg("AC").toInt(); if (t > 0 && t < 14) apChannel = t; + apBehavior = request->arg(F("AB")).toInt(); + strlcpy(apSSID, request->arg(F("AS")).c_str(), 33); + apHide = request->hasArg(F("AH")); + int passlen = request->arg(F("AP")).length(); + if (passlen == 0 || (passlen > 7 && !isAsterisksOnly(request->arg(F("AP")).c_str(), 65))) strlcpy(apPass, request->arg(F("AP")).c_str(), 65); + int t = request->arg(F("AC")).toInt(); if (t > 0 && t < 14) apChannel = t; - noWifiSleep = request->hasArg("WS"); + noWifiSleep = request->hasArg(F("WS")); char k[3]; k[2] = 0; for (int i = 0; i<4; i++) @@ -71,110 +71,110 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) //LED SETTINGS if (subPage == 2) { - int t = request->arg("LC").toInt(); + int t = request->arg(F("LC")).toInt(); if (t > 0 && t <= MAX_LEDS) ledCount = t; #ifdef ESP8266 #if LEDPIN == 3 if (ledCount > MAX_LEDS_DMA) ledCount = MAX_LEDS_DMA; //DMA method uses too much ram #endif #endif - strip.ablMilliampsMax = request->arg("MA").toInt(); - strip.milliampsPerLed = request->arg("LA").toInt(); + strip.ablMilliampsMax = request->arg(F("MA")).toInt(); + strip.milliampsPerLed = request->arg(F("LA")).toInt(); - useRGBW = request->hasArg("EW"); - strip.colorOrder = request->arg("CO").toInt(); - strip.rgbwMode = request->arg("AW").toInt(); + useRGBW = request->hasArg(F("EW")); + strip.colorOrder = request->arg(F("CO")).toInt(); + strip.rgbwMode = request->arg(F("AW")).toInt(); - briS = request->arg("CA").toInt(); + briS = request->arg(F("CA")).toInt(); - saveCurrPresetCycConf = request->hasArg("PC"); - turnOnAtBoot = request->hasArg("BO"); - t = request->arg("BP").toInt(); + saveCurrPresetCycConf = request->hasArg(F("PC")); + turnOnAtBoot = request->hasArg(F("BO")); + t = request->arg(F("BP")).toInt(); if (t <= 25) bootPreset = t; - strip.gammaCorrectBri = request->hasArg("GB"); - strip.gammaCorrectCol = request->hasArg("GC"); + strip.gammaCorrectBri = request->hasArg(F("GB")); + strip.gammaCorrectCol = request->hasArg(F("GC")); - fadeTransition = request->hasArg("TF"); - t = request->arg("TD").toInt(); + fadeTransition = request->hasArg(F("TF")); + t = request->arg(F("TD")).toInt(); if (t > 0) transitionDelay = t; transitionDelayDefault = t; - strip.paletteFade = request->hasArg("PF"); + strip.paletteFade = request->hasArg(F("PF")); - nightlightTargetBri = request->arg("TB").toInt(); - t = request->arg("TL").toInt(); + nightlightTargetBri = request->arg(F("TB")).toInt(); + t = request->arg(F("TL")).toInt(); if (t > 0) nightlightDelayMinsDefault = t; nightlightDelayMins = nightlightDelayMinsDefault; - nightlightMode = request->arg("TW").toInt(); + nightlightMode = request->arg(F("TW")).toInt(); - t = request->arg("PB").toInt(); + t = request->arg(F("PB")).toInt(); if (t >= 0 && t < 4) strip.paletteBlend = t; - strip.reverseMode = request->hasArg("RV"); - skipFirstLed = request->hasArg("SL"); - t = request->arg("BF").toInt(); + strip.reverseMode = request->hasArg(F("RV")); + skipFirstLed = request->hasArg(F("SL")); + t = request->arg(F("BF")).toInt(); if (t > 0) briMultiplier = t; } //UI if (subPage == 3) { - strlcpy(serverDescription, request->arg("DS").c_str(), 33); - syncToggleReceive = request->hasArg("ST"); + strlcpy(serverDescription, request->arg(F("DS")).c_str(), 33); + syncToggleReceive = request->hasArg(F("ST")); } //SYNC if (subPage == 4) { - buttonEnabled = request->hasArg("BT"); - irEnabled = request->arg("IR").toInt(); - int t = request->arg("UP").toInt(); + buttonEnabled = request->hasArg(F("BT")); + irEnabled = request->arg(F("IR")).toInt(); + int t = request->arg(F("UP")).toInt(); if (t > 0) udpPort = t; - receiveNotificationBrightness = request->hasArg("RB"); - receiveNotificationColor = request->hasArg("RC"); - receiveNotificationEffects = request->hasArg("RX"); + receiveNotificationBrightness = request->hasArg(F("RB")); + receiveNotificationColor = request->hasArg(F("RC")); + receiveNotificationEffects = request->hasArg(F("RX")); receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects); - notifyDirectDefault = request->hasArg("SD"); + notifyDirectDefault = request->hasArg(F("SD")); notifyDirect = notifyDirectDefault; - notifyButton = request->hasArg("SB"); - notifyAlexa = request->hasArg("SA"); - notifyHue = request->hasArg("SH"); - notifyMacro = request->hasArg("SM"); - notifyTwice = request->hasArg("S2"); + notifyButton = request->hasArg(F("SB")); + notifyAlexa = request->hasArg(F("SA")); + notifyHue = request->hasArg(F("SH")); + notifyMacro = request->hasArg(F("SM")); + notifyTwice = request->hasArg(F("S2")); - receiveDirect = request->hasArg("RD"); - e131SkipOutOfSequence = request->hasArg("ES"); - e131Multicast = request->hasArg("EM"); - t = request->arg("EP").toInt(); + receiveDirect = request->hasArg(F("RD")); + e131SkipOutOfSequence = request->hasArg(F("ES")); + e131Multicast = request->hasArg(F("EM")); + t = request->arg(F("EP")).toInt(); if (t > 0) e131Port = t; - t = request->arg("EU").toInt(); + t = request->arg(F("EU")).toInt(); if (t >= 0 && t <= 63999) e131Universe = t; - t = request->arg("DA").toInt(); + t = request->arg(F("DA")).toInt(); if (t >= 0 && t <= 510) DMXAddress = t; - t = request->arg("DM").toInt(); + t = request->arg(F("DM")).toInt(); if (t >= DMX_MODE_DISABLED && t <= DMX_MODE_MULTIPLE_DRGB) DMXMode = t; - t = request->arg("ET").toInt(); + t = request->arg(F("ET")).toInt(); if (t > 99 && t <= 65000) realtimeTimeoutMs = t; - arlsForceMaxBri = request->hasArg("FB"); - arlsDisableGammaCorrection = request->hasArg("RG"); - t = request->arg("WO").toInt(); + arlsForceMaxBri = request->hasArg(F("FB")); + arlsDisableGammaCorrection = request->hasArg(F("RG")); + t = request->arg(F("WO")).toInt(); if (t >= -255 && t <= 255) arlsOffset = t; - alexaEnabled = request->hasArg("AL"); - strlcpy(alexaInvocationName, request->arg("AI").c_str(), 33); + alexaEnabled = request->hasArg(F("AL")); + strlcpy(alexaInvocationName, request->arg(F("AI")).c_str(), 33); - if (request->hasArg("BK") && !request->arg("BK").equals("Hidden")) { + if (request->hasArg("BK") && !request->arg("BK").equals(F("Hidden"))) { strlcpy(blynkApiKey, request->arg("BK").c_str(), 36); initBlynk(blynkApiKey); } #ifdef WLED_ENABLE_MQTT - mqttEnabled = request->hasArg("MQ"); - strlcpy(mqttServer, request->arg("MS").c_str(), 33); - t = request->arg("MQPORT").toInt(); + mqttEnabled = request->hasArg(F("MQ")); + strlcpy(mqttServer, request->arg(F("MS")).c_str(), 33); + t = request->arg(F("MQPORT")).toInt(); if (t > 0) mqttPort = t; - strlcpy(mqttUser, request->arg("MQUSER").c_str(), 41); - if (!isAsterisksOnly(request->arg("MQPASS").c_str(), 41)) strlcpy(mqttPass, request->arg("MQPASS").c_str(), 41); - strlcpy(mqttClientID, request->arg("MQCID").c_str(), 41); - strlcpy(mqttDeviceTopic, request->arg("MD").c_str(), 33); - strlcpy(mqttGroupTopic, request->arg("MG").c_str(), 33); + strlcpy(mqttUser, request->arg(F("MQUSER")).c_str(), 41); + if (!isAsterisksOnly(request->arg(F("MQPASS")).c_str(), 41)) strlcpy(mqttPass, request->arg(F("MQPASS")).c_str(), 41); + strlcpy(mqttClientID, request->arg(F("MQCID")).c_str(), 41); + strlcpy(mqttDeviceTopic, request->arg(F("MD")).c_str(), 33); + strlcpy(mqttGroupTopic, request->arg(F("MG")).c_str(), 33); #endif #ifndef WLED_DISABLE_HUESYNC @@ -183,16 +183,16 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) hueIP[i] = request->arg(a).toInt(); } - t = request->arg("HL").toInt(); + t = request->arg(F("HL")).toInt(); if (t > 0) huePollLightId = t; - t = request->arg("HI").toInt(); + t = request->arg(F("HI")).toInt(); if (t > 50) huePollIntervalMs = t; - hueApplyOnOff = request->hasArg("HO"); - hueApplyBri = request->hasArg("HB"); - hueApplyColor = request->hasArg("HC"); - huePollingEnabled = request->hasArg("HP"); + hueApplyOnOff = request->hasArg(F("HO")); + hueApplyBri = request->hasArg(F("HB")); + hueApplyColor = request->hasArg(F("HC")); + huePollingEnabled = request->hasArg(F("HP")); hueStoreAllowed = true; reconnectHue(); #endif @@ -201,35 +201,35 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) //TIME if (subPage == 5) { - ntpEnabled = request->hasArg("NT"); - strlcpy(ntpServerName, request->arg("NS").c_str(), 33); - useAMPM = !request->hasArg("CF"); - currentTimezone = request->arg("TZ").toInt(); - utcOffsetSecs = request->arg("UO").toInt(); + ntpEnabled = request->hasArg(F("NT")); + strlcpy(ntpServerName, request->arg(F("NS")).c_str(), 33); + useAMPM = !request->hasArg(F("CF")); + currentTimezone = request->arg(F("TZ")).toInt(); + utcOffsetSecs = request->arg(F("UO")).toInt(); //start ntp if not already connected if (ntpEnabled && WLED_CONNECTED && !ntpConnected) ntpConnected = ntpUdp.begin(ntpLocalPort); - if (request->hasArg("OL")){ - overlayDefault = request->arg("OL").toInt(); + if (request->hasArg(F("OL"))) { + overlayDefault = request->arg(F("OL")).toInt(); overlayCurrent = overlayDefault; } - overlayMin = request->arg("O1").toInt(); - overlayMax = request->arg("O2").toInt(); - analogClock12pixel = request->arg("OM").toInt(); - analogClock5MinuteMarks = request->hasArg("O5"); - analogClockSecondsTrail = request->hasArg("OS"); + overlayMin = request->arg(F("O1")).toInt(); + overlayMax = request->arg(F("O2")).toInt(); + analogClock12pixel = request->arg(F("OM")).toInt(); + analogClock5MinuteMarks = request->hasArg(F("O5")); + analogClockSecondsTrail = request->hasArg(F("OS")); - strcpy(cronixieDisplay,request->arg("CX").c_str()); - cronixieBacklight = request->hasArg("CB"); - countdownMode = request->hasArg("CE"); - countdownYear = request->arg("CY").toInt(); - countdownMonth = request->arg("CI").toInt(); - countdownDay = request->arg("CD").toInt(); - countdownHour = request->arg("CH").toInt(); - countdownMin = request->arg("CM").toInt(); - countdownSec = request->arg("CS").toInt(); + strcpy(cronixieDisplay,request->arg(F("CX")).c_str()); + cronixieBacklight = request->hasArg(F("CB")); + countdownMode = request->hasArg(F("CE")); + countdownYear = request->arg(F("CY")).toInt(); + countdownMonth = request->arg(F("CI")).toInt(); + countdownDay = request->arg(F("CD")).toInt(); + countdownHour = request->arg(F("CH")).toInt(); + countdownMin = request->arg(F("CM")).toInt(); + countdownSec = request->arg(F("CS")).toInt(); for (int i=1;i<17;i++) { @@ -237,14 +237,14 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) if (request->hasArg(a.c_str())) saveMacro(i,request->arg(a),false); } - macroBoot = request->arg("MB").toInt(); - macroAlexaOn = request->arg("A0").toInt(); - macroAlexaOff = request->arg("A1").toInt(); - macroButton = request->arg("MP").toInt(); - macroLongPress = request->arg("ML").toInt(); - macroCountdown = request->arg("MC").toInt(); - macroNl = request->arg("MN").toInt(); - macroDoublePress = request->arg("MD").toInt(); + macroBoot = request->arg(F("MB")).toInt(); + macroAlexaOn = request->arg(F("A0")).toInt(); + macroAlexaOff = request->arg(F("A1")).toInt(); + macroButton = request->arg(F("MP")).toInt(); + macroLongPress = request->arg(F("ML")).toInt(); + macroCountdown = request->arg(F("MC")).toInt(); + macroNl = request->arg(F("MN")).toInt(); + macroDoublePress = request->arg(F("MD")).toInt(); char k[3]; k[2] = 0; for (int i = 0; i<8; i++) @@ -268,52 +268,52 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) //SECURITY if (subPage == 6) { - if (request->hasArg("RS")) //complete factory reset + if (request->hasArg(F("RS"))) //complete factory reset { clearEEPROM(); - serveMessage(request, 200, "All Settings erased.", "Connect to WLED-AP to setup again",255); + serveMessage(request, 200, F("All Settings erased."), F("Connect to WLED-AP to setup again"),255); doReboot = true; } bool pwdCorrect = !otaLock; //always allow access if ota not locked - if (request->hasArg("OP")) + if (request->hasArg(F("OP"))) { - if (otaLock && strcmp(otaPass,request->arg("OP").c_str()) == 0) + if (otaLock && strcmp(otaPass,request->arg(F("OP")).c_str()) == 0) { pwdCorrect = true; } - if (!otaLock && request->arg("OP").length() > 0) + if (!otaLock && request->arg(F("OP")).length() > 0) { - strlcpy(otaPass,request->arg("OP").c_str(), 33); + strlcpy(otaPass,request->arg(F("OP")).c_str(), 33); } } if (pwdCorrect) //allow changes if correct pwd or no ota active { - otaLock = request->hasArg("NO"); - wifiLock = request->hasArg("OW"); - aOtaEnabled = request->hasArg("AO"); + otaLock = request->hasArg(F("NO")); + wifiLock = request->hasArg(F("OW")); + aOtaEnabled = request->hasArg(F("AO")); } } #ifdef WLED_ENABLE_DMX // include only if DMX is enabled if (subPage == 7) { - int t = request->arg("PU").toInt(); + int t = request->arg(F("PU")).toInt(); if (t >= 0 && t <= 63999) e131ProxyUniverse = t; - t = request->arg("CN").toInt(); + t = request->arg(F("CN")).toInt(); if (t>0 && t<16) { DMXChannels = t; } - t = request->arg("CS").toInt(); + t = request->arg(F("CS")).toInt(); if (t>0 && t<513) { DMXStart = t; } - t = request->arg("CG").toInt(); + t = request->arg(F("CG")).toInt(); if (t>0 && t<513) { DMXGap = t; } - t = request->arg("SL").toInt(); + t = request->arg(F("SL")).toInt(); if (t>=0 && t < MAX_LEDS) { DMXStartLED = t; } @@ -377,18 +377,18 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) if (!(req.indexOf("win") >= 0)) return false; int pos = 0; - DEBUG_PRINT("API req: "); + DEBUG_PRINT(F("API req: ")); DEBUG_PRINTLN(req); //write presets and macros saved to flash directly? bool persistSaves = true; - pos = req.indexOf("NP"); + pos = req.indexOf(F("NP")); if (pos > 0) { persistSaves = false; } //save macro, requires &MS=() format - pos = req.indexOf("&MS="); + pos = req.indexOf(F("&MS=")); if (pos > 0) { int i = req.substring(pos + 4).toInt(); pos = req.indexOf('(') +1; @@ -399,7 +399,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) saveMacro(i, mc, persistSaves); } - pos = req.indexOf("IN"); + pos = req.indexOf(F("IN")); if (pos < 1) XML_response(request); return true; //if you save a macro in one request, other commands in that request are ignored due to unwanted behavior otherwise @@ -409,21 +409,21 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) //segment select (sets main segment) byte prevMain = strip.getMainSegmentId(); - pos = req.indexOf("SM="); + pos = req.indexOf(F("SM=")); if (pos > 0) { strip.mainSegment = getNumVal(&req, pos); } byte main = strip.getMainSegmentId(); if (main != prevMain) setValuesFromMainSeg(); - pos = req.indexOf("SS="); + pos = req.indexOf(F("SS=")); if (pos > 0) { byte t = getNumVal(&req, pos); if (t < strip.getMaxSegments()) main = t; } WS2812FX::Segment& mainseg = strip.getSegment(main); - pos = req.indexOf("SV="); //segment selected + pos = req.indexOf(F("SV=")); //segment selected if (pos > 0) { byte t = getNumVal(&req, pos); if (t == 2) { @@ -439,20 +439,20 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) uint16_t stopI = mainseg.stop; uint8_t grpI = mainseg.grouping; uint16_t spcI = mainseg.spacing; - pos = req.indexOf("&S="); //segment start + pos = req.indexOf(F("&S=")); //segment start if (pos > 0) { startI = getNumVal(&req, pos); } - pos = req.indexOf("S2="); //segment stop + pos = req.indexOf(F("S2=")); //segment stop if (pos > 0) { stopI = getNumVal(&req, pos); } - pos = req.indexOf("GP="); //segment grouping + pos = req.indexOf(F("GP=")); //segment grouping if (pos > 0) { grpI = getNumVal(&req, pos); if (grpI == 0) grpI = 1; } - pos = req.indexOf("SP="); //segment spacing + pos = req.indexOf(F("SP=")); //segment spacing if (pos > 0) { spcI = getNumVal(&req, pos); } @@ -461,30 +461,30 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) main = strip.getMainSegmentId(); //set presets - pos = req.indexOf("P1="); //sets first preset for cycle + pos = req.indexOf(F("P1=")); //sets first preset for cycle if (pos > 0) presetCycleMin = getNumVal(&req, pos); - pos = req.indexOf("P2="); //sets last preset for cycle + pos = req.indexOf(F("P2=")); //sets last preset for cycle if (pos > 0) presetCycleMax = getNumVal(&req, pos); //preset cycle - pos = req.indexOf("CY="); + pos = req.indexOf(F("CY=")); if (pos > 0) { presetCyclingEnabled = (req.charAt(pos+3) != '0'); presetCycCurr = presetCycleMin; } - pos = req.indexOf("PT="); //sets cycle time in ms + pos = req.indexOf(F("PT=")); //sets cycle time in ms if (pos > 0) { int v = getNumVal(&req, pos); if (v > 100) presetCycleTime = v/100; } - pos = req.indexOf("PA="); //apply brightness from preset + pos = req.indexOf(F("PA=")); //apply brightness from preset if (pos > 0) presetApplyBri = (req.charAt(pos+3) != '0'); - pos = req.indexOf("PS="); //saves current in preset + pos = req.indexOf(F("PS=")); //saves current in preset if (pos > 0) savePreset(getNumVal(&req, pos), persistSaves); //apply preset @@ -506,27 +506,27 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) updateVal(&req, "W2=", &colSec[3]); //set hue - pos = req.indexOf("HU="); + pos = req.indexOf(F("HU=")); if (pos > 0) { uint16_t temphue = getNumVal(&req, pos); byte tempsat = 255; - pos = req.indexOf("SA="); + pos = req.indexOf(F("SA=")); if (pos > 0) { tempsat = getNumVal(&req, pos); } - colorHStoRGB(temphue,tempsat,(req.indexOf("H2")>0)? colSec:col); + colorHStoRGB(temphue,tempsat,(req.indexOf(F("H2"))>0)? colSec:col); } //set color from HEX or 32bit DEC - pos = req.indexOf("CL="); + pos = req.indexOf(F("CL=")); if (pos > 0) { colorFromDecOrHexString(col, (char*)req.substring(pos + 3).c_str()); } - pos = req.indexOf("C2="); + pos = req.indexOf(F("C2=")); if (pos > 0) { colorFromDecOrHexString(colSec, (char*)req.substring(pos + 3).c_str()); } - pos = req.indexOf("C3="); + pos = req.indexOf(F("C3=")); if (pos > 0) { byte t[4]; colorFromDecOrHexString(t, (char*)req.substring(pos + 3).c_str()); @@ -534,13 +534,13 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) } //set to random hue SR=0->1st SR=1->2nd - pos = req.indexOf("SR"); + pos = req.indexOf(F("SR")); if (pos > 0) { _setRandomColor(getNumVal(&req, pos)); } //swap 2nd & 1st - pos = req.indexOf("SC"); + pos = req.indexOf(F("SC")); if (pos > 0) { byte temp; for (uint8_t i=0; i<4; i++) @@ -558,33 +558,33 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) updateVal(&req, "FP=", &effectPalette, 0, strip.getPaletteCount()-1); //set advanced overlay - pos = req.indexOf("OL="); + pos = req.indexOf(F("OL=")); if (pos > 0) { overlayCurrent = getNumVal(&req, pos); } //apply macro - pos = req.indexOf("&M="); + pos = req.indexOf(F("&M=")); if (pos > 0) { applyMacro(getNumVal(&req, pos)); } //toggle send UDP direct notifications - pos = req.indexOf("SN="); + pos = req.indexOf(F("SN=")); if (pos > 0) notifyDirect = (req.charAt(pos+3) != '0'); //toggle receive UDP direct notifications - pos = req.indexOf("RN="); + pos = req.indexOf(F("RN=")); if (pos > 0) receiveNotifications = (req.charAt(pos+3) != '0'); //receive live data via UDP/Hyperion - pos = req.indexOf("RD="); + pos = req.indexOf(F("RD=")); if (pos > 0) receiveDirect = (req.charAt(pos+3) != '0'); //toggle nightlight mode bool aNlDef = false; - if (req.indexOf("&ND") > 0) aNlDef = true; - pos = req.indexOf("NL="); + if (req.indexOf(F("&ND")) > 0) aNlDef = true; + pos = req.indexOf(F("NL=")); if (pos > 0) { if (req.charAt(pos+3) == '0') @@ -603,14 +603,14 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) } //set nightlight target brightness - pos = req.indexOf("NT="); + pos = req.indexOf(F("NT=")); if (pos > 0) { nightlightTargetBri = getNumVal(&req, pos); nightlightActiveOld = false; //re-init } //toggle nightlight fade - pos = req.indexOf("NF="); + pos = req.indexOf(F("NF=")); if (pos > 0) { nightlightMode = getNumVal(&req, pos); @@ -621,7 +621,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) #if AUXPIN >= 0 //toggle general purpose output - pos = req.indexOf("AX="); + pos = req.indexOf(F("AX=")); if (pos > 0) { auxTime = getNumVal(&req, pos); auxActive = true; @@ -629,11 +629,11 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) } #endif - pos = req.indexOf("TT="); + pos = req.indexOf(F("TT=")); if (pos > 0) transitionDelay = getNumVal(&req, pos); //main toggle on/off - pos = req.indexOf("&T="); + pos = req.indexOf(F("&T=")); if (pos > 0) { nightlightActive = false; //always disable nightlight when toggling switch (getNumVal(&req, pos)) @@ -645,15 +645,15 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) } //Segment reverse - pos = req.indexOf("RV="); + pos = req.indexOf(F("RV=")); if (pos > 0) strip.getSegment(main).setOption(SEG_OPTION_REVERSED, req.charAt(pos+3) != '0'); //Segment reverse - pos = req.indexOf("MI="); + pos = req.indexOf(F("MI=")); if (pos > 0) strip.getSegment(main).setOption(SEG_OPTION_MIRROR, req.charAt(pos+3) != '0'); //Segment brightness/opacity - pos = req.indexOf("SB="); + pos = req.indexOf(F("SB=")); if (pos > 0) { byte segbri = getNumVal(&req, pos); strip.getSegment(main).setOption(SEG_OPTION_ON, segbri); @@ -663,40 +663,40 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) } //set time (unix timestamp) - pos = req.indexOf("ST="); + pos = req.indexOf(F("ST=")); if (pos > 0) { setTime(getNumVal(&req, pos)); } //set countdown goal (unix timestamp) - pos = req.indexOf("CT="); + pos = req.indexOf(F("CT=")); if (pos > 0) { countdownTime = getNumVal(&req, pos); if (countdownTime - now() > 0) countdownOverTriggered = false; } - pos = req.indexOf("LO="); + pos = req.indexOf(F("LO=")); if (pos > 0) { realtimeOverride = getNumVal(&req, pos); if (realtimeOverride > 2) realtimeOverride = REALTIME_OVERRIDE_ALWAYS; } - pos = req.indexOf("RB"); + pos = req.indexOf(F("RB")); if (pos > 0) doReboot = true; //cronixie #ifndef WLED_DISABLE_CRONIXIE //mode, 1 countdown - pos = req.indexOf("NM="); + pos = req.indexOf(F("NM=")); if (pos > 0) countdownMode = (req.charAt(pos+3) != '0'); - pos = req.indexOf("NX="); //sets digits to code + pos = req.indexOf(F("NX=")); //sets digits to code if (pos > 0) { strlcpy(cronixieDisplay, req.substring(pos + 3, pos + 9).c_str(), 6); setCronixie(); } - pos = req.indexOf("NB="); + pos = req.indexOf(F("NB=")); if (pos > 0) //sets backlight { cronixieBacklight = (req.charAt(pos+3) != '0'); @@ -704,22 +704,22 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) } #endif - pos = req.indexOf("U0="); //user var 0 + pos = req.indexOf(F("U0=")); //user var 0 if (pos > 0) { userVar0 = getNumVal(&req, pos); } - pos = req.indexOf("U1="); //user var 1 + pos = req.indexOf(F("U1=")); //user var 1 if (pos > 0) { userVar1 = getNumVal(&req, pos); } //you can add more if you need //internal call, does not send XML response - pos = req.indexOf("IN"); + pos = req.indexOf(F("IN")); if (pos < 1) XML_response(request); - pos = req.indexOf("&NN"); //do not send UDP notifications this time + pos = req.indexOf(F("&NN")); //do not send UDP notifications this time colorUpdated((pos > 0) ? NOTIFIER_CALL_MODE_NO_NOTIFY : NOTIFIER_CALL_MODE_DIRECT_CHANGE); return true; diff --git a/wled00/src/dependencies/json/ArduinoJson-v6.h b/wled00/src/dependencies/json/ArduinoJson-v6.h index 1cd94f604..5d8d40799 100644 --- a/wled00/src/dependencies/json/ArduinoJson-v6.h +++ b/wled00/src/dependencies/json/ArduinoJson-v6.h @@ -1,42 +1,53 @@ // ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2019 +// Copyright Benoit Blanchon 2014-2020 // MIT License #pragma once #ifdef __cplusplus -#ifndef ARDUINOJSON_DEBUG -#ifdef __clang__ -#pragma clang system_header -#elif defined __GNUC__ -#pragma GCC system_header +#if __cplusplus >= 201103L +#define ARDUINOJSON_HAS_LONG_LONG 1 +#define ARDUINOJSON_HAS_NULLPTR 1 +#define ARDUINOJSON_HAS_RVALUE_REFERENCES 1 +#else +#define ARDUINOJSON_HAS_LONG_LONG 0 +#define ARDUINOJSON_HAS_NULLPTR 0 +#define ARDUINOJSON_HAS_RVALUE_REFERENCES 0 #endif -#endif -#define ARDUINOJSON_VERSION "6.11.0" -#define ARDUINOJSON_VERSION_MAJOR 6 -#define ARDUINOJSON_VERSION_MINOR 11 -#define ARDUINOJSON_VERSION_REVISION 0 -#if defined(_MSC_VER) +#if defined(_MSC_VER) && !ARDUINOJSON_HAS_LONG_LONG #define ARDUINOJSON_HAS_INT64 1 #else #define ARDUINOJSON_HAS_INT64 0 #endif -#if __cplusplus >= 201103L -#define ARDUINOJSON_HAS_LONG_LONG 1 -#define ARDUINOJSON_HAS_NULLPTR 1 -#else -#define ARDUINOJSON_HAS_LONG_LONG 0 -#define ARDUINOJSON_HAS_NULLPTR 0 -#endif #ifndef ARDUINOJSON_EMBEDDED_MODE -#if defined(ARDUINO) || defined(__IAR_SYSTEMS_ICC__) || defined(__XC) || \ - defined(__ARMCC_VERSION) +#if defined(ARDUINO) /* Arduino*/ \ + || defined(__IAR_SYSTEMS_ICC__) /* IAR Embedded Workbench */ \ + || defined(__XC) /* MPLAB XC compiler */ \ + || defined(__ARMCC_VERSION) /* Keil ARM Compiler */ \ + || defined(__AVR) /* Atmel AVR8/GNU C Compiler */ #define ARDUINOJSON_EMBEDDED_MODE 1 #else #define ARDUINOJSON_EMBEDDED_MODE 0 #endif #endif +#if !defined(ARDUINOJSON_ENABLE_STD_STREAM) && defined(__has_include) +#if __has_include() && \ + __has_include() && \ + !defined(min) && \ + !defined(max) +#define ARDUINOJSON_ENABLE_STD_STREAM 1 +#else +#define ARDUINOJSON_ENABLE_STD_STREAM 0 +#endif +#endif +#if !defined(ARDUINOJSON_ENABLE_STD_STRING) && defined(__has_include) +#if __has_include() && !defined(min) && !defined(max) +#define ARDUINOJSON_ENABLE_STD_STRING 1 +#else +#define ARDUINOJSON_ENABLE_STD_STRING 0 +#endif +#endif #if ARDUINOJSON_EMBEDDED_MODE #ifndef ARDUINOJSON_USE_DOUBLE #define ARDUINOJSON_USE_DOUBLE 0 @@ -75,6 +86,7 @@ #endif #endif // ARDUINOJSON_EMBEDDED_MODE #ifdef ARDUINO +#include #ifndef ARDUINOJSON_ENABLE_ARDUINO_STRING #define ARDUINOJSON_ENABLE_ARDUINO_STRING 1 #endif @@ -103,7 +115,10 @@ #endif #endif #ifndef ARDUINOJSON_DECODE_UNICODE -#define ARDUINOJSON_DECODE_UNICODE 0 +#define ARDUINOJSON_DECODE_UNICODE 1 +#endif +#ifndef ARDUINOJSON_ENABLE_COMMENTS +#define ARDUINOJSON_ENABLE_COMMENTS 0 #endif #ifndef ARDUINOJSON_ENABLE_NAN #define ARDUINOJSON_ENABLE_NAN 0 @@ -126,34 +141,186 @@ #define ARDUINOJSON_LITTLE_ENDIAN 0 #endif #endif +#ifndef ARDUINOJSON_ENABLE_ALIGNMENT +#if defined(__AVR) +#define ARDUINOJSON_ENABLE_ALIGNMENT 0 +#else +#define ARDUINOJSON_ENABLE_ALIGNMENT 1 +#endif +#endif #ifndef ARDUINOJSON_TAB #define ARDUINOJSON_TAB " " #endif -#define ARDUINOJSON_DO_CONCAT(A, B) A##B -#define ARDUINOJSON_CONCAT2(A, B) ARDUINOJSON_DO_CONCAT(A, B) -#define ARDUINOJSON_CONCAT3(A, B, C) \ - ARDUINOJSON_CONCAT2(A, ARDUINOJSON_CONCAT2(B, C)) +#ifndef ARDUINOJSON_ENABLE_STRING_DEDUPLICATION +#define ARDUINOJSON_ENABLE_STRING_DEDUPLICATION 1 +#endif +#ifndef ARDUINOJSON_STRING_BUFFER_SIZE +#define ARDUINOJSON_STRING_BUFFER_SIZE 32 +#endif +#ifndef ARDUINOJSON_DEBUG +#ifdef __PLATFORMIO_BUILD_DEBUG__ +#define ARDUINOJSON_DEBUG 1 +#else +#define ARDUINOJSON_DEBUG 0 +#endif +#endif +#if !ARDUINOJSON_DEBUG +#ifdef __clang__ +#pragma clang system_header +#elif defined __GNUC__ +#pragma GCC system_header +#endif +#endif +#define ARDUINOJSON_VERSION "6.16.1" +#define ARDUINOJSON_VERSION_MAJOR 6 +#define ARDUINOJSON_VERSION_MINOR 16 +#define ARDUINOJSON_VERSION_REVISION 1 +#ifndef ARDUINOJSON_NAMESPACE +#define ARDUINOJSON_HEX_DIGIT_0000() 0 +#define ARDUINOJSON_HEX_DIGIT_0001() 1 +#define ARDUINOJSON_HEX_DIGIT_0010() 2 +#define ARDUINOJSON_HEX_DIGIT_0011() 3 +#define ARDUINOJSON_HEX_DIGIT_0100() 4 +#define ARDUINOJSON_HEX_DIGIT_0101() 5 +#define ARDUINOJSON_HEX_DIGIT_0110() 6 +#define ARDUINOJSON_HEX_DIGIT_0111() 7 +#define ARDUINOJSON_HEX_DIGIT_1000() 8 +#define ARDUINOJSON_HEX_DIGIT_1001() 9 +#define ARDUINOJSON_HEX_DIGIT_1010() A +#define ARDUINOJSON_HEX_DIGIT_1011() B +#define ARDUINOJSON_HEX_DIGIT_1100() C +#define ARDUINOJSON_HEX_DIGIT_1101() D +#define ARDUINOJSON_HEX_DIGIT_1110() E +#define ARDUINOJSON_HEX_DIGIT_1111() F +#define ARDUINOJSON_HEX_DIGIT_(A, B, C, D) ARDUINOJSON_HEX_DIGIT_##A##B##C##D() +#define ARDUINOJSON_HEX_DIGIT(A, B, C, D) ARDUINOJSON_HEX_DIGIT_(A, B, C, D) +#define ARDUINOJSON_CONCAT_(A, B) A##B +#define ARDUINOJSON_CONCAT2(A, B) ARDUINOJSON_CONCAT_(A, B) #define ARDUINOJSON_CONCAT4(A, B, C, D) \ ARDUINOJSON_CONCAT2(ARDUINOJSON_CONCAT2(A, B), ARDUINOJSON_CONCAT2(C, D)) -#define ARDUINOJSON_CONCAT8(A, B, C, D, E, F, G, H) \ - ARDUINOJSON_CONCAT2(ARDUINOJSON_CONCAT4(A, B, C, D), \ - ARDUINOJSON_CONCAT4(E, F, G, H)) -#define ARDUINOJSON_CONCAT10(A, B, C, D, E, F, G, H, I, J) \ - ARDUINOJSON_CONCAT8(A, B, C, D, E, F, G, ARDUINOJSON_CONCAT3(H, I, J)) -#define ARDUINOJSON_NAMESPACE \ - ARDUINOJSON_CONCAT10( \ - ArduinoJson, ARDUINOJSON_VERSION_MAJOR, ARDUINOJSON_VERSION_MINOR, \ - ARDUINOJSON_VERSION_REVISION, _, ARDUINOJSON_USE_LONG_LONG, \ - ARDUINOJSON_USE_DOUBLE, ARDUINOJSON_DECODE_UNICODE, \ - ARDUINOJSON_ENABLE_NAN, ARDUINOJSON_ENABLE_INFINITY) -#ifdef ARDUINOJSON_DEBUG +#define ARDUINOJSON_NAMESPACE \ + ARDUINOJSON_CONCAT4( \ + ARDUINOJSON_CONCAT4(ArduinoJson, ARDUINOJSON_VERSION_MAJOR, \ + ARDUINOJSON_VERSION_MINOR, \ + ARDUINOJSON_VERSION_REVISION), \ + _, \ + ARDUINOJSON_HEX_DIGIT(0, ARDUINOJSON_USE_LONG_LONG, \ + ARDUINOJSON_USE_DOUBLE, \ + ARDUINOJSON_ENABLE_STRING_DEDUPLICATION), \ + ARDUINOJSON_HEX_DIGIT( \ + ARDUINOJSON_ENABLE_NAN, ARDUINOJSON_ENABLE_INFINITY, \ + ARDUINOJSON_ENABLE_COMMENTS, ARDUINOJSON_DECODE_UNICODE)) +#endif +#if ARDUINOJSON_DEBUG #include #define ARDUINOJSON_ASSERT(X) assert(X) #else #define ARDUINOJSON_ASSERT(X) ((void)0) #endif -#include // for size_t +#include namespace ARDUINOJSON_NAMESPACE { +class MemoryPool; +class VariantData; +class VariantSlot; +class CollectionData { + VariantSlot *_head; + VariantSlot *_tail; + public: + VariantData *addElement(MemoryPool *pool); + VariantData *getElement(size_t index) const; + VariantData *getOrAddElement(size_t index, MemoryPool *pool); + void removeElement(size_t index); + bool equalsArray(const CollectionData &other) const; + template + VariantData *addMember(TAdaptedString key, MemoryPool *pool); + template + VariantData *getMember(TAdaptedString key) const; + template + VariantData *getOrAddMember(TAdaptedString key, MemoryPool *pool); + template + void removeMember(TAdaptedString key) { + removeSlot(getSlot(key)); + } + template + bool containsKey(const TAdaptedString &key) const; + bool equalsObject(const CollectionData &other) const; + void clear(); + size_t memoryUsage() const; + size_t nesting() const; + size_t size() const; + VariantSlot *addSlot(MemoryPool *); + void removeSlot(VariantSlot *slot); + bool copyFrom(const CollectionData &src, MemoryPool *pool); + VariantSlot *head() const { + return _head; + } + void movePointers(ptrdiff_t stringDistance, ptrdiff_t variantDistance); + private: + VariantSlot *getSlot(size_t index) const; + template + VariantSlot *getSlot(TAdaptedString key) const; + VariantSlot *getPreviousSlot(VariantSlot *) const; +}; +inline VariantData *arrayAdd(CollectionData *arr, MemoryPool *pool) { + return arr ? arr->addElement(pool) : 0; +} +template +inline void arrayAccept(const CollectionData *arr, Visitor &visitor) { + if (arr) + visitor.visitArray(*arr); + else + visitor.visitNull(); +} +inline bool arrayEquals(const CollectionData *lhs, const CollectionData *rhs) { + if (lhs == rhs) + return true; + if (!lhs || !rhs) + return false; + return lhs->equalsArray(*rhs); +} +#if ARDUINOJSON_ENABLE_ALIGNMENT +inline bool isAligned(size_t value) { + const size_t mask = sizeof(void *) - 1; + size_t addr = value; + return (addr & mask) == 0; +} +inline size_t addPadding(size_t bytes) { + const size_t mask = sizeof(void *) - 1; + return (bytes + mask) & ~mask; +} +template +struct AddPadding { + static const size_t mask = sizeof(void *) - 1; + static const size_t value = (bytes + mask) & ~mask; +}; +#else +inline bool isAligned(size_t) { + return true; +} +inline size_t addPadding(size_t bytes) { + return bytes; +} +template +struct AddPadding { + static const size_t value = bytes; +}; +#endif +template +inline bool isAligned(T *ptr) { + return isAligned(reinterpret_cast(ptr)); +} +template +inline T *addPadding(T *p) { + size_t address = addPadding(reinterpret_cast(p)); + return reinterpret_cast(address); +} +} // namespace ARDUINOJSON_NAMESPACE +#define JSON_STRING_SIZE(SIZE) (SIZE + 1) +namespace ARDUINOJSON_NAMESPACE { +struct StringSlot { + char *value; + size_t size; +}; template Y)> struct Max {}; template @@ -165,6 +332,7 @@ struct Max { static const size_t value = Y; }; } // namespace ARDUINOJSON_NAMESPACE +#include namespace ARDUINOJSON_NAMESPACE { template class not_null { @@ -184,8 +352,6 @@ not_null make_not_null(T ptr) { ARDUINOJSON_ASSERT(ptr != NULL); return not_null(ptr); } -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { template struct conditional { typedef TrueType type; @@ -194,32 +360,24 @@ template struct conditional { typedef FalseType type; }; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { template struct enable_if {}; template struct enable_if { typedef T type; }; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { template struct integral_constant { static const T value = v; }; typedef integral_constant true_type; typedef integral_constant false_type; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { template struct is_array : false_type {}; template struct is_array : true_type {}; template struct is_array : true_type {}; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { template class is_base_of { protected: // <- to avoid GCC's "all member functions in class are private" @@ -231,13 +389,50 @@ class is_base_of { static const bool value = sizeof(probe(reinterpret_cast(0))) == sizeof(Yes); }; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { +template +T declval(); +template +struct is_class { + protected: // <- to avoid GCC's "all member functions in class are private" + typedef char Yes[1]; + typedef char No[2]; + template + static Yes &probe(void (U::*)(void)); + template + static No &probe(...); + public: + static const bool value = sizeof(probe(0)) == sizeof(Yes); +}; template struct is_const : false_type {}; template struct is_const : true_type {}; } // namespace ARDUINOJSON_NAMESPACE +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4244) +#endif +#ifdef __ICCARM__ +#pragma diag_suppress=Pa093 +#endif +namespace ARDUINOJSON_NAMESPACE { +template +struct is_convertible { + protected: // <- to avoid GCC's "all member functions in class are private" + typedef char Yes[1]; + typedef char No[2]; + static Yes &probe(To); + static No &probe(...); + public: + static const bool value = sizeof(probe(declval())) == sizeof(Yes); +}; +} // namespace ARDUINOJSON_NAMESPACE +#ifdef _MSC_VER +#pragma warning(pop) +#endif +#ifdef __ICCARM__ +#pragma diag_default=Pa093 +#endif namespace ARDUINOJSON_NAMESPACE { template struct is_floating_point : false_type {}; @@ -245,14 +440,10 @@ template <> struct is_floating_point : true_type {}; template <> struct is_floating_point : true_type {}; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { template struct is_same : false_type {}; template struct is_same : true_type {}; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { template struct is_integral { static const bool value = @@ -268,12 +459,16 @@ struct is_integral { is_same::value || is_same::value || #endif - is_same::value; + is_same::value || is_same::value; }; template struct is_integral : is_integral {}; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { +template +struct is_enum { + static const bool value = is_convertible::value && + !is_class::value && !is_integral::value && + !is_floating_point::value; +}; template struct is_signed : false_type {}; template <> @@ -298,8 +493,6 @@ struct is_signed : true_type {}; template <> struct is_signed : true_type {}; #endif -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { template struct is_unsigned : false_type {}; template <> @@ -320,14 +513,10 @@ struct is_unsigned : true_type {}; template <> struct is_unsigned : true_type {}; #endif -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { template struct type_identity { typedef T type; }; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { template struct make_unsigned; template <> @@ -360,8 +549,6 @@ struct make_unsigned : type_identity {}; template <> struct make_unsigned : type_identity {}; #endif -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { template struct remove_const { typedef T type; @@ -370,8 +557,6 @@ template struct remove_const { typedef T type; }; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { template struct remove_reference { typedef T type; @@ -380,56 +565,16 @@ template struct remove_reference { typedef T type; }; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { -class MemoryPool; -class VariantData; -class VariantSlot; -class CollectionData { - VariantSlot *_head; - VariantSlot *_tail; - public: - VariantSlot *addSlot(MemoryPool *); - VariantData *add(MemoryPool *pool); - template - VariantData *add(TAdaptedString key, MemoryPool *pool); - void clear(); - template - bool containsKey(const TAdaptedString &key) const; - bool copyFrom(const CollectionData &src, MemoryPool *pool); - bool equalsArray(const CollectionData &other) const; - bool equalsObject(const CollectionData &other) const; - VariantData *get(size_t index) const; - template - VariantData *get(TAdaptedString key) const; - VariantSlot *head() const { - return _head; - } - void remove(size_t index); - template - void remove(TAdaptedString key) { - remove(getSlot(key)); - } - void remove(VariantSlot *slot); - size_t memoryUsage() const; - size_t nesting() const; - size_t size() const; - private: - VariantSlot *getSlot(size_t index) const; - template - VariantSlot *getSlot(TAdaptedString key) const; - VariantSlot *getPreviousSlot(VariantSlot *) const; -}; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { +namespace storage_policies { +struct store_by_address {}; +struct store_by_copy {}; +struct decide_at_runtime {}; +} // namespace storage_policies #if ARDUINOJSON_USE_DOUBLE typedef double Float; #else typedef float Float; #endif -} // namespace ARDUINOJSON_NAMESPACE -#include // int64_t -namespace ARDUINOJSON_NAMESPACE { #if ARDUINOJSON_USE_LONG_LONG typedef int64_t Integer; typedef uint64_t UInt; @@ -438,18 +583,28 @@ typedef long Integer; typedef unsigned long UInt; #endif } // namespace ARDUINOJSON_NAMESPACE +#if ARDUINOJSON_HAS_LONG_LONG && !ARDUINOJSON_USE_LONG_LONG +#define ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T) \ + static_assert(sizeof(T) <= sizeof(ARDUINOJSON_NAMESPACE::Integer), \ + "To use 64-bit integers with ArduinoJson, you must set " \ + "ARDUINOJSON_USE_LONG_LONG to 1. See " \ + "https://arduinojson.org/v6/api/config/use_long_long/"); +#else +#define ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T) +#endif namespace ARDUINOJSON_NAMESPACE { enum { VALUE_MASK = 0x7F, + VALUE_IS_OWNED = 0x01, VALUE_IS_NULL = 0, - VALUE_IS_LINKED_RAW = 0x01, - VALUE_IS_OWNED_RAW = 0x02, - VALUE_IS_LINKED_STRING = 0x03, - VALUE_IS_OWNED_STRING = 0x04, - VALUE_IS_BOOLEAN = 0x05, - VALUE_IS_POSITIVE_INTEGER = 0x06, - VALUE_IS_NEGATIVE_INTEGER = 0x07, - VALUE_IS_FLOAT = 0x08, + VALUE_IS_LINKED_RAW = 0x02, + VALUE_IS_OWNED_RAW = 0x03, + VALUE_IS_LINKED_STRING = 0x04, + VALUE_IS_OWNED_STRING = 0x05, + VALUE_IS_BOOLEAN = 0x06, + VALUE_IS_POSITIVE_INTEGER = 0x08, + VALUE_IS_NEGATIVE_INTEGER = 0x0A, + VALUE_IS_FLOAT = 0x0C, COLLECTION_MASK = 0x60, VALUE_IS_OBJECT = 0x20, VALUE_IS_ARRAY = 0x40, @@ -469,8 +624,6 @@ union VariantContent { size_t size; } asRaw; }; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { typedef conditional::type VariantSlotDiff; class VariantSlot { VariantContent _content; @@ -493,7 +646,8 @@ class VariantSlot { VariantSlot* next(size_t distance) { VariantSlot* slot = this; while (distance--) { - if (!slot->_next) return 0; + if (!slot->_next) + return 0; slot += slot->_next; } return slot; @@ -508,13 +662,15 @@ class VariantSlot { ARDUINOJSON_ASSERT(slot != 0); _next = VariantSlotDiff(slot - this); } - void setOwnedKey(not_null k) { + void setKey(const char* k, storage_policies::store_by_copy) { + ARDUINOJSON_ASSERT(k != NULL); _flags |= KEY_IS_OWNED; - _key = k.get(); + _key = k; } - void setLinkedKey(not_null k) { + void setKey(const char* k, storage_policies::store_by_address) { + ARDUINOJSON_ASSERT(k != NULL); _flags &= VALUE_MASK; - _key = k.get(); + _key = k; } const char* key() const { return _key; @@ -527,31 +683,17 @@ class VariantSlot { _flags = 0; _key = 0; } + void movePointers(ptrdiff_t stringDistance, ptrdiff_t variantDistance) { + if (_flags & KEY_IS_OWNED) + _key += stringDistance; + if (_flags & VALUE_IS_OWNED) + _content.asString += stringDistance; + if (_flags & COLLECTION_MASK) + _content.asCollection.movePointers(stringDistance, variantDistance); + } }; } // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { -inline bool isAligned(void *ptr) { - const size_t mask = sizeof(void *) - 1; - size_t addr = reinterpret_cast(ptr); - return (addr & mask) == 0; -} -inline size_t addPadding(size_t bytes) { - const size_t mask = sizeof(void *) - 1; - return (bytes + mask) & ~mask; -} -template -struct AddPadding { - static const size_t mask = sizeof(void *) - 1; - static const size_t value = (bytes + mask) & ~mask; -}; -} // namespace ARDUINOJSON_NAMESPACE -#define JSON_STRING_SIZE(SIZE) (SIZE) -namespace ARDUINOJSON_NAMESPACE { -struct StringSlot { - char *value; - size_t size; -}; -} // namespace ARDUINOJSON_NAMESPACE +#include namespace ARDUINOJSON_NAMESPACE { class MemoryPool { public: @@ -576,25 +718,37 @@ class MemoryPool { VariantSlot* allocVariant() { return allocRight(); } - char* allocFrozenString(size_t n) { - if (!canAlloc(n)) return 0; - char* s = _left; - _left += n; - checkInvariants(); - return s; + template + const char* saveString(const TAdaptedString& str) { + if (str.isNull()) + return 0; +#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION + const char* existingCopy = findString(str.begin()); + if (existingCopy) + return existingCopy; +#endif + size_t n = str.size(); + char* newCopy = allocString(n + 1); + if (newCopy) { + str.copyTo(newCopy, n); + newCopy[n] = 0; // force null-terminator + } + return newCopy; } - StringSlot allocExpandableString() { - StringSlot s; - s.value = _left; - s.size = size_t(_right - _left); - _left = _right; - checkInvariants(); - return s; + void getFreeZone(char** zoneStart, size_t* zoneSize) const { + *zoneStart = _left; + *zoneSize = size_t(_right - _left); } - void freezeString(StringSlot& s, size_t newSize) { - _left -= (s.size - newSize); - s.size = newSize; + const char* saveStringFromFreeZone(size_t len) { +#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION + const char* dup = findString(_left); + if (dup) + return dup; +#endif + const char* str = _left; + _left += len; checkInvariants(); + return str; } void clear() { _left = _begin; @@ -606,18 +760,26 @@ class MemoryPool { bool owns(void* p) const { return _begin <= p && p < _end; } - template - T* allocRight() { - return reinterpret_cast(allocRight(sizeof(T))); - } - void* allocRight(size_t bytes) { - if (!canAlloc(bytes)) return 0; - _right -= bytes; - return _right; - } void* operator new(size_t, void* p) { return p; } + ptrdiff_t squash() { + char* new_right = addPadding(_left); + if (new_right >= _right) + return 0; + size_t right_size = static_cast(_end - _right); + memmove(new_right, _right, right_size); + ptrdiff_t bytes_reclaimed = _right - new_right; + _right = new_right; + _end = new_right + right_size; + return bytes_reclaimed; + } + void movePointers(ptrdiff_t offset) { + _begin += offset; + _left += offset; + _right += offset; + _end += offset; + } private: StringSlot* allocStringSlot() { return allocRight(); @@ -628,37 +790,68 @@ class MemoryPool { ARDUINOJSON_ASSERT(_right <= _end); ARDUINOJSON_ASSERT(isAligned(_right)); } +#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION + template + const char* findString(TIterator str) { + for (char* next = _begin; next < _left; ++next) { + char* begin = next; + for (TIterator it = str; *it == *next; ++it) { + if (*next++ == 0) + return begin; + } + while (*next) ++next; + } + return 0; + } +#endif + char* allocString(size_t n) { + if (!canAlloc(n)) + return 0; + char* s = _left; + _left += n; + checkInvariants(); + return s; + } + template + T* allocRight() { + return reinterpret_cast(allocRight(sizeof(T))); + } + void* allocRight(size_t bytes) { + if (!canAlloc(bytes)) + return 0; + _right -= bytes; + return _right; + } char *_begin, *_left, *_right, *_end; }; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { +inline int safe_strcmp(const char* a, const char* b) { + if (a == b) + return 0; + if (!a) + return -1; + if (!b) + return 1; + return strcmp(a, b); +} +inline int safe_strncmp(const char* a, const char* b, size_t n) { + if (a == b) + return 0; + if (!a) + return -1; + if (!b) + return 1; + return strncmp(a, b, n); +} template struct IsString : false_type {}; template struct IsString : IsString {}; template struct IsString : IsString {}; -} // namespace ARDUINOJSON_NAMESPACE -#include // strcmp -namespace ARDUINOJSON_NAMESPACE { -inline int8_t safe_strcmp(const char* a, const char* b) { - if (a == b) return 0; - if (!a) return -1; - if (!b) return 1; - return static_cast(strcmp(a, b)); -} -inline int8_t safe_strncmp(const char* a, const char* b, size_t n) { - if (a == b) return 0; - if (!a) return -1; - if (!b) return 1; - return static_cast(strncmp(a, b, n)); -} -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { class ConstRamStringAdapter { public: ConstRamStringAdapter(const char* str = 0) : _str(str) {} - int8_t compare(const char* other) const { + int compare(const char* other) const { return safe_strcmp(_str, other); } bool equals(const char* expected) const { @@ -667,41 +860,35 @@ class ConstRamStringAdapter { bool isNull() const { return !_str; } - template - char* save(TMemoryPool*) const { - return 0; - } size_t size() const { - if (!_str) return 0; + if (!_str) + return 0; return strlen(_str); } const char* data() const { return _str; } - bool isStatic() const { - return true; + const char* begin() const { + return _str; } + typedef storage_policies::store_by_address storage_policy; protected: const char* _str; }; +template <> +struct IsString : true_type {}; +template +struct IsString : true_type {}; inline ConstRamStringAdapter adaptString(const char* str) { return ConstRamStringAdapter(str); } -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { class RamStringAdapter : public ConstRamStringAdapter { public: RamStringAdapter(const char* str) : ConstRamStringAdapter(str) {} - char* save(MemoryPool* pool) const { - if (!_str) return NULL; - size_t n = size() + 1; - char* dup = pool->allocFrozenString(n); - if (dup) memcpy(dup, _str, n); - return dup; - } - bool isStatic() const { - return false; + void copyTo(char* p, size_t n) const { + memcpy(p, _str, n); } + typedef ARDUINOJSON_NAMESPACE::storage_policies::store_by_copy storage_policy; }; template inline RamStringAdapter adaptString(const TChar* str) { @@ -718,13 +905,11 @@ template <> struct IsString { static const bool value = false; }; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { class SizedRamStringAdapter { public: SizedRamStringAdapter(const char* str, size_t n) : _str(str), _size(n) {} - int8_t compare(const char* other) const { - return safe_strncmp(_str, other, _size) == 0; + int compare(const char* other) const { + return safe_strncmp(_str, other, _size); } bool equals(const char* expected) const { return compare(expected) == 0; @@ -732,18 +917,16 @@ class SizedRamStringAdapter { bool isNull() const { return !_str; } - char* save(MemoryPool* pool) const { - if (!_str) return NULL; - char* dup = pool->allocFrozenString(_size); - if (dup) memcpy(dup, _str, _size); - return dup; + void copyTo(char* p, size_t n) const { + memcpy(p, _str, n); } size_t size() const { return _size; } - bool isStatic() const { - return false; + const char* begin() const { + return _str; } + typedef storage_policies::store_by_copy storage_policy; private: const char* _str; size_t _size; @@ -756,42 +939,44 @@ inline SizedRamStringAdapter adaptString(const TChar* str, size_t size) { #if ARDUINOJSON_ENABLE_STD_STRING #include namespace ARDUINOJSON_NAMESPACE { +template class StlStringAdapter { public: - StlStringAdapter(const std::string& str) : _str(&str) {} - char* save(MemoryPool* pool) const { - size_t n = _str->length() + 1; - char* dup = pool->allocFrozenString(n); - if (dup) memcpy(dup, _str->c_str(), n); - return dup; + StlStringAdapter(const TString& str) : _str(&str) {} + void copyTo(char* p, size_t n) const { + memcpy(p, _str->c_str(), n); } bool isNull() const { return false; } - int8_t compare(const char* other) const { - if (!other) return 1; - return static_cast(_str->compare(other)); + int compare(const char* other) const { + if (!other) + return 1; + return _str->compare(other); } bool equals(const char* expected) const { - if (!expected) return false; + if (!expected) + return false; return *_str == expected; } - const char* data() const { - return _str->data(); - } size_t size() const { return _str->size(); } - bool isStatic() const { - return false; + const char* begin() const { + return _str->c_str(); } + typedef storage_policies::store_by_copy storage_policy; private: - const std::string* _str; + const TString* _str; }; -template <> -struct IsString : true_type {}; -inline StlStringAdapter adaptString(const std::string& str) { - return StlStringAdapter(str); +template +struct IsString > : true_type { +}; +template +inline StlStringAdapter > +adaptString(const std::basic_string& str) { + return StlStringAdapter >( + str); } } // namespace ARDUINOJSON_NAMESPACE #endif @@ -801,32 +986,26 @@ namespace ARDUINOJSON_NAMESPACE { class ArduinoStringAdapter { public: ArduinoStringAdapter(const ::String& str) : _str(&str) {} - char* save(MemoryPool* pool) const { - if (isNull()) return NULL; - size_t n = _str->length() + 1; - char* dup = pool->allocFrozenString(n); - if (dup) memcpy(dup, _str->c_str(), n); - return dup; + void copyTo(char* p, size_t n) const { + memcpy(p, _str->c_str(), n); } bool isNull() const { return !_str->c_str(); } - int8_t compare(const char* other) const { + int compare(const char* other) const { const char* me = _str->c_str(); return safe_strcmp(me, other); } bool equals(const char* expected) const { return compare(expected) == 0; } - const char* data() const { - return _str->c_str(); - } size_t size() const { return _str->length(); } - bool isStatic() const { - return false; + const char* begin() const { + return _str->c_str(); } + typedef storage_policies::store_by_copy storage_policy; private: const ::String* _str; }; @@ -841,13 +1020,105 @@ inline ArduinoStringAdapter adaptString(const ::String& str) { #endif #if ARDUINOJSON_ENABLE_PROGMEM namespace ARDUINOJSON_NAMESPACE { +struct pgm_p { + pgm_p(const char* p) : address(p) {} + const char* address; +}; +} // namespace ARDUINOJSON_NAMESPACE +#ifndef strlen_P +inline size_t strlen_P(ARDUINOJSON_NAMESPACE::pgm_p s) { + const char* p = s.address; + ARDUINOJSON_ASSERT(p != NULL); + while (pgm_read_byte(p)) p++; + return size_t(p - s.address); +} +#endif +#ifndef strncmp_P +inline int strncmp_P(const char* a, ARDUINOJSON_NAMESPACE::pgm_p b, size_t n) { + const char* s1 = a; + const char* s2 = b.address; + ARDUINOJSON_ASSERT(s1 != NULL); + ARDUINOJSON_ASSERT(s2 != NULL); + while (n-- > 0) { + char c1 = *s1++; + char c2 = static_cast(pgm_read_byte(s2++)); + if (c1 < c2) + return -1; + if (c1 > c2) + return 1; + if (c1 == 0 /* and c2 as well */) + return 0; + } + return 0; +} +#endif +#ifndef strcmp_P +inline int strcmp_P(const char* a, ARDUINOJSON_NAMESPACE::pgm_p b) { + const char* s1 = a; + const char* s2 = b.address; + ARDUINOJSON_ASSERT(s1 != NULL); + ARDUINOJSON_ASSERT(s2 != NULL); + for (;;) { + char c1 = *s1++; + char c2 = static_cast(pgm_read_byte(s2++)); + if (c1 < c2) + return -1; + if (c1 > c2) + return 1; + if (c1 == 0 /* and c2 as well */) + return 0; + } +} +#endif +#ifndef memcpy_P +inline void* memcpy_P(void* dst, ARDUINOJSON_NAMESPACE::pgm_p src, size_t n) { + uint8_t* d = reinterpret_cast(dst); + const char* s = src.address; + ARDUINOJSON_ASSERT(d != NULL); + ARDUINOJSON_ASSERT(s != NULL); + while (n-- > 0) { + *d++ = pgm_read_byte(s++); + } + return dst; +} +#endif +namespace ARDUINOJSON_NAMESPACE { +class FlashStringIterator { + public: + explicit FlashStringIterator(const __FlashStringHelper* ptr) + : _ptr(reinterpret_cast(ptr)) {} + explicit FlashStringIterator(const char* ptr) : _ptr(ptr) {} + FlashStringIterator operator+(ptrdiff_t d) const { + return FlashStringIterator(_ptr + d); + } + ptrdiff_t operator-(FlashStringIterator other) const { + return _ptr - other._ptr; + } + FlashStringIterator operator++(int) { + return FlashStringIterator(_ptr++); + } + FlashStringIterator operator++() { + return FlashStringIterator(++_ptr); + } + bool operator!=(FlashStringIterator other) const { + return _ptr != other._ptr; + } + char operator*() const { + return char(pgm_read_byte(_ptr)); + } + private: + const char* _ptr; +}; class FlashStringAdapter { public: FlashStringAdapter(const __FlashStringHelper* str) : _str(str) {} - int8_t compare(const char* other) const { - if (!other && !_str) return 0; - if (!_str) return -1; - if (!other) return 1; + int compare(const char* other) const { + if (!other && !_str) + return 0; + if (!_str) + return -1; + if (!other) + return 1; return -strcmp_P(other, reinterpret_cast(_str)); } bool equals(const char* expected) const { @@ -856,23 +1127,18 @@ class FlashStringAdapter { bool isNull() const { return !_str; } - char* save(MemoryPool* pool) const { - if (!_str) return NULL; - size_t n = size() + 1; // copy the terminator - char* dup = pool->allocFrozenString(n); - if (dup) memcpy_P(dup, reinterpret_cast(_str), n); - return dup; - } - const char* data() const { - return 0; + void copyTo(char* p, size_t n) const { + memcpy_P(p, reinterpret_cast(_str), n); } size_t size() const { - if (!_str) return 0; + if (!_str) + return 0; return strlen_P(reinterpret_cast(_str)); } - bool isStatic() const { - return false; + FlashStringIterator begin() const { + return FlashStringIterator(_str); } + typedef storage_policies::store_by_copy storage_policy; private: const __FlashStringHelper* _str; }; @@ -881,16 +1147,17 @@ inline FlashStringAdapter adaptString(const __FlashStringHelper* str) { } template <> struct IsString : true_type {}; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { class SizedFlashStringAdapter { public: SizedFlashStringAdapter(const __FlashStringHelper* str, size_t sz) : _str(str), _size(sz) {} - int8_t compare(const char* other) const { - if (!other && !_str) return 0; - if (!_str) return -1; - if (!other) return 1; + int compare(const char* other) const { + if (!other && !_str) + return 0; + if (!_str) + return -1; + if (!other) + return 1; return -strncmp_P(other, reinterpret_cast(_str), _size); } bool equals(const char* expected) const { @@ -899,18 +1166,16 @@ class SizedFlashStringAdapter { bool isNull() const { return !_str; } - char* save(MemoryPool* pool) const { - if (!_str) return NULL; - char* dup = pool->allocFrozenString(_size); - if (dup) memcpy_P(dup, (const char*)_str, _size); - return dup; + void copyTo(char* p, size_t n) const { + memcpy_P(p, reinterpret_cast(_str), n); } size_t size() const { return _size; } - bool isStatic() const { - return false; + FlashStringIterator begin() const { + return FlashStringIterator(_str); } + typedef storage_policies::store_by_copy storage_policy; private: const __FlashStringHelper* _str; size_t _size; @@ -977,37 +1242,7 @@ inline SerializedValue serialized(TChar* p, size_t n) { #endif #pragma GCC diagnostic ignored "-Wconversion" #endif -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4310) -#endif -namespace ARDUINOJSON_NAMESPACE { -template -struct numeric_limits; -template -struct numeric_limits::value>::type> { - static T lowest() { - return 0; - } - static T highest() { - return T(-1); - } -}; -template -struct numeric_limits< - T, typename enable_if::value && is_signed::value>::type> { - static T lowest() { - return T(T(1) << (sizeof(T) * 8 - 1)); - } - static T highest() { - return T(~lowest()); - } -}; -} // namespace ARDUINOJSON_NAMESPACE -#ifdef _MSC_VER -#pragma warning(pop) -#endif -#include // for size_t +#include namespace ARDUINOJSON_NAMESPACE { #ifndef isnan template @@ -1021,8 +1256,6 @@ bool isinf(T x) { return x != 0.0 && x * 2 == x; } #endif -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { template struct alias_cast_t { union { @@ -1036,8 +1269,6 @@ T alias_cast(F raw_data) { ac.raw = raw_data; return ac.data; } -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { template struct FloatTraits {}; template @@ -1052,13 +1283,15 @@ struct FloatTraits { static T make_float(T m, TExponent e) { if (e > 0) { for (uint8_t index = 0; e != 0; index++) { - if (e & 1) m *= positiveBinaryPowerOfTen(index); + if (e & 1) + m *= positiveBinaryPowerOfTen(index); e >>= 1; } } else { e = TExponent(-e); for (uint8_t index = 0; e != 0; index++) { - if (e & 1) m *= negativeBinaryPowerOfTen(index); + if (e & 1) + m *= negativeBinaryPowerOfTen(index); e >>= 1; } } @@ -1134,13 +1367,15 @@ struct FloatTraits { static T make_float(T m, TExponent e) { if (e > 0) { for (uint8_t index = 0; e != 0; index++) { - if (e & 1) m *= positiveBinaryPowerOfTen(index); + if (e & 1) + m *= positiveBinaryPowerOfTen(index); e >>= 1; } } else { e = -e; for (uint8_t index = 0; e != 0; index++) { - if (e & 1) m *= negativeBinaryPowerOfTen(index); + if (e & 1) + m *= negativeBinaryPowerOfTen(index); e >>= 1; } } @@ -1175,6 +1410,36 @@ struct FloatTraits { } }; } // namespace ARDUINOJSON_NAMESPACE +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4310) +#endif +namespace ARDUINOJSON_NAMESPACE { +template +struct numeric_limits; +template +struct numeric_limits::value>::type> { + static T lowest() { + return 0; + } + static T highest() { + return T(-1); + } +}; +template +struct numeric_limits< + T, typename enable_if::value && is_signed::value>::type> { + static T lowest() { + return T(T(1) << (sizeof(T) * 8 - 1)); + } + static T highest() { + return T(~lowest()); + } +}; +} // namespace ARDUINOJSON_NAMESPACE +#ifdef _MSC_VER +#pragma warning(pop) +#endif namespace ARDUINOJSON_NAMESPACE { template typename enable_if::value && sizeof(TOut) <= sizeof(TIn), @@ -1247,6 +1512,13 @@ typename enable_if::value, TOut>::type convertFloat( #pragma GCC diagnostic pop #endif #endif +#if defined(__GNUC__) +#if __GNUC__ >= 7 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#pragma GCC diagnostic ignored "-Wuninitialized" +#endif +#endif namespace ARDUINOJSON_NAMESPACE { class VariantData { VariantContent _content; // must be first to allow cast from array to variant @@ -1302,7 +1574,7 @@ class VariantData { case VALUE_IS_OBJECT: return toObject().copyFrom(src._content.asCollection, pool); case VALUE_IS_OWNED_STRING: - return setOwnedString(RamStringAdapter(src._content.asString), pool); + return setString(RamStringAdapter(src._content.asString), pool); case VALUE_IS_OWNED_RAW: return setOwnedRaw( serialized(src._content.asRaw.data, src._content.asRaw.size), pool); @@ -1312,32 +1584,6 @@ class VariantData { return true; } } - bool equals(const VariantData &other) const { - if (type() != other.type()) return false; - switch (type()) { - case VALUE_IS_LINKED_STRING: - case VALUE_IS_OWNED_STRING: - return !strcmp(_content.asString, other._content.asString); - case VALUE_IS_LINKED_RAW: - case VALUE_IS_OWNED_RAW: - return _content.asRaw.size == other._content.asRaw.size && - !memcmp(_content.asRaw.data, other._content.asRaw.data, - _content.asRaw.size); - case VALUE_IS_BOOLEAN: - case VALUE_IS_POSITIVE_INTEGER: - case VALUE_IS_NEGATIVE_INTEGER: - return _content.asInteger == other._content.asInteger; - case VALUE_IS_ARRAY: - return _content.asCollection.equalsArray(other._content.asCollection); - case VALUE_IS_OBJECT: - return _content.asCollection.equalsObject(other._content.asCollection); - case VALUE_IS_FLOAT: - return _content.asFloat == other._content.asFloat; - case VALUE_IS_NULL: - default: - return true; - } - } bool isArray() const { return (_flags & VALUE_IS_ARRAY) != 0; } @@ -1372,14 +1618,16 @@ class VariantData { return type() == VALUE_IS_NULL; } bool isEnclosed() const { - return isCollection() || isString(); + return !isFloat(); } void remove(size_t index) { - if (isArray()) _content.asCollection.remove(index); + if (isArray()) + _content.asCollection.removeElement(index); } template void remove(TAdaptedString key) { - if (isObject()) _content.asCollection.remove(key); + if (isObject()) + _content.asCollection.removeMember(key); } void setBoolean(bool value) { setType(VALUE_IS_BOOLEAN); @@ -1400,7 +1648,7 @@ class VariantData { } template bool setOwnedRaw(SerializedValue value, MemoryPool *pool) { - char *dup = adaptString(value.data(), value.size()).save(pool); + const char *dup = pool->saveString(adaptString(value.data(), value.size())); if (dup) { setType(VALUE_IS_OWNED_RAW); _content.asRaw.data = dup; @@ -1427,6 +1675,10 @@ class VariantData { setNegativeInteger(~static_cast(value) + 1); } } + void setUnsignedInteger(UInt value) { + setType(VALUE_IS_POSITIVE_INTEGER); + _content.asInteger = static_cast(value); + } void setPositiveInteger(UInt value) { setType(VALUE_IS_POSITIVE_INTEGER); _content.asInteger = value; @@ -1435,37 +1687,49 @@ class VariantData { setType(VALUE_IS_NEGATIVE_INTEGER); _content.asInteger = value; } - void setLinkedString(const char *value) { - if (value) { - setType(VALUE_IS_LINKED_STRING); - _content.asString = value; - } else { - setType(VALUE_IS_NULL); - } - } void setNull() { setType(VALUE_IS_NULL); } - void setOwnedString(not_null s) { + void setString(not_null s, storage_policies::store_by_copy) { setType(VALUE_IS_OWNED_STRING); _content.asString = s.get(); } - bool setOwnedString(const char *s) { + void setString(not_null s, storage_policies::store_by_address) { + setType(VALUE_IS_LINKED_STRING); + _content.asString = s.get(); + } + template + bool setString(const char *s, TStoragePolicy storage_policy) { if (s) { - setOwnedString(make_not_null(s)); + setString(make_not_null(s), storage_policy); return true; } else { setType(VALUE_IS_NULL); return false; } } - template - bool setOwnedString(T value, MemoryPool *pool) { - return setOwnedString(value.save(pool)); + template + bool setString(TAdaptedString value, MemoryPool *pool) { + return setString(value, pool, typename TAdaptedString::storage_policy()); } - void setUnsignedInteger(UInt value) { - setType(VALUE_IS_POSITIVE_INTEGER); - _content.asInteger = static_cast(value); + template + inline bool setString(TAdaptedString value, MemoryPool *pool, + storage_policies::decide_at_runtime) { + if (value.isStatic()) + return setString(value, pool, storage_policies::store_by_address()); + else + return setString(value, pool, storage_policies::store_by_copy()); + } + template + inline bool setString(TAdaptedString value, MemoryPool *, + storage_policies::store_by_address) { + return setString(value.data(), storage_policies::store_by_address()); + } + template + inline bool setString(TAdaptedString value, MemoryPool *pool, + storage_policies::store_by_copy) { + return setString(pool->saveString(value), + storage_policies::store_by_copy()); } CollectionData &toArray() { setType(VALUE_IS_ARRAY); @@ -1497,24 +1761,39 @@ class VariantData { return isCollection() ? _content.asCollection.size() : 0; } VariantData *addElement(MemoryPool *pool) { - if (isNull()) toArray(); - if (!isArray()) return 0; - return _content.asCollection.add(pool); + if (isNull()) + toArray(); + if (!isArray()) + return 0; + return _content.asCollection.addElement(pool); } VariantData *getElement(size_t index) const { - return isArray() ? _content.asCollection.get(index) : 0; + return isArray() ? _content.asCollection.getElement(index) : 0; + } + VariantData *getOrAddElement(size_t index, MemoryPool *pool) { + if (isNull()) + toArray(); + if (!isArray()) + return 0; + return _content.asCollection.getOrAddElement(index, pool); } template VariantData *getMember(TAdaptedString key) const { - return isObject() ? _content.asCollection.get(key) : 0; + return isObject() ? _content.asCollection.getMember(key) : 0; } template VariantData *getOrAddMember(TAdaptedString key, MemoryPool *pool) { - if (isNull()) toObject(); - if (!isObject()) return 0; - VariantData *var = _content.asCollection.get(key); - if (var) return var; - return _content.asCollection.add(key, pool); + if (isNull()) + toObject(); + if (!isObject()) + return 0; + return _content.asCollection.getOrAddMember(key, pool); + } + void movePointers(ptrdiff_t stringDistance, ptrdiff_t variantDistance) { + if (_flags & VALUE_IS_OWNED) + _content.asString += stringDistance; + if (_flags & COLLECTION_MASK) + _content.asCollection.movePointers(stringDistance, variantDistance); } private: uint8_t type() const { @@ -1526,34 +1805,42 @@ class VariantData { } }; } // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { -inline VariantData *arrayAdd(CollectionData *arr, MemoryPool *pool) { - return arr ? arr->add(pool) : 0; -} -template -inline void arrayAccept(const CollectionData *arr, Visitor &visitor) { - if (arr) - visitor.visitArray(*arr); - else - visitor.visitNull(); -} -inline bool arrayEquals(const CollectionData *lhs, const CollectionData *rhs) { - if (lhs == rhs) return true; - if (!lhs || !rhs) return false; - return lhs->equalsArray(*rhs); -} -} // namespace ARDUINOJSON_NAMESPACE +#if defined(__GNUC__) +#if __GNUC__ >= 8 +#pragma GCC diagnostic pop +#endif +#endif namespace ARDUINOJSON_NAMESPACE { template inline bool slotSetKey(VariantSlot* var, TAdaptedString key, MemoryPool* pool) { - if (!var) return false; + if (!var) + return false; + return slotSetKey(var, key, pool, typename TAdaptedString::storage_policy()); +} +template +inline bool slotSetKey(VariantSlot* var, TAdaptedString key, MemoryPool* pool, + storage_policies::decide_at_runtime) { if (key.isStatic()) { - var->setLinkedKey(make_not_null(key.data())); + return slotSetKey(var, key, pool, storage_policies::store_by_address()); } else { - const char* dup = key.save(pool); - if (!dup) return false; - var->setOwnedKey(make_not_null(dup)); + return slotSetKey(var, key, pool, storage_policies::store_by_copy()); } +} +template +inline bool slotSetKey(VariantSlot* var, TAdaptedString key, MemoryPool*, + storage_policies::store_by_address) { + ARDUINOJSON_ASSERT(var); + var->setKey(key.data(), storage_policies::store_by_address()); + return true; +} +template +inline bool slotSetKey(VariantSlot* var, TAdaptedString key, MemoryPool* pool, + storage_policies::store_by_copy) { + const char* dup = pool->saveString(key); + if (!dup) + return false; + ARDUINOJSON_ASSERT(var); + var->setKey(dup, storage_policies::store_by_copy()); return true; } inline size_t slotSize(const VariantSlot* var) { @@ -1567,8 +1854,6 @@ inline size_t slotSize(const VariantSlot* var) { inline VariantData* slotData(VariantSlot* slot) { return reinterpret_cast(slot); } -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { struct Visitable { }; template @@ -1576,6 +1861,98 @@ struct IsVisitable : is_base_of {}; template struct IsVisitable : IsVisitable {}; } // namespace ARDUINOJSON_NAMESPACE +#if ARDUINOJSON_ENABLE_ARDUINO_STRING +#endif +#if ARDUINOJSON_ENABLE_STD_STRING +#endif +namespace ARDUINOJSON_NAMESPACE { +template +struct IsWriteableString : false_type {}; +#if ARDUINOJSON_ENABLE_ARDUINO_STRING +template <> +struct IsWriteableString< ::String> : true_type {}; +#endif +#if ARDUINOJSON_ENABLE_STD_STRING +template +struct IsWriteableString > + : true_type {}; +#endif +class ArrayRef; +class ArrayConstRef; +class ObjectRef; +class ObjectConstRef; +class VariantRef; +class VariantConstRef; +template +struct VariantAs { + typedef T type; +}; +template <> +struct VariantAs { + typedef const char* type; +}; +template +struct VariantConstAs { + typedef typename VariantAs::type type; +}; +template <> +struct VariantConstAs { + typedef VariantConstRef type; +}; +template <> +struct VariantConstAs { + typedef ObjectConstRef type; +}; +template <> +struct VariantConstAs { + typedef ArrayConstRef type; +}; +template +inline typename enable_if::value && !is_same::value, + T>::type +variantAs(const VariantData* data) { + ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T); + return data != 0 ? data->asIntegral() : T(0); +} +template +inline typename enable_if::value, T>::type variantAs( + const VariantData* data) { + return data != 0 ? static_cast(data->asIntegral()) : T(); +} +template +inline typename enable_if::value, T>::type variantAs( + const VariantData* data) { + return data != 0 ? data->asBoolean() : false; +} +template +inline typename enable_if::value, T>::type variantAs( + const VariantData* data) { + return data != 0 ? data->asFloat() : T(0); +} +template +inline typename enable_if::value || + is_same::value, + const char*>::type +variantAs(const VariantData* data) { + return data != 0 ? data->asString() : 0; +} +template +T variantAs(VariantData* data, MemoryPool*) { + return variantAs(data); +} +template +inline typename enable_if::value, T>::type variantAs( + const VariantData* data); +template +inline typename enable_if::value, T>::type variantAs( + const VariantData* data); +template +inline typename enable_if::value, T>::type +variantAs(const VariantData* data); +template +inline typename enable_if::value, T>::type variantAs( + const VariantData* data); +} // namespace ARDUINOJSON_NAMESPACE #ifdef _MSC_VER // Visual Studio #define FORCE_INLINE // __forceinline causes C4714 when returning std::string #define NO_INLINE __declspec(noinline) @@ -1608,432 +1985,6 @@ struct IsVisitable : IsVisitable {}; #define ARDUINOJSON_NO_SANITIZE(check) #endif namespace ARDUINOJSON_NAMESPACE { -template -class VariantCasts { - public: - template - FORCE_INLINE operator T() const { - return impl()->template as(); - } - private: - const TImpl *impl() const { - return static_cast(this); - } -}; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { -template -struct Comparer; -template -struct Comparer::value>::type> { - T rhs; - int result; - explicit Comparer(T value) : rhs(value), result(1) {} - void visitArray(const CollectionData &) {} - void visitObject(const CollectionData &) {} - void visitFloat(Float) {} - void visitString(const char *lhs) { - result = -adaptString(rhs).compare(lhs); - } - void visitRawJson(const char *, size_t) {} - void visitNegativeInteger(UInt) {} - void visitPositiveInteger(UInt) {} - void visitBoolean(bool) {} - void visitNull() { - result = adaptString(rhs).compare(NULL); - } -}; -template -typename enable_if::value, int>::type sign(const T &value) { - return value < 0 ? -1 : value > 0 ? 1 : 0; -} -template -typename enable_if::value, int>::type sign(const T &value) { - return value > 0 ? 1 : 0; -} -template -struct Comparer::value || - is_floating_point::value>::type> { - T rhs; - int result; - explicit Comparer(T value) : rhs(value), result(1) {} - void visitArray(const CollectionData &) {} - void visitObject(const CollectionData &) {} - void visitFloat(Float lhs) { - result = sign(lhs - static_cast(rhs)); - } - void visitString(const char *) {} - void visitRawJson(const char *, size_t) {} - void visitNegativeInteger(UInt lhs) { - result = -sign(static_cast(lhs) + rhs); - } - void visitPositiveInteger(UInt lhs) { - result = static_cast(lhs) < rhs ? -1 : static_cast(lhs) > rhs ? 1 : 0; - } - void visitBoolean(bool) {} - void visitNull() {} -}; -template <> -struct Comparer { - bool rhs; - int result; - explicit Comparer(bool value) : rhs(value), result(1) {} - void visitArray(const CollectionData &) {} - void visitObject(const CollectionData &) {} - void visitFloat(Float) {} - void visitString(const char *) {} - void visitRawJson(const char *, size_t) {} - void visitNegativeInteger(UInt) {} - void visitPositiveInteger(UInt) {} - void visitBoolean(bool lhs) { - result = static_cast(lhs - rhs); - } - void visitNull() {} -}; -#if ARDUINOJSON_HAS_NULLPTR -template <> -struct Comparer { - int result; - explicit Comparer(decltype(nullptr)) : result(1) {} - void visitArray(const CollectionData &) {} - void visitObject(const CollectionData &) {} - void visitFloat(Float) {} - void visitString(const char *) {} - void visitRawJson(const char *, size_t) {} - void visitNegativeInteger(UInt) {} - void visitPositiveInteger(UInt) {} - void visitBoolean(bool) {} - void visitNull() { - result = 0; - } -}; -#endif -template -class VariantComparisons { - private: - template - static int compare(TVariant lhs, const T &rhs) { - Comparer comparer(rhs); - lhs.accept(comparer); - return comparer.result; - } - public: - template - friend bool operator==(T *lhs, TVariant rhs) { - return compare(rhs, lhs) == 0; - } - template - friend bool operator==(const T &lhs, TVariant rhs) { - return compare(rhs, lhs) == 0; - } - template - friend bool operator==(TVariant lhs, T *rhs) { - return compare(lhs, rhs) == 0; - } - template - friend bool operator==(TVariant lhs, const T &rhs) { - return compare(lhs, rhs) == 0; - } - template - friend bool operator!=(T *lhs, TVariant rhs) { - return compare(rhs, lhs) != 0; - } - template - friend bool operator!=(const T &lhs, TVariant rhs) { - return compare(rhs, lhs) != 0; - } - template - friend bool operator!=(TVariant lhs, T *rhs) { - return compare(lhs, rhs) != 0; - } - template - friend bool operator!=(TVariant lhs, const T &rhs) { - return compare(lhs, rhs) != 0; - } - template - friend bool operator<(T *lhs, TVariant rhs) { - return compare(rhs, lhs) > 0; - } - template - friend bool operator<(const T &lhs, TVariant rhs) { - return compare(rhs, lhs) > 0; - } - template - friend bool operator<(TVariant lhs, T *rhs) { - return compare(lhs, rhs) < 0; - } - template - friend bool operator<(TVariant lhs, const T &rhs) { - return compare(lhs, rhs) < 0; - } - template - friend bool operator<=(T *lhs, TVariant rhs) { - return compare(rhs, lhs) >= 0; - } - template - friend bool operator<=(const T &lhs, TVariant rhs) { - return compare(rhs, lhs) >= 0; - } - template - friend bool operator<=(TVariant lhs, T *rhs) { - return compare(lhs, rhs) <= 0; - } - template - friend bool operator<=(TVariant lhs, const T &rhs) { - return compare(lhs, rhs) <= 0; - } - template - friend bool operator>(T *lhs, TVariant rhs) { - return compare(rhs, lhs) < 0; - } - template - friend bool operator>(const T &lhs, TVariant rhs) { - return compare(rhs, lhs) < 0; - } - template - friend bool operator>(TVariant lhs, T *rhs) { - return compare(lhs, rhs) > 0; - } - template - friend bool operator>(TVariant lhs, const T &rhs) { - return compare(lhs, rhs) > 0; - } - template - friend bool operator>=(T *lhs, TVariant rhs) { - return compare(rhs, lhs) <= 0; - } - template - friend bool operator>=(const T &lhs, TVariant rhs) { - return compare(rhs, lhs) <= 0; - } - template - friend bool operator>=(TVariant lhs, T *rhs) { - return compare(lhs, rhs) >= 0; - } - template - friend bool operator>=(TVariant lhs, const T &rhs) { - return compare(lhs, rhs) >= 0; - } -}; -} // namespace ARDUINOJSON_NAMESPACE -#if ARDUINOJSON_ENABLE_ARDUINO_STRING -#endif -#if ARDUINOJSON_ENABLE_STD_STRING -#endif -namespace ARDUINOJSON_NAMESPACE { -template -struct IsWriteableString : false_type {}; -template -class DynamicStringWriter {}; -#if ARDUINOJSON_ENABLE_ARDUINO_STRING -template <> -struct IsWriteableString : true_type {}; -template <> -class DynamicStringWriter { - public: - DynamicStringWriter(String &str) : _str(&str) {} - size_t write(uint8_t c) { - _str->operator+=(static_cast(c)); - return 1; - } - size_t write(const uint8_t *s, size_t n) { - _str->reserve(_str->length() + n); - while (n > 0) { - _str->operator+=(static_cast(*s++)); - n--; - } - return n; - } - private: - String *_str; -}; -#endif -#if ARDUINOJSON_ENABLE_STD_STRING -template <> -struct IsWriteableString : true_type {}; -template <> -class DynamicStringWriter { - public: - DynamicStringWriter(std::string &str) : _str(&str) {} - size_t write(uint8_t c) { - _str->operator+=(static_cast(c)); - return 1; - } - size_t write(const uint8_t *s, size_t n) { - _str->append(reinterpret_cast(s), n); - return n; - } - private: - std::string *_str; -}; -#endif -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { -class ArrayRef; -class ArrayConstRef; -class ObjectRef; -class ObjectConstRef; -class VariantRef; -class VariantConstRef; -template -struct VariantAs { - typedef T type; -}; -template <> -struct VariantAs { - typedef const char* type; -}; -template -struct VariantConstAs { - typedef typename VariantAs::type type; -}; -template <> -struct VariantConstAs { - typedef VariantConstRef type; -}; -template <> -struct VariantConstAs { - typedef ObjectConstRef type; -}; -template <> -struct VariantConstAs { - typedef ArrayConstRef type; -}; -template -inline typename enable_if::value, T>::type variantAs( - const VariantData* _data) { - return _data != 0 ? _data->asIntegral() : T(0); -} -template -inline typename enable_if::value, T>::type variantAs( - const VariantData* _data) { - return _data != 0 ? _data->asBoolean() : false; -} -template -inline typename enable_if::value, T>::type variantAs( - const VariantData* _data) { - return _data != 0 ? _data->asFloat() : T(0); -} -template -inline typename enable_if::value || - is_same::value, - const char*>::type -variantAs(const VariantData* _data) { - return _data != 0 ? _data->asString() : 0; -} -template -inline typename enable_if::value, T>::type variantAs( - const VariantData* _data); -template -inline typename enable_if::value, T>::type variantAs( - const VariantData* _data); -template -inline typename enable_if::value, T>::type -variantAs(const VariantData* _data); -template -inline typename enable_if::value, T>::type variantAs( - const VariantData* _data); -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { -template -class VariantOr { - public: - template - T operator|(const T &defaultValue) const { - if (impl()->template is()) - return impl()->template as(); - else - return defaultValue; - } - const char *operator|(const char *defaultValue) const { - const char *value = impl()->template as(); - return value ? value : defaultValue; - } - private: - const TImpl *impl() const { - return static_cast(this); - } -}; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { -template -class ElementProxy; -template -class ArrayShortcuts { - public: - FORCE_INLINE ElementProxy operator[](size_t index) const; - FORCE_INLINE ObjectRef createNestedObject() const; - FORCE_INLINE ArrayRef createNestedArray() const; - template - FORCE_INLINE bool add(const T &value) const { - return impl()->addElement().set(value); - } - template - FORCE_INLINE bool add(T *value) const { - return impl()->addElement().set(value); - } - private: - const TArray *impl() const { - return static_cast(this); - } -}; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { -template -class MemberProxy; -template -class ObjectShortcuts { - public: - template - FORCE_INLINE typename enable_if::value, bool>::type - containsKey(const TString &key) const; - template - FORCE_INLINE typename enable_if::value, bool>::type - containsKey(TChar *key) const; - template - FORCE_INLINE - typename enable_if::value, - MemberProxy >::type - operator[](const TString &key) const; - template - FORCE_INLINE typename enable_if::value, - MemberProxy >::type - operator[](TChar *key) const; - template - FORCE_INLINE ArrayRef createNestedArray(const TString &key) const; - template - FORCE_INLINE ArrayRef createNestedArray(TChar *key) const; - template - ObjectRef createNestedObject(const TString &key) const; - template - ObjectRef createNestedObject(TChar *key) const; - private: - const TObject *impl() const { - return static_cast(this); - } -}; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { -template -class VariantShortcuts : public ObjectShortcuts, - public ArrayShortcuts { - public: - using ArrayShortcuts::createNestedArray; - using ArrayShortcuts::createNestedObject; - using ArrayShortcuts::operator[]; - using ObjectShortcuts::createNestedArray; - using ObjectShortcuts::createNestedObject; - using ObjectShortcuts::operator[]; -}; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { -template -class VariantOperators : public VariantCasts, - public VariantComparisons, - public VariantOr, - public VariantShortcuts {}; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { template inline void variantAccept(const VariantData *var, Visitor &visitor) { if (var != 0) @@ -2052,18 +2003,15 @@ inline CollectionData *variantAsObject(VariantData *var) { } inline bool variantCopyFrom(VariantData *dst, const VariantData *src, MemoryPool *pool) { - if (!dst) return false; + if (!dst) + return false; if (!src) { dst->setNull(); return true; } return dst->copyFrom(*src, pool); } -inline bool variantEquals(const VariantData *a, const VariantData *b) { - if (a == b) return true; - if (!a || !b) return false; - return a->equals(*b); -} +inline int variantCompare(const VariantData *a, const VariantData *b); inline bool variantIsArray(const VariantData *var) { return var && var->isArray(); } @@ -2087,18 +2035,21 @@ inline bool variantIsNull(const VariantData *var) { return var == 0 || var->isNull(); } inline bool variantSetBoolean(VariantData *var, bool value) { - if (!var) return false; + if (!var) + return false; var->setBoolean(value); return true; } inline bool variantSetFloat(VariantData *var, Float value) { - if (!var) return false; + if (!var) + return false; var->setFloat(value); return true; } inline bool variantSetLinkedRaw(VariantData *var, SerializedValue value) { - if (!var) return false; + if (!var) + return false; var->setLinkedRaw(value); return true; } @@ -2107,61 +2058,337 @@ inline bool variantSetOwnedRaw(VariantData *var, SerializedValue value, MemoryPool *pool) { return var != 0 && var->setOwnedRaw(value, pool); } -template -inline bool variantSetSignedInteger(VariantData *var, T value) { - if (!var) return false; - var->setSignedInteger(value); - return true; -} -inline bool variantSetLinkedString(VariantData *var, const char *value) { - if (!var) return false; - var->setLinkedString(value); - return true; -} inline void variantSetNull(VariantData *var) { - if (!var) return; + if (!var) + return; var->setNull(); } -inline bool variantSetOwnedString(VariantData *var, char *value) { - if (!var) return false; - var->setOwnedString(value); - return true; +template +inline bool variantSetString(VariantData *var, TAdaptedString value, + MemoryPool *pool) { + if (!var) + return false; + return var->setString(value, pool); } template -inline bool variantSetOwnedString(VariantData *var, T value, MemoryPool *pool) { - return var != 0 && var->setOwnedString(value, pool); -} -inline bool variantSetUnsignedInteger(VariantData *var, UInt value) { - if (!var) return false; - var->setUnsignedInteger(value); +inline bool variantSetInteger(VariantData *var, T value) { + ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T); + if (!var) + return false; + var->setInteger(value); return true; } inline size_t variantSize(const VariantData *var) { return var != 0 ? var->size() : 0; } inline CollectionData *variantToArray(VariantData *var) { - if (!var) return 0; + if (!var) + return 0; return &var->toArray(); } inline CollectionData *variantToObject(VariantData *var) { - if (!var) return 0; + if (!var) + return 0; return &var->toObject(); } -inline NO_INLINE VariantData *variantAdd(VariantData *var, MemoryPool *pool) { +inline NO_INLINE VariantData *variantAddElement(VariantData *var, + MemoryPool *pool) { return var != 0 ? var->addElement(pool) : 0; } +inline NO_INLINE VariantData *variantGetOrAddElement(VariantData *var, + size_t index, + MemoryPool *pool) { + return var != 0 ? var->getOrAddElement(index, pool) : 0; +} template -NO_INLINE VariantData *variantGetOrCreate(VariantData *var, TChar *key, - MemoryPool *pool) { +NO_INLINE VariantData *variantGetOrAddMember(VariantData *var, TChar *key, + MemoryPool *pool) { return var != 0 ? var->getOrAddMember(adaptString(key), pool) : 0; } template -NO_INLINE VariantData *variantGetOrCreate(VariantData *var, const TString &key, - MemoryPool *pool) { +NO_INLINE VariantData *variantGetOrAddMember(VariantData *var, + const TString &key, + MemoryPool *pool) { return var != 0 ? var->getOrAddMember(adaptString(key), pool) : 0; } -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { +enum CompareResult { + COMPARE_RESULT_DIFFER = 0, + COMPARE_RESULT_EQUAL = 1, + COMPARE_RESULT_GREATER = 2, + COMPARE_RESULT_LESS = 4, + COMPARE_RESULT_GREATER_OR_EQUAL = 3, + COMPARE_RESULT_LESS_OR_EQUAL = 5 +}; +template +CompareResult arithmeticCompare(const T &lhs, const T &rhs) { + if (lhs < rhs) + return COMPARE_RESULT_LESS; + else if (lhs > rhs) + return COMPARE_RESULT_GREATER; + else + return COMPARE_RESULT_EQUAL; +} +template +CompareResult arithmeticCompare( + const T1 &lhs, const T2 &rhs, + typename enable_if::value && is_integral::value && + sizeof(T1) < sizeof(T2), + int // Using int instead of void to avoid C2572 on + >::type * = 0) { + return arithmeticCompare(static_cast(lhs), rhs); +} +template +CompareResult arithmeticCompare( + const T1 &lhs, const T2 &rhs, + typename enable_if::value && is_integral::value && + sizeof(T2) < sizeof(T1)>::type * = 0) { + return arithmeticCompare(lhs, static_cast(rhs)); +} +template +CompareResult arithmeticCompare( + const T1 &lhs, const T2 &rhs, + typename enable_if::value && is_integral::value && + is_signed::value == is_signed::value && + sizeof(T2) == sizeof(T1)>::type * = 0) { + return arithmeticCompare(lhs, static_cast(rhs)); +} +template +CompareResult arithmeticCompare( + const T1 &lhs, const T2 &rhs, + typename enable_if::value && is_integral::value && + is_unsigned::value && is_signed::value && + sizeof(T2) == sizeof(T1)>::type * = 0) { + if (rhs < 0) + return COMPARE_RESULT_GREATER; + return arithmeticCompare(lhs, static_cast(rhs)); +} +template +CompareResult arithmeticCompare( + const T1 &lhs, const T2 &rhs, + typename enable_if::value && is_integral::value && + is_signed::value && is_unsigned::value && + sizeof(T2) == sizeof(T1)>::type * = 0) { + if (lhs < 0) + return COMPARE_RESULT_LESS; + return arithmeticCompare(static_cast(lhs), rhs); +} +template +CompareResult arithmeticCompare( + const T1 &lhs, const T2 &rhs, + typename enable_if::value || + is_floating_point::value>::type * = 0) { + return arithmeticCompare(static_cast(lhs), + static_cast(rhs)); +} +template +CompareResult arithmeticCompareNegateLeft( + UInt, const T2 &, typename enable_if::value>::type * = 0) { + return COMPARE_RESULT_LESS; +} +template +CompareResult arithmeticCompareNegateLeft( + UInt lhs, const T2 &rhs, + typename enable_if::value>::type * = 0) { + if (rhs > 0) + return COMPARE_RESULT_LESS; + return arithmeticCompare(-rhs, static_cast(lhs)); +} +template +CompareResult arithmeticCompareNegateRight( + const T1 &, UInt, typename enable_if::value>::type * = 0) { + return COMPARE_RESULT_GREATER; +} +template +CompareResult arithmeticCompareNegateRight( + const T1 &lhs, UInt rhs, + typename enable_if::value>::type * = 0) { + if (lhs > 0) + return COMPARE_RESULT_GREATER; + return arithmeticCompare(static_cast(rhs), -lhs); +} +template +CompareResult compare(const T1 &lhs, const T2 &rhs); // VariantCompare.cpp +template +struct VariantOperators { + template + friend T operator|(const TVariant &variant, const T &defaultValue) { + if (variant.template is()) + return variant.template as(); + else + return defaultValue; + } + friend const char *operator|(const TVariant &variant, + const char *defaultValue) { + const char *value = variant.template as(); + return value ? value : defaultValue; + } + template + friend bool operator==(T *lhs, TVariant rhs) { + return compare(rhs, lhs) == COMPARE_RESULT_EQUAL; + } + template + friend bool operator==(const T &lhs, TVariant rhs) { + return compare(rhs, lhs) == COMPARE_RESULT_EQUAL; + } + template + friend bool operator==(TVariant lhs, T *rhs) { + return compare(lhs, rhs) == COMPARE_RESULT_EQUAL; + } + template + friend typename enable_if::value, bool>::type operator==( + TVariant lhs, const T &rhs) { + return compare(lhs, rhs) == COMPARE_RESULT_EQUAL; + } + template + friend bool operator!=(T *lhs, TVariant rhs) { + return compare(rhs, lhs) != COMPARE_RESULT_EQUAL; + } + template + friend bool operator!=(const T &lhs, TVariant rhs) { + return compare(rhs, lhs) != COMPARE_RESULT_EQUAL; + } + template + friend bool operator!=(TVariant lhs, T *rhs) { + return compare(lhs, rhs) != COMPARE_RESULT_EQUAL; + } + template + friend typename enable_if::value, bool>::type operator!=( + TVariant lhs, const T &rhs) { + return compare(lhs, rhs) != COMPARE_RESULT_EQUAL; + } + template + friend bool operator<(T *lhs, TVariant rhs) { + return compare(rhs, lhs) == COMPARE_RESULT_GREATER; + } + template + friend bool operator<(const T &lhs, TVariant rhs) { + return compare(rhs, lhs) == COMPARE_RESULT_GREATER; + } + template + friend bool operator<(TVariant lhs, T *rhs) { + return compare(lhs, rhs) == COMPARE_RESULT_LESS; + } + template + friend typename enable_if::value, bool>::type operator<( + TVariant lhs, const T &rhs) { + return compare(lhs, rhs) == COMPARE_RESULT_LESS; + } + template + friend bool operator<=(T *lhs, TVariant rhs) { + return (compare(rhs, lhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; + } + template + friend bool operator<=(const T &lhs, TVariant rhs) { + return (compare(rhs, lhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; + } + template + friend bool operator<=(TVariant lhs, T *rhs) { + return (compare(lhs, rhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; + } + template + friend typename enable_if::value, bool>::type operator<=( + TVariant lhs, const T &rhs) { + return (compare(lhs, rhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; + } + template + friend bool operator>(T *lhs, TVariant rhs) { + return compare(rhs, lhs) == COMPARE_RESULT_LESS; + } + template + friend bool operator>(const T &lhs, TVariant rhs) { + return compare(rhs, lhs) == COMPARE_RESULT_LESS; + } + template + friend bool operator>(TVariant lhs, T *rhs) { + return compare(lhs, rhs) == COMPARE_RESULT_GREATER; + } + template + friend typename enable_if::value, bool>::type operator>( + TVariant lhs, const T &rhs) { + return compare(lhs, rhs) == COMPARE_RESULT_GREATER; + } + template + friend bool operator>=(T *lhs, TVariant rhs) { + return (compare(rhs, lhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; + } + template + friend bool operator>=(const T &lhs, TVariant rhs) { + return (compare(rhs, lhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; + } + template + friend bool operator>=(TVariant lhs, T *rhs) { + return (compare(lhs, rhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; + } + template + friend typename enable_if::value, bool>::type operator>=( + TVariant lhs, const T &rhs) { + return (compare(lhs, rhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; + } +}; +template +class ElementProxy; +template +class ArrayShortcuts { + public: + FORCE_INLINE ElementProxy operator[](size_t index) const; + FORCE_INLINE ObjectRef createNestedObject() const; + FORCE_INLINE ArrayRef createNestedArray() const; + template + FORCE_INLINE bool add(const T &value) const { + return impl()->addElement().set(value); + } + template + FORCE_INLINE bool add(T *value) const { + return impl()->addElement().set(value); + } + private: + const TArray *impl() const { + return static_cast(this); + } +}; +template +class MemberProxy; +template +class ObjectShortcuts { + public: + template + FORCE_INLINE typename enable_if::value, bool>::type + containsKey(const TString &key) const; + template + FORCE_INLINE typename enable_if::value, bool>::type + containsKey(TChar *key) const; + template + FORCE_INLINE typename enable_if::value, + MemberProxy >::type + operator[](const TString &key) const; + template + FORCE_INLINE typename enable_if::value, + MemberProxy >::type + operator[](TChar *key) const; + template + FORCE_INLINE ArrayRef createNestedArray(const TString &key) const; + template + FORCE_INLINE ArrayRef createNestedArray(TChar *key) const; + template + ObjectRef createNestedObject(const TString &key) const; + template + ObjectRef createNestedObject(TChar *key) const; + private: + const TObject *impl() const { + return static_cast(this); + } +}; +template +class VariantShortcuts : public ObjectShortcuts, + public ArrayShortcuts { + public: + using ArrayShortcuts::createNestedArray; + using ArrayShortcuts::createNestedObject; + using ArrayShortcuts::operator[]; + using ObjectShortcuts::createNestedArray; + using ObjectShortcuts::createNestedObject; + using ObjectShortcuts::operator[]; +}; class ArrayRef; class ObjectRef; template @@ -2170,8 +2397,10 @@ template class VariantRefBase { public: template - FORCE_INLINE typename enable_if::value, bool>::type is() - const { + FORCE_INLINE + typename enable_if::value && !is_same::value, + bool>::type + is() const { return variantIsInteger(_data); } template @@ -2204,6 +2433,18 @@ class VariantRefBase { is() const { return variantIsObject(_data); } +#if ARDUINOJSON_HAS_NULLPTR + template + FORCE_INLINE + typename enable_if::value, bool>::type + is() const { + return variantIsNull(_data); + } +#endif + template + FORCE_INLINE typename enable_if::value, bool>::type is() const { + return variantIsInteger(_data); + } FORCE_INLINE bool isNull() const { return variantIsNull(_data); } @@ -2225,6 +2466,7 @@ class VariantRefBase { }; class VariantRef : public VariantRefBase, public VariantOperators, + public VariantShortcuts, public Visitable { typedef VariantRefBase base_type; friend class VariantConstRef; @@ -2235,7 +2477,9 @@ class VariantRef : public VariantRefBase, FORCE_INLINE void clear() const { return variantSetNull(_data); } - FORCE_INLINE bool set(bool value) const { + template + FORCE_INLINE bool set( + T value, typename enable_if::value>::type * = 0) const { return variantSetBoolean(_data, value); } template @@ -2245,17 +2489,10 @@ class VariantRef : public VariantRefBase, return variantSetFloat(_data, static_cast(value)); } template - FORCE_INLINE bool set( - T value, - typename enable_if::value && is_signed::value>::type * = - 0) const { - return variantSetSignedInteger(_data, value); - } - template FORCE_INLINE bool set( T value, typename enable_if::value && - is_unsigned::value>::type * = 0) const { - return variantSetUnsignedInteger(_data, static_cast(value)); + !is_same::value>::type * = 0) const { + return variantSetInteger(_data, value); } FORCE_INLINE bool set(SerializedValue value) const { return variantSetLinkedRaw(_data, value); @@ -2270,48 +2507,39 @@ class VariantRef : public VariantRefBase, FORCE_INLINE bool set( const T &value, typename enable_if::value>::type * = 0) const { - return variantSetOwnedString(_data, adaptString(value), _pool); + return variantSetString(_data, adaptString(value), _pool); } template FORCE_INLINE bool set( T *value, typename enable_if::value>::type * = 0) const { - return variantSetOwnedString(_data, adaptString(value), _pool); - } - FORCE_INLINE bool set(const char *value) const { - return variantSetLinkedString(_data, value); + return variantSetString(_data, adaptString(value), _pool); } template typename enable_if::value, bool>::type set( const TVariant &value) const; template - FORCE_INLINE typename enable_if::value && - !is_same::value && - !is_same::value, - typename VariantAs::type>::type - as() const { - return variantAs(_data); + FORCE_INLINE bool set( + T value, typename enable_if::value>::type * = 0) const { + return variantSetInteger(_data, static_cast(value)); + } +#if ARDUINOJSON_HAS_NULLPTR + FORCE_INLINE bool set(decltype(nullptr)) const { + variantSetNull(_data); + return true; + } +#endif + template + FORCE_INLINE typename VariantAs::type as() const { + return variantAs::type>(_data, _pool); } template - FORCE_INLINE typename enable_if::value, T>::type as() - const; - template - FORCE_INLINE typename enable_if::value, T>::type as() - const; - template - FORCE_INLINE typename enable_if::value, T>::type as() - const { - return *this; + FORCE_INLINE operator T() const { + return variantAs(_data, _pool); } template void accept(Visitor &visitor) const { variantAccept(_data, visitor); } - FORCE_INLINE bool operator==(VariantRef lhs) const { - return variantEquals(_data, lhs._data); - } - FORCE_INLINE bool operator!=(VariantRef lhs) const { - return !variantEquals(_data, lhs._data); - } template typename enable_if::value, ArrayRef>::type to() const; template @@ -2321,6 +2549,7 @@ class VariantRef : public VariantRefBase, const; VariantRef addElement() const; FORCE_INLINE VariantRef getElement(size_t) const; + FORCE_INLINE VariantRef getOrAddElement(size_t) const; template FORCE_INLINE VariantRef getMember(TChar *) const; template @@ -2331,23 +2560,27 @@ class VariantRef : public VariantRefBase, template FORCE_INLINE VariantRef getOrAddMember(const TString &) const; FORCE_INLINE void remove(size_t index) const { - if (_data) _data->remove(index); + if (_data) + _data->remove(index); } template FORCE_INLINE typename enable_if::value>::type remove( TChar *key) const { - if (_data) _data->remove(adaptString(key)); + if (_data) + _data->remove(adaptString(key)); } template FORCE_INLINE typename enable_if::value>::type remove( const TString &key) const { - if (_data) _data->remove(adaptString(key)); + if (_data) + _data->remove(adaptString(key)); } private: MemoryPool *_pool; }; // namespace ARDUINOJSON_NAMESPACE class VariantConstRef : public VariantRefBase, public VariantOperators, + public VariantShortcuts, public Visitable { typedef VariantRefBase base_type; friend class VariantRef; @@ -2363,23 +2596,37 @@ class VariantConstRef : public VariantRefBase, FORCE_INLINE typename VariantConstAs::type as() const { return variantAs::type>(_data); } - FORCE_INLINE VariantConstRef operator[](size_t index) const; + template + FORCE_INLINE operator T() const { + return variantAs(_data); + } + FORCE_INLINE VariantConstRef getElement(size_t) const; + FORCE_INLINE VariantConstRef operator[](size_t index) const { + return getElement(index); + } + template + FORCE_INLINE VariantConstRef getMember(const TString &key) const { + return VariantConstRef( + objectGetMember(variantAsObject(_data), adaptString(key))); + } + template + FORCE_INLINE VariantConstRef getMember(TChar *key) const { + const CollectionData *obj = variantAsObject(_data); + return VariantConstRef(obj ? obj->getMember(adaptString(key)) : 0); + } template FORCE_INLINE typename enable_if::value, VariantConstRef>::type operator[](const TString &key) const { - return VariantConstRef(objectGet(variantAsObject(_data), adaptString(key))); + return getMember(key); } template FORCE_INLINE typename enable_if::value, VariantConstRef>::type operator[](TChar *key) const { - const CollectionData *obj = variantAsObject(_data); - return VariantConstRef(obj ? obj->get(adaptString(key)) : 0); + return getMember(key); } }; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { class VariantPtr { public: VariantPtr(MemoryPool *pool, VariantData *data) : _variant(pool, data) {} @@ -2487,6 +2734,9 @@ class ArrayRefBase { FORCE_INLINE bool isNull() const { return _data == 0; } + FORCE_INLINE operator bool() const { + return _data != 0; + } FORCE_INLINE size_t memoryUsage() const { return _data ? _data->memoryUsage() : 0; } @@ -2507,7 +2757,8 @@ class ArrayConstRef : public ArrayRefBase, public: typedef ArrayConstRefIterator iterator; FORCE_INLINE iterator begin() const { - if (!_data) return iterator(); + if (!_data) + return iterator(); return iterator(_data->head()); } FORCE_INLINE iterator end() const { @@ -2522,7 +2773,7 @@ class ArrayConstRef : public ArrayRefBase, return getElement(index); } FORCE_INLINE VariantConstRef getElement(size_t index) const { - return VariantConstRef(_data ? _data->get(index) : 0); + return VariantConstRef(_data ? _data->getElement(index) : 0); } }; class ArrayRef : public ArrayRefBase, @@ -2545,35 +2796,40 @@ class ArrayRef : public ArrayRefBase, return VariantRef(_pool, arrayAdd(_data, _pool)); } FORCE_INLINE iterator begin() const { - if (!_data) return iterator(); + if (!_data) + return iterator(); return iterator(_pool, _data->head()); } FORCE_INLINE iterator end() const { return iterator(); } FORCE_INLINE bool set(ArrayConstRef src) const { - if (!_data || !src._data) return false; + if (!_data || !src._data) + return false; return _data->copyFrom(*src._data, _pool); } FORCE_INLINE bool operator==(ArrayRef rhs) const { return arrayEquals(_data, rhs._data); } + FORCE_INLINE VariantRef getOrAddElement(size_t index) const { + return VariantRef(_pool, _data ? _data->getOrAddElement(index, _pool) : 0); + } FORCE_INLINE VariantRef getElement(size_t index) const { - return VariantRef(_pool, _data ? _data->get(index) : 0); + return VariantRef(_pool, _data ? _data->getElement(index) : 0); } FORCE_INLINE void remove(iterator it) const { - if (!_data) return; - _data->remove(it.internal()); + if (!_data) + return; + _data->removeSlot(it.internal()); } FORCE_INLINE void remove(size_t index) const { - if (!_data) return; - _data->remove(index); + if (!_data) + return; + _data->removeElement(index); } private: MemoryPool* _pool; }; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { template void objectAccept(const CollectionData *obj, Visitor &visitor) { if (obj) @@ -2582,31 +2838,32 @@ void objectAccept(const CollectionData *obj, Visitor &visitor) { visitor.visitNull(); } inline bool objectEquals(const CollectionData *lhs, const CollectionData *rhs) { - if (lhs == rhs) return true; - if (!lhs || !rhs) return false; + if (lhs == rhs) + return true; + if (!lhs || !rhs) + return false; return lhs->equalsObject(*rhs); } template -inline VariantData *objectGet(const CollectionData *obj, TAdaptedString key) { - if (!obj) return 0; - return obj->get(key); +inline VariantData *objectGetMember(const CollectionData *obj, + TAdaptedString key) { + if (!obj) + return 0; + return obj->getMember(key); } template void objectRemove(CollectionData *obj, TAdaptedString key) { - if (!obj) return; - obj->remove(key); + if (!obj) + return; + obj->removeMember(key); } template -inline VariantData *objectGetOrCreate(CollectionData *obj, TAdaptedString key, - MemoryPool *pool) { - if (!obj) return 0; - if (key.isNull()) return 0; - VariantData *var = obj->get(key); - if (var) return var; - return obj->add(key, pool); +inline VariantData *objectGetOrAddMember(CollectionData *obj, + TAdaptedString key, MemoryPool *pool) { + if (!obj) + return 0; + return obj->getOrAddMember(key, pool); } -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { class String { public: String() : _data(0), _isStatic(true) {} @@ -2622,11 +2879,23 @@ class String { return _isStatic; } friend bool operator==(String lhs, String rhs) { - if (lhs._data == rhs._data) return true; - if (!lhs._data) return false; - if (!rhs._data) return false; + if (lhs._data == rhs._data) + return true; + if (!lhs._data) + return false; + if (!rhs._data) + return false; return strcmp(lhs._data, rhs._data) == 0; } + friend bool operator!=(String lhs, String rhs) { + if (lhs._data == rhs._data) + return false; + if (!lhs._data) + return true; + if (!rhs._data) + return true; + return strcmp(lhs._data, rhs._data) != 0; + } private: const char* _data; bool _isStatic; @@ -2638,10 +2907,7 @@ class StringAdapter : public RamStringAdapter { bool isStatic() const { return _isStatic; } - /* const char* save(MemoryPool* pool) const { - if (_isStatic) return c_str(); - return RamStringAdapter::save(pool); - }*/ + typedef storage_policies::decide_at_runtime storage_policy; private: bool _isStatic; }; @@ -2650,8 +2916,6 @@ struct IsString : true_type {}; inline StringAdapter adaptString(const String& str) { return StringAdapter(str); } -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { class Pair { public: Pair(MemoryPool* pool, VariantSlot* slot) { @@ -2688,8 +2952,6 @@ class PairConst { String _key; VariantConstRef _value; }; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { class PairPtr { public: PairPtr(MemoryPool *pool, VariantSlot *slot) : _pair(pool, slot) {} @@ -2794,6 +3056,9 @@ class ObjectRefBase { FORCE_INLINE bool isNull() const { return _data == 0; } + FORCE_INLINE operator bool() const { + return _data != 0; + } FORCE_INLINE size_t memoryUsage() const { return _data ? _data->memoryUsage() : 0; } @@ -2816,7 +3081,8 @@ class ObjectConstRef : public ObjectRefBase, ObjectConstRef() : base_type(0) {} ObjectConstRef(const CollectionData* data) : base_type(data) {} FORCE_INLINE iterator begin() const { - if (!_data) return iterator(); + if (!_data) + return iterator(); return iterator(_data->head()); } FORCE_INLINE iterator end() const { @@ -2856,7 +3122,7 @@ class ObjectConstRef : public ObjectRefBase, private: template FORCE_INLINE VariantConstRef get_impl(TAdaptedString key) const { - return VariantConstRef(objectGet(_data, key)); + return VariantConstRef(objectGetMember(_data, key)); } }; class ObjectRef : public ObjectRefBase, @@ -2876,42 +3142,48 @@ class ObjectRef : public ObjectRefBase, return ObjectConstRef(_data); } FORCE_INLINE iterator begin() const { - if (!_data) return iterator(); + if (!_data) + return iterator(); return iterator(_pool, _data->head()); } FORCE_INLINE iterator end() const { return iterator(); } void clear() const { - if (!_data) return; + if (!_data) + return; _data->clear(); } FORCE_INLINE bool set(ObjectConstRef src) { - if (!_data || !src._data) return false; + if (!_data || !src._data) + return false; return _data->copyFrom(*src._data, _pool); } template FORCE_INLINE VariantRef getMember(const TString& key) const { - return get_impl(adaptString(key)); + return VariantRef(_pool, objectGetMember(_data, adaptString(key))); } template FORCE_INLINE VariantRef getMember(TChar* key) const { - return get_impl(adaptString(key)); + return VariantRef(_pool, objectGetMember(_data, adaptString(key))); } template FORCE_INLINE VariantRef getOrAddMember(const TString& key) const { - return getOrCreate_impl(adaptString(key)); + return VariantRef(_pool, + objectGetOrAddMember(_data, adaptString(key), _pool)); } template FORCE_INLINE VariantRef getOrAddMember(TChar* key) const { - return getOrCreate_impl(adaptString(key)); + return VariantRef(_pool, + objectGetOrAddMember(_data, adaptString(key), _pool)); } FORCE_INLINE bool operator==(ObjectRef rhs) const { return objectEquals(_data, rhs._data); } FORCE_INLINE void remove(iterator it) const { - if (!_data) return; - _data->remove(it.internal()); + if (!_data) + return; + _data->removeSlot(it.internal()); } template FORCE_INLINE void remove(const TString& key) const { @@ -2922,18 +3194,8 @@ class ObjectRef : public ObjectRefBase, objectRemove(_data, adaptString(key)); } private: - template - FORCE_INLINE VariantRef get_impl(TAdaptedString key) const { - return VariantRef(_pool, objectGet(_data, key)); - } - template - FORCE_INLINE VariantRef getOrCreate_impl(TAdaptedString key) const { - return VariantRef(_pool, objectGetOrCreate(_data, key, _pool)); - } MemoryPool* _pool; }; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { class ArrayRef; class ObjectRef; class VariantRef; @@ -2959,23 +3221,26 @@ struct VariantTo { namespace ARDUINOJSON_NAMESPACE { template class ElementProxy : public VariantOperators >, + public VariantShortcuts >, public Visitable { typedef ElementProxy this_type; public: FORCE_INLINE ElementProxy(TArray array, size_t index) : _array(array), _index(index) {} + FORCE_INLINE ElementProxy(const ElementProxy& src) + : _array(src._array), _index(src._index) {} FORCE_INLINE this_type& operator=(const this_type& src) { - getUpstreamElement().set(src.as()); + getOrAddUpstreamElement().set(src.as()); return *this; } template FORCE_INLINE this_type& operator=(const T& src) { - getUpstreamElement().set(src); + getOrAddUpstreamElement().set(src); return *this; } template FORCE_INLINE this_type& operator=(T* src) { - getUpstreamElement().set(src); + getOrAddUpstreamElement().set(src); return *this; } FORCE_INLINE void clear() const { @@ -2989,20 +3254,24 @@ class ElementProxy : public VariantOperators >, return getUpstreamElement().template as(); } template + FORCE_INLINE operator T() const { + return getUpstreamElement(); + } + template FORCE_INLINE bool is() const { return getUpstreamElement().template is(); } template FORCE_INLINE typename VariantTo::type to() const { - return getUpstreamElement().template to(); + return getOrAddUpstreamElement().template to(); } template FORCE_INLINE bool set(const TValue& value) const { - return getUpstreamElement().set(value); + return getOrAddUpstreamElement().set(value); } template FORCE_INLINE bool set(TValue* value) const { - return getUpstreamElement().set(value); + return getOrAddUpstreamElement().set(value); } template void accept(Visitor& visitor) const { @@ -3021,17 +3290,20 @@ class ElementProxy : public VariantOperators >, } template VariantRef getOrAddMember(TNestedKey* key) const { - return getUpstreamElement().getOrAddMember(key); + return getOrAddUpstreamElement().getOrAddMember(key); } template VariantRef getOrAddMember(const TNestedKey& key) const { - return getUpstreamElement().getOrAddMember(key); + return getOrAddUpstreamElement().getOrAddMember(key); } VariantRef addElement() const { - return getUpstreamElement().addElement(); + return getOrAddUpstreamElement().addElement(); } VariantRef getElement(size_t index) const { - return getUpstreamElement().getElement(index); + return getOrAddUpstreamElement().getElement(index); + } + VariantRef getOrAddElement(size_t index) const { + return getOrAddUpstreamElement().getOrAddElement(index); } FORCE_INLINE void remove(size_t index) const { getUpstreamElement().remove(index); @@ -3050,14 +3322,12 @@ class ElementProxy : public VariantOperators >, FORCE_INLINE VariantRef getUpstreamElement() const { return _array.getElement(_index); } + FORCE_INLINE VariantRef getOrAddUpstreamElement() const { + return _array.getOrAddElement(_index); + } TArray _array; const size_t _index; }; -template -inline ElementProxy ArrayShortcuts::operator[]( - size_t index) const { - return ElementProxy(*impl(), index); -} } // namespace ARDUINOJSON_NAMESPACE #ifdef _MSC_VER #pragma warning(pop) @@ -3069,11 +3339,14 @@ inline ElementProxy ArrayShortcuts::operator[]( namespace ARDUINOJSON_NAMESPACE { template class MemberProxy : public VariantOperators >, + public VariantShortcuts >, public Visitable { typedef MemberProxy this_type; public: FORCE_INLINE MemberProxy(TObject variant, TStringRef key) : _object(variant), _key(key) {} + FORCE_INLINE MemberProxy(const MemberProxy &src) + : _object(src._object), _key(src._key) {} FORCE_INLINE operator VariantConstRef() const { return getUpstreamMember(); } @@ -3102,6 +3375,10 @@ class MemberProxy : public VariantOperators >, FORCE_INLINE typename VariantAs::type as() const { return getUpstreamMember().template as(); } + template + FORCE_INLINE operator T() const { + return getUpstreamMember(); + } template FORCE_INLINE bool is() const { return getUpstreamMember().template is(); @@ -3127,12 +3404,11 @@ class MemberProxy : public VariantOperators >, return getOrAddUpstreamMember().template to(); } template - FORCE_INLINE typename enable_if::value, bool>::type set( - const TValue &value) { + FORCE_INLINE bool set(const TValue &value) { return getOrAddUpstreamMember().set(value); } template - FORCE_INLINE bool set(const TChar *value) { + FORCE_INLINE bool set(TChar *value) { return getOrAddUpstreamMember().set(value); } template @@ -3145,6 +3421,9 @@ class MemberProxy : public VariantOperators >, FORCE_INLINE VariantRef getElement(size_t index) const { return getUpstreamMember().getElement(index); } + FORCE_INLINE VariantRef getOrAddElement(size_t index) const { + return getOrAddUpstreamMember().getOrAddElement(index); + } template FORCE_INLINE VariantRef getMember(TChar *key) const { return getUpstreamMember().getMember(key); @@ -3171,20 +3450,6 @@ class MemberProxy : public VariantOperators >, TObject _object; TStringRef _key; }; -template -template -inline typename enable_if::value, - MemberProxy >::type - ObjectShortcuts::operator[](const TString &key) const { - return MemberProxy(*impl(), key); -} -template -template -inline typename enable_if::value, - MemberProxy >::type - ObjectShortcuts::operator[](TString *key) const { - return MemberProxy(*impl(), key); -} } // namespace ARDUINOJSON_NAMESPACE #ifdef _MSC_VER #pragma warning(pop) @@ -3277,11 +3542,10 @@ class JsonDocument : public Visitable { return !getMember(key).isUndefined(); } template - FORCE_INLINE - typename enable_if::value, - MemberProxy >::type - operator[](const TString& key) { - return MemberProxy(*this, key); + FORCE_INLINE typename enable_if::value, + MemberProxy >::type + operator[](const TString& key) { + return MemberProxy(*this, key); } template FORCE_INLINE typename enable_if::value, @@ -3313,6 +3577,9 @@ class JsonDocument : public Visitable { FORCE_INLINE VariantConstRef getElement(size_t index) const { return VariantConstRef(_data.getElement(index)); } + FORCE_INLINE VariantRef getOrAddElement(size_t index) { + return VariantRef(&_pool, _data.getOrAddElement(index, &_pool)); + } template FORCE_INLINE VariantConstRef getMember(TChar* key) const { return VariantConstRef(_data.getMember(adaptString(key))); @@ -3364,7 +3631,19 @@ class JsonDocument : public Visitable { const TString& key) { _data.remove(adaptString(key)); } + FORCE_INLINE operator VariantConstRef() const { + return VariantConstRef(&_data); + } + bool operator==(VariantConstRef rhs) const { + return getVariant() == rhs; + } + bool operator!=(VariantConstRef rhs) const { + return getVariant() != rhs; + } protected: + JsonDocument() : _pool(0, 0) { + _data.setNull(); + } JsonDocument(MemoryPool pool) : _pool(pool) { _data.setNull(); } @@ -3374,7 +3653,6 @@ class JsonDocument : public Visitable { void replacePool(MemoryPool pool) { _pool = pool; } - private: VariantRef getVariant() { return VariantRef(&_pool, &_data); } @@ -3383,20 +3661,28 @@ class JsonDocument : public Visitable { } MemoryPool _pool; VariantData _data; + private: + JsonDocument(const JsonDocument&); + JsonDocument& operator=(const JsonDocument&); }; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { template class AllocatorOwner { - protected: + public: AllocatorOwner() {} AllocatorOwner(const AllocatorOwner& src) : _allocator(src._allocator) {} - AllocatorOwner(TAllocator allocator) : _allocator(allocator) {} - void* allocate(size_t n) { - return _allocator.allocate(n); + AllocatorOwner(TAllocator a) : _allocator(a) {} + void* allocate(size_t size) { + return _allocator.allocate(size); } - void deallocate(void* p) { - _allocator.deallocate(p); + void deallocate(void* ptr) { + if (ptr) + _allocator.deallocate(ptr); + } + void* reallocate(void* ptr, size_t new_size) { + return _allocator.reallocate(ptr, new_size); + } + TAllocator& allocator() { + return _allocator; } private: TAllocator _allocator; @@ -3404,16 +3690,28 @@ class AllocatorOwner { template class BasicJsonDocument : AllocatorOwner, public JsonDocument { public: - explicit BasicJsonDocument(size_t capa, TAllocator allocator = TAllocator()) - : AllocatorOwner(allocator), JsonDocument(allocPool(capa)) {} + explicit BasicJsonDocument(size_t capa, TAllocator alloc = TAllocator()) + : AllocatorOwner(alloc), JsonDocument(allocPool(capa)) {} BasicJsonDocument(const BasicJsonDocument& src) - : AllocatorOwner(src), - JsonDocument(allocPool(src.memoryUsage())) { - set(src); + : AllocatorOwner(src), JsonDocument() { + copyAssignFrom(src); + } +#if ARDUINOJSON_HAS_RVALUE_REFERENCES + BasicJsonDocument(BasicJsonDocument&& src) : AllocatorOwner(src) { + moveAssignFrom(src); + } +#endif + BasicJsonDocument(const JsonDocument& src) { + copyAssignFrom(src); } template - BasicJsonDocument(const T& src, - typename enable_if::value>::type* = 0) + BasicJsonDocument( + const T& src, + typename enable_if< + is_same::value || is_same::value || + is_same::value || is_same::value || + is_same::value || + is_same::value>::type* = 0) : JsonDocument(allocPool(src.memoryUsage())) { set(src); } @@ -3425,43 +3723,79 @@ class BasicJsonDocument : AllocatorOwner, public JsonDocument { freePool(); } BasicJsonDocument& operator=(const BasicJsonDocument& src) { - reallocPoolIfTooSmall(src.memoryUsage()); - set(src); + copyAssignFrom(src); return *this; } +#if ARDUINOJSON_HAS_RVALUE_REFERENCES + BasicJsonDocument& operator=(BasicJsonDocument&& src) { + moveAssignFrom(src); + return *this; + } +#endif template BasicJsonDocument& operator=(const T& src) { reallocPoolIfTooSmall(src.memoryUsage()); set(src); return *this; } + void shrinkToFit() { + ptrdiff_t bytes_reclaimed = _pool.squash(); + if (bytes_reclaimed == 0) + return; + void* old_ptr = _pool.buffer(); + void* new_ptr = this->reallocate(old_ptr, _pool.capacity()); + ptrdiff_t ptr_offset = + static_cast(new_ptr) - static_cast(old_ptr); + _pool.movePointers(ptr_offset); + _data.movePointers(ptr_offset, ptr_offset - bytes_reclaimed); + } + bool garbageCollect() { + BasicJsonDocument tmp(*this); + if (!tmp.capacity()) + return false; + tmp.set(*this); + moveAssignFrom(tmp); + return true; + } + using AllocatorOwner::allocator; private: MemoryPool allocPool(size_t requiredSize) { size_t capa = addPadding(requiredSize); return MemoryPool(reinterpret_cast(this->allocate(capa)), capa); } void reallocPoolIfTooSmall(size_t requiredSize) { - if (requiredSize <= capacity()) return; + if (requiredSize <= capacity()) + return; freePool(); replacePool(allocPool(addPadding(requiredSize))); } void freePool() { this->deallocate(memoryPool().buffer()); } -}; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { -struct DefaultAllocator { - void* allocate(size_t n) { - return malloc(n); + void copyAssignFrom(const JsonDocument& src) { + reallocPoolIfTooSmall(src.capacity()); + set(src); } - void deallocate(void* p) { - free(p); + void moveAssignFrom(BasicJsonDocument& src) { + freePool(); + _data = src._data; + _pool = src._pool; + src._data.setNull(); + src._pool = MemoryPool(0, 0); + } +}; +struct DefaultAllocator { + void* allocate(size_t size) { + return malloc(size); + } + void deallocate(void* ptr) { + free(ptr); + } + void* reallocate(void* ptr, size_t new_size) { + return realloc(ptr, new_size); } }; typedef BasicJsonDocument DynamicJsonDocument; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { template class StaticJsonDocument : public JsonDocument { static const size_t _capacity = @@ -3490,11 +3824,13 @@ class StaticJsonDocument : public JsonDocument { set(src); return *this; } + void garbageCollect() { + StaticJsonDocument tmp(*this); + set(tmp); + } private: char _buffer[_capacity]; }; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { template inline ArrayRef ArrayShortcuts::createNestedArray() const { return impl()->addElement().template to(); @@ -3503,22 +3839,41 @@ template inline ObjectRef ArrayShortcuts::createNestedObject() const { return impl()->addElement().template to(); } -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { -template -inline bool copyArray(T (&src)[N], ArrayRef dst) { +template +inline ElementProxy ArrayShortcuts::operator[]( + size_t index) const { + return ElementProxy(*impl(), index); +} +template +inline typename enable_if::value && + !is_base_of::value, + bool>::type +copyArray(T (&src)[N], const TDestination& dst) { return copyArray(src, N, dst); } -template -inline bool copyArray(T* src, size_t len, ArrayRef dst) { +template +inline bool copyArray(T (&src)[N], JsonDocument& dst) { + return copyArray(src, dst.to()); +} +template +inline typename enable_if::value && + !is_base_of::value, + bool>::type +copyArray(T* src, size_t len, const TDestination& dst) { bool ok = true; for (size_t i = 0; i < len; i++) { ok &= dst.add(src[i]); } return ok; } -template -inline bool copyArray(T (&src)[N1][N2], ArrayRef dst) { +template +inline bool copyArray(T* src, size_t len, JsonDocument& dst) { + return copyArray(src, len, dst.to()); +} +template +inline typename enable_if::value, + bool>::type +copyArray(T (&src)[N1][N2], const TDestination& dst) { bool ok = true; for (size_t i = 0; i < N1; i++) { ArrayRef nestedArray = dst.createNestedArray(); @@ -3528,31 +3883,86 @@ inline bool copyArray(T (&src)[N1][N2], ArrayRef dst) { } return ok; } -template -inline size_t copyArray(ArrayConstRef src, T (&dst)[N]) { - return copyArray(src, dst, N); +template +inline bool copyArray(T (&src)[N1][N2], JsonDocument& dst) { + return copyArray(src, dst.to()); } template -inline size_t copyArray(ArrayConstRef src, T* dst, size_t len) { - size_t i = 0; - for (ArrayConstRef::iterator it = src.begin(); it != src.end() && i < len; - ++it) - dst[i++] = *it; - return i; -} -template -inline void copyArray(ArrayConstRef src, T (&dst)[N1][N2]) { - size_t i = 0; - for (ArrayConstRef::iterator it = src.begin(); it != src.end() && i < N1; - ++it) { - copyArray(it->as(), dst[i++]); +class ArrayCopier1D { + public: + ArrayCopier1D(T* destination, size_t capacity) + : _destination(destination), _capacity(capacity), _size(0) {} + void visitArray(const CollectionData& array) { + VariantSlot* slot = array.head(); + while (slot != 0 && _size < _capacity) { + _destination[_size++] = variantAs(slot->data()); + slot = slot->next(); + } } + void visitObject(const CollectionData&) {} + void visitFloat(Float) {} + void visitString(const char*) {} + void visitRawJson(const char*, size_t) {} + void visitNegativeInteger(UInt) {} + void visitPositiveInteger(UInt) {} + void visitBoolean(bool) {} + void visitNull() {} + size_t result() const { + return _size; + } + private: + T* _destination; + size_t _capacity; + size_t _size; +}; +template +class ArrayCopier2D { + public: + ArrayCopier2D(T (*destination)[N1][N2]) : _destination(destination) {} + void visitArray(const CollectionData& array) { + VariantSlot* slot = array.head(); + size_t n = 0; + while (slot != 0 && n < N1) { + ArrayCopier1D copier((*_destination)[n++], N2); + variantAccept(slot->data(), copier); + slot = slot->next(); + } + } + void visitObject(const CollectionData&) {} + void visitFloat(Float) {} + void visitString(const char*) {} + void visitRawJson(const char*, size_t) {} + void visitNegativeInteger(UInt) {} + void visitPositiveInteger(UInt) {} + void visitBoolean(bool) {} + void visitNull() {} + private: + T (*_destination)[N1][N2]; + size_t _capacity1, _capacity2; +}; +template +inline typename enable_if::value, size_t>::type copyArray( + const TSource& src, T (&dst)[N]) { + return copyArray(src, dst, N); +} +template +inline size_t copyArray(const TSource& src, T* dst, size_t len) { + ArrayCopier1D copier(dst, len); + src.accept(copier); + return copier.result(); +} +template +inline void copyArray(const TSource& src, T (&dst)[N1][N2]) { + ArrayCopier2D copier(&dst); + src.accept(copier); +} +inline bool variantEquals(const VariantData* a, const VariantData* b) { + return variantCompare(a, b) == COMPARE_RESULT_EQUAL; } -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { inline VariantSlot* CollectionData::addSlot(MemoryPool* pool) { VariantSlot* slot = pool->allocVariant(); - if (!slot) return 0; + if (!slot) + return 0; if (_tail) { _tail->setNextNotNull(slot); _tail = slot; @@ -3563,13 +3973,17 @@ inline VariantSlot* CollectionData::addSlot(MemoryPool* pool) { slot->clear(); return slot; } -inline VariantData* CollectionData::add(MemoryPool* pool) { +inline VariantData* CollectionData::addElement(MemoryPool* pool) { return slotData(addSlot(pool)); } template -inline VariantData* CollectionData::add(TAdaptedString key, MemoryPool* pool) { +inline VariantData* CollectionData::addMember(TAdaptedString key, + MemoryPool* pool) { VariantSlot* slot = addSlot(pool); - if (!slotSetKey(slot, key, pool)) return 0; + if (!slotSetKey(slot, key, pool)) { + removeSlot(slot); + return 0; + } return slot->data(); } inline void CollectionData::clear() { @@ -3587,14 +4001,16 @@ inline bool CollectionData::copyFrom(const CollectionData& src, VariantData* var; if (s->key() != 0) { if (s->ownsKey()) - var = add(RamStringAdapter(s->key()), pool); + var = addMember(RamStringAdapter(s->key()), pool); else - var = add(ConstRamStringAdapter(s->key()), pool); + var = addMember(ConstRamStringAdapter(s->key()), pool); } else { - var = add(pool); + var = addElement(pool); } - if (!var) return false; - if (!var->copyFrom(*s->data(), pool)) return false; + if (!var) + return false; + if (!var->copyFrom(*s->data(), pool)) + return false; } return true; } @@ -3602,8 +4018,9 @@ inline bool CollectionData::equalsObject(const CollectionData& other) const { size_t count = 0; for (VariantSlot* slot = _head; slot; slot = slot->next()) { VariantData* v1 = slot->data(); - VariantData* v2 = other.get(adaptString(slot->key())); - if (!variantEquals(v1, v2)) return false; + VariantData* v2 = other.getMember(adaptString(slot->key())); + if (!variantEquals(v1, v2)) + return false; count++; } return count == other.size(); @@ -3612,9 +4029,12 @@ inline bool CollectionData::equalsArray(const CollectionData& other) const { VariantSlot* s1 = _head; VariantSlot* s2 = other._head; for (;;) { - if (s1 == s2) return true; - if (!s1 || !s2) return false; - if (!variantEquals(s1->data(), s2->data())) return false; + if (s1 == s2) + return true; + if (!s1 || !s2) + return false; + if (!variantEquals(s1->data(), s2->data())) + return false; s1 = s1->next(); s2 = s2->next(); } @@ -3623,7 +4043,8 @@ template inline VariantSlot* CollectionData::getSlot(TAdaptedString key) const { VariantSlot* slot = _head; while (slot) { - if (key.equals(slot->key())) break; + if (key.equals(slot->key())) + break; slot = slot->next(); } return slot; @@ -3635,38 +4056,67 @@ inline VariantSlot* CollectionData::getPreviousSlot(VariantSlot* target) const { VariantSlot* current = _head; while (current) { VariantSlot* next = current->next(); - if (next == target) return current; + if (next == target) + return current; current = next; } return 0; } template -inline VariantData* CollectionData::get(TAdaptedString key) const { +inline VariantData* CollectionData::getMember(TAdaptedString key) const { VariantSlot* slot = getSlot(key); return slot ? slot->data() : 0; } -inline VariantData* CollectionData::get(size_t index) const { +template +inline VariantData* CollectionData::getOrAddMember(TAdaptedString key, + MemoryPool* pool) { + if (key.isNull()) + return 0; + VariantSlot* slot = getSlot(key); + if (slot) + return slot->data(); + return addMember(key, pool); +} +inline VariantData* CollectionData::getElement(size_t index) const { VariantSlot* slot = getSlot(index); return slot ? slot->data() : 0; } -inline void CollectionData::remove(VariantSlot* slot) { - if (!slot) return; +inline VariantData* CollectionData::getOrAddElement(size_t index, + MemoryPool* pool) { + VariantSlot* slot = _head; + while (slot && index > 0) { + slot = slot->next(); + index--; + } + if (!slot) + index++; + while (index > 0) { + slot = addSlot(pool); + index--; + } + return slotData(slot); +} +inline void CollectionData::removeSlot(VariantSlot* slot) { + if (!slot) + return; VariantSlot* prev = getPreviousSlot(slot); VariantSlot* next = slot->next(); if (prev) prev->setNext(next); else _head = next; - if (!next) _tail = prev; + if (!next) + _tail = prev; } -inline void CollectionData::remove(size_t index) { - remove(getSlot(index)); +inline void CollectionData::removeElement(size_t index) { + removeSlot(getSlot(index)); } inline size_t CollectionData::memoryUsage() const { size_t total = 0; for (VariantSlot* s = _head; s; s = s->next()) { total += sizeof(VariantSlot) + s->data()->memoryUsage(); - if (s->ownsKey()) total += strlen(s->key()) + 1; + if (s->ownsKey()) + total += strlen(s->key()) + 1; } return total; } @@ -3674,15 +4124,29 @@ inline size_t CollectionData::nesting() const { size_t maxChildNesting = 0; for (VariantSlot* s = _head; s; s = s->next()) { size_t childNesting = s->data()->nesting(); - if (childNesting > maxChildNesting) maxChildNesting = childNesting; + if (childNesting > maxChildNesting) + maxChildNesting = childNesting; } return maxChildNesting + 1; } inline size_t CollectionData::size() const { return slotSize(_head); } -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { +template +inline void movePointer(T*& p, ptrdiff_t offset) { + if (!p) + return; + p = reinterpret_cast( + reinterpret_cast(reinterpret_cast(p) + offset)); + ARDUINOJSON_ASSERT(isAligned(p)); +} +inline void CollectionData::movePointers(ptrdiff_t stringDistance, + ptrdiff_t variantDistance) { + movePointer(_head, variantDistance); + movePointer(_tail, variantDistance); + for (VariantSlot* slot = _head; slot; slot = slot->next()) + slot->movePointers(stringDistance, variantDistance); +} template template inline ArrayRef ObjectShortcuts::createNestedArray( @@ -3718,8 +4182,20 @@ inline typename enable_if::value, bool>::type ObjectShortcuts::containsKey(TChar* key) const { return !impl()->getMember(key).isUndefined(); } -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { +template +template +inline typename enable_if::value, + MemberProxy >::type + ObjectShortcuts::operator[](TString* key) const { + return MemberProxy(*impl(), key); +} +template +template +inline typename enable_if::value, + MemberProxy >::type + ObjectShortcuts::operator[](const TString& key) const { + return MemberProxy(*impl(), key); +} template inline typename enable_if::value, T>::type variantAs( const VariantData* _data) { @@ -3739,30 +4215,222 @@ template inline typename enable_if::value, T>::type variantAs( const VariantData* _data) { const char* cstr = _data != 0 ? _data->asString() : 0; - if (cstr) return T(cstr); + if (cstr) + return T(cstr); T s; serializeJson(VariantConstRef(_data), s); return s; } -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { +template <> +inline ArrayRef variantAs(VariantData* data, MemoryPool* pool) { + return ArrayRef(pool, data != 0 ? data->asArray() : 0); +} +template <> +inline ObjectRef variantAs(VariantData* data, MemoryPool* pool) { + return ObjectRef(pool, data != 0 ? data->asObject() : 0); +} +template <> +inline VariantRef variantAs(VariantData* data, MemoryPool* pool) { + return VariantRef(pool, data); +} +class CollectionData; +struct ComparerBase { + CompareResult result; + ComparerBase() : result(COMPARE_RESULT_DIFFER) {} + void visitArray(const CollectionData &) {} + void visitBoolean(bool) {} + void visitFloat(Float) {} + void visitNegativeInteger(UInt) {} + void visitNull() {} + void visitObject(const CollectionData &) {} + void visitPositiveInteger(UInt) {} + void visitRawJson(const char *, size_t) {} + void visitString(const char *) {} +}; +template +struct Comparer; +template +struct Comparer::value>::type> + : ComparerBase { + T rhs; + explicit Comparer(T value) : rhs(value) {} + void visitString(const char *lhs) { + int i = adaptString(rhs).compare(lhs); + if (i < 0) + result = COMPARE_RESULT_GREATER; + else if (i > 0) + result = COMPARE_RESULT_LESS; + else + result = COMPARE_RESULT_EQUAL; + } + void visitNull() { + if (adaptString(rhs).isNull()) + result = COMPARE_RESULT_EQUAL; + } +}; +template +struct Comparer::value || + is_floating_point::value>::type> + : ComparerBase { + T rhs; + explicit Comparer(T value) : rhs(value) {} + void visitFloat(Float lhs) { + result = arithmeticCompare(lhs, rhs); + } + void visitNegativeInteger(UInt lhs) { + result = arithmeticCompareNegateLeft(lhs, rhs); + } + void visitPositiveInteger(UInt lhs) { + result = arithmeticCompare(lhs, rhs); + } + void visitBoolean(bool lhs) { + visitPositiveInteger(static_cast(lhs)); + } +}; +struct NullComparer : ComparerBase { + void visitNull() { + result = COMPARE_RESULT_EQUAL; + } +}; +#if ARDUINOJSON_HAS_NULLPTR +template <> +struct Comparer : NullComparer { + explicit Comparer(decltype(nullptr)) : NullComparer() {} +}; +#endif +struct ArrayComparer : ComparerBase { + const CollectionData *_rhs; + explicit ArrayComparer(const CollectionData &rhs) : _rhs(&rhs) {} + void visitArray(const CollectionData &lhs) { + if (lhs.equalsArray(*_rhs)) + result = COMPARE_RESULT_EQUAL; + } +}; +struct NegativeIntegerComparer : ComparerBase { + UInt _rhs; + explicit NegativeIntegerComparer(UInt rhs) : _rhs(rhs) {} + void visitFloat(Float lhs) { + result = arithmeticCompareNegateRight(lhs, _rhs); + } + void visitNegativeInteger(UInt lhs) { + result = arithmeticCompare(_rhs, lhs); + } + void visitPositiveInteger(UInt) { + result = COMPARE_RESULT_GREATER; + } + void visitBoolean(bool) { + result = COMPARE_RESULT_GREATER; + } +}; +struct ObjectComparer : ComparerBase { + const CollectionData *_rhs; + explicit ObjectComparer(const CollectionData &rhs) : _rhs(&rhs) {} + void visitObject(const CollectionData &lhs) { + if (lhs.equalsObject(*_rhs)) + result = COMPARE_RESULT_EQUAL; + } +}; +struct RawComparer : ComparerBase { + const char *_rhsData; + size_t _rhsSize; + explicit RawComparer(const char *rhsData, size_t rhsSize) + : _rhsData(rhsData), _rhsSize(rhsSize) {} + void visitRawJson(const char *lhsData, size_t lhsSize) { + size_t size = _rhsSize < lhsSize ? _rhsSize : lhsSize; + int n = memcmp(lhsData, _rhsData, size); + if (n < 0) + result = COMPARE_RESULT_LESS; + else if (n > 0) + result = COMPARE_RESULT_GREATER; + else + result = COMPARE_RESULT_EQUAL; + } +}; +template +struct Comparer::value>::type> + : ComparerBase { + T rhs; + explicit Comparer(T value) : rhs(value) {} + void visitArray(const CollectionData &lhs) { + ArrayComparer comparer(lhs); + accept(comparer); + } + void visitObject(const CollectionData &lhs) { + ObjectComparer comparer(lhs); + accept(comparer); + } + void visitFloat(Float lhs) { + Comparer comparer(lhs); + accept(comparer); + } + void visitString(const char *lhs) { + Comparer comparer(lhs); + accept(comparer); + } + void visitRawJson(const char *lhsData, size_t lhsSize) { + RawComparer comparer(lhsData, lhsSize); + accept(comparer); + } + void visitNegativeInteger(UInt lhs) { + NegativeIntegerComparer comparer(lhs); + accept(comparer); + } + void visitPositiveInteger(UInt lhs) { + Comparer comparer(lhs); + accept(comparer); + } + void visitBoolean(bool lhs) { + Comparer comparer(lhs); + accept(comparer); + } + void visitNull() { + NullComparer comparer; + accept(comparer); + } + private: + template + void accept(TComparer &comparer) { + rhs.accept(comparer); + switch (comparer.result) { + case COMPARE_RESULT_GREATER: + result = COMPARE_RESULT_LESS; + break; + case COMPARE_RESULT_LESS: + result = COMPARE_RESULT_GREATER; + break; + default: + result = comparer.result; + break; + } + } +}; +template +CompareResult compare(const T1 &lhs, const T2 &rhs) { + Comparer comparer(rhs); + lhs.accept(comparer); + return comparer.result; +} +inline int variantCompare(const VariantData *a, const VariantData *b) { + return compare(VariantConstRef(a), VariantConstRef(b)); +} inline bool isdigit(char c) { return '0' <= c && c <= '9'; } inline bool issign(char c) { return '-' == c || c == '+'; } -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { template struct ParsedNumber { - ParsedNumber() : uintValue(0), floatValue(0), _type(VALUE_IS_NULL) {} - ParsedNumber(TUInt value, bool is_negative) - : uintValue(value), - floatValue(TFloat(value)), - _type(uint8_t(is_negative ? VALUE_IS_NEGATIVE_INTEGER - : VALUE_IS_POSITIVE_INTEGER)) {} - ParsedNumber(TFloat value) : floatValue(value), _type(VALUE_IS_FLOAT) {} + ParsedNumber() : _type(VALUE_IS_NULL) {} + void setInteger(TUInt value, bool is_negative) { + uintValue = value; + _type = uint8_t(is_negative ? VALUE_IS_NEGATIVE_INTEGER + : VALUE_IS_POSITIVE_INTEGER); + } + void setFloat(TFloat value) { + floatValue = value; + _type = VALUE_IS_FLOAT; + } template T as() const { switch (_type) { @@ -3779,19 +4447,20 @@ struct ParsedNumber { uint8_t type() const { return _type; } - TUInt uintValue; - TFloat floatValue; + union { + TUInt uintValue; + TFloat floatValue; + }; uint8_t _type; -}; +}; // namespace ARDUINOJSON_NAMESPACE template struct choose_largest : conditional<(sizeof(A) > sizeof(B)), A, B> {}; template -inline ParsedNumber parseNumber(const char *s) { +inline void parseNumber(const char* s, ParsedNumber& result) { typedef FloatTraits traits; typedef typename choose_largest::type mantissa_t; typedef typename traits::exponent_type exponent_t; - typedef ParsedNumber return_type; ARDUINOJSON_ASSERT(s != 0); bool is_negative = false; switch (*s) { @@ -3804,25 +4473,36 @@ inline ParsedNumber parseNumber(const char *s) { break; } #if ARDUINOJSON_ENABLE_NAN - if (*s == 'n' || *s == 'N') return traits::nan(); + if (*s == 'n' || *s == 'N') { + result.setFloat(traits::nan()); + return; + } #endif #if ARDUINOJSON_ENABLE_INFINITY - if (*s == 'i' || *s == 'I') - return is_negative ? -traits::inf() : traits::inf(); + if (*s == 'i' || *s == 'I') { + result.setFloat(is_negative ? -traits::inf() : traits::inf()); + return; + } #endif - if (!isdigit(*s) && *s != '.') return return_type(); + if (!isdigit(*s) && *s != '.') + return; mantissa_t mantissa = 0; exponent_t exponent_offset = 0; const mantissa_t maxUint = TUInt(-1); while (isdigit(*s)) { uint8_t digit = uint8_t(*s - '0'); - if (mantissa > maxUint / 10) break; + if (mantissa > maxUint / 10) + break; mantissa *= 10; - if (mantissa > maxUint - digit) break; + if (mantissa > maxUint - digit) + break; mantissa += digit; s++; } - if (*s == '\0') return return_type(TUInt(mantissa), is_negative); + if (*s == '\0') { + result.setInteger(TUInt(mantissa), is_negative); + return; + } while (mantissa > traits::mantissa_max) { mantissa /= 10; exponent_offset++; @@ -3855,36 +4535,38 @@ inline ParsedNumber parseNumber(const char *s) { exponent = exponent * 10 + (*s - '0'); if (exponent + exponent_offset > traits::exponent_max) { if (negative_exponent) - return is_negative ? -0.0f : 0.0f; + result.setFloat(is_negative ? -0.0f : 0.0f); else - return is_negative ? -traits::inf() : traits::inf(); + result.setFloat(is_negative ? -traits::inf() : traits::inf()); + return; } s++; } - if (negative_exponent) exponent = -exponent; + if (negative_exponent) + exponent = -exponent; } exponent += exponent_offset; - if (*s != '\0') return return_type(); - TFloat result = traits::make_float(static_cast(mantissa), exponent); - return is_negative ? -result : result; + if (*s != '\0') + return; + TFloat final_result = + traits::make_float(static_cast(mantissa), exponent); + result.setFloat(is_negative ? -final_result : final_result); } -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { template inline T parseFloat(const char* s) { typedef typename choose_largest::type TFloat; - return parseNumber(s).template as(); + ParsedNumber value; + parseNumber(s, value); + return value.template as(); } -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { template T parseInteger(const char *s) { typedef typename choose_largest::type>::type TUInt; - return parseNumber(s).template as(); + ParsedNumber value; + parseNumber(s, value); + return value.template as(); } -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { template inline T VariantData::asIntegral() const { switch (type()) { @@ -3910,11 +4592,10 @@ inline bool VariantData::asBoolean() const { return _content.asInteger != 0; case VALUE_IS_FLOAT: return _content.asFloat != 0; - case VALUE_IS_LINKED_STRING: - case VALUE_IS_OWNED_STRING: - return strcmp("true", _content.asString) == 0; - default: + case VALUE_IS_NULL: return false; + default: + return true; } } template @@ -3950,16 +4631,6 @@ typename enable_if::value, bool>::type VariantRef::set( return variantCopyFrom(_data, v._data, _pool); } template -inline typename enable_if::value, T>::type VariantRef::as() - const { - return ArrayRef(_pool, _data != 0 ? _data->asArray() : 0); -} -template -inline typename enable_if::value, T>::type -VariantRef::as() const { - return ObjectRef(_pool, variantAsObject(_data)); -} -template inline typename enable_if::value, ArrayRef>::type VariantRef::to() const { return ArrayRef(_pool, variantToArray(_data)); @@ -3975,15 +4646,18 @@ VariantRef::to() const { variantSetNull(_data); return *this; } -inline VariantConstRef VariantConstRef::operator[](size_t index) const { +inline VariantConstRef VariantConstRef::getElement(size_t index) const { return ArrayConstRef(_data != 0 ? _data->asArray() : 0)[index]; } inline VariantRef VariantRef::addElement() const { - return VariantRef(_pool, variantAdd(_data, _pool)); + return VariantRef(_pool, variantAddElement(_data, _pool)); } inline VariantRef VariantRef::getElement(size_t index) const { return VariantRef(_pool, _data != 0 ? _data->getElement(index) : 0); } +inline VariantRef VariantRef::getOrAddElement(size_t index) const { + return VariantRef(_pool, variantGetOrAddElement(_data, index, _pool)); +} template inline VariantRef VariantRef::getMember(TChar *key) const { return VariantRef(_pool, _data != 0 ? _data->getMember(adaptString(key)) : 0); @@ -3995,176 +4669,13 @@ VariantRef::getMember(const TString &key) const { } template inline VariantRef VariantRef::getOrAddMember(TChar *key) const { - return VariantRef(_pool, variantGetOrCreate(_data, key, _pool)); + return VariantRef(_pool, variantGetOrAddMember(_data, key, _pool)); } template inline VariantRef VariantRef::getOrAddMember(const TString &key) const { - return VariantRef(_pool, variantGetOrCreate(_data, key, _pool)); + return VariantRef(_pool, variantGetOrAddMember(_data, key, _pool)); } } // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { -class StringBuilder { - public: - explicit StringBuilder(MemoryPool* parent) : _parent(parent), _size(0) { - _slot = _parent->allocExpandableString(); - } - void append(const char* s) { - while (*s) append(*s++); - } - void append(const char* s, size_t n) { - while (n-- > 0) append(*s++); - } - void append(char c) { - if (!_slot.value) return; - if (_size >= _slot.size) { - _slot.value = 0; - return; - } - _slot.value[_size++] = c; - } - char* complete() { - append('\0'); - if (_slot.value) { - _parent->freezeString(_slot, _size); - } - return _slot.value; - } - private: - MemoryPool* _parent; - size_t _size; - StringSlot _slot; -}; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { -class StringCopier { - public: - typedef ARDUINOJSON_NAMESPACE::StringBuilder StringBuilder; - StringCopier(MemoryPool* pool) : _pool(pool) {} - StringBuilder startString() { - return StringBuilder(_pool); - } - private: - MemoryPool* _pool; -}; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { -class StringMover { - public: - class StringBuilder { - public: - StringBuilder(char** ptr) : _writePtr(ptr), _startPtr(*ptr) {} - void append(char c) { - *(*_writePtr)++ = char(c); - } - char* complete() const { - *(*_writePtr)++ = 0; - return _startPtr; - } - private: - char** _writePtr; - char* _startPtr; - }; - StringMover(char* ptr) : _ptr(ptr) {} - StringBuilder startString() { - return StringBuilder(&_ptr); - } - private: - char* _ptr; -}; -} // namespace ARDUINOJSON_NAMESPACE -namespace ARDUINOJSON_NAMESPACE { -template -struct StringStorage { - typedef StringCopier type; - static type create(MemoryPool& pool, TInput&) { - return type(&pool); - } -}; -template -struct StringStorage::value>::type> { - typedef StringMover type; - static type create(MemoryPool&, TChar* input) { - return type(reinterpret_cast(input)); - } -}; -template -typename StringStorage::type makeStringStorage(MemoryPool& pool, - TInput& input) { - return StringStorage::create(pool, input); -} -template -typename StringStorage::type makeStringStorage(MemoryPool& pool, - TChar* input) { - return StringStorage::create(pool, input); -} -} // namespace ARDUINOJSON_NAMESPACE -#if ARDUINOJSON_ENABLE_ARDUINO_STREAM -#include -namespace ARDUINOJSON_NAMESPACE { -struct ArduinoStreamReader { - Stream& _stream; - public: - explicit ArduinoStreamReader(Stream& stream) : _stream(stream) {} - int read() { - uint8_t c; - return _stream.readBytes(&c, 1) ? c : -1; - } -}; -inline ArduinoStreamReader makeReader(Stream& input) { - return ArduinoStreamReader(input); -} -} // namespace ARDUINOJSON_NAMESPACE -#endif -namespace ARDUINOJSON_NAMESPACE { -template -struct IsCharOrVoid { - static const bool value = - is_same::value || is_same::value || - is_same::value || is_same::value; -}; -template -struct IsCharOrVoid : IsCharOrVoid {}; -class UnsafeCharPointerReader { - const char* _ptr; - public: - explicit UnsafeCharPointerReader(const char* ptr) - : _ptr(ptr ? ptr : reinterpret_cast("")) {} - int read() { - return static_cast(*_ptr++); - } -}; -class SafeCharPointerReader { - const char* _ptr; - const char* _end; - public: - explicit SafeCharPointerReader(const char* ptr, size_t len) - : _ptr(ptr ? ptr : reinterpret_cast("")), _end(_ptr + len) {} - int read() { - if (_ptr < _end) - return static_cast(*_ptr++); - else - return -1; - } -}; -template -inline typename enable_if::value, - UnsafeCharPointerReader>::type -makeReader(TChar* input) { - return UnsafeCharPointerReader(reinterpret_cast(input)); -} -template -inline - typename enable_if::value, SafeCharPointerReader>::type - makeReader(TChar* input, size_t n) { - return SafeCharPointerReader(reinterpret_cast(input), n); -} -#if ARDUINOJSON_ENABLE_ARDUINO_STRING -inline SafeCharPointerReader makeReader(const ::String& input) { - return SafeCharPointerReader(input.c_str(), input.length()); -} -#endif -} // namespace ARDUINOJSON_NAMESPACE #if ARDUINOJSON_ENABLE_STD_STREAM #include #endif @@ -4253,41 +4764,79 @@ inline std::ostream& operator<<(std::ostream& s, DeserializationError::Code c) { return s; } #endif -} // namespace ARDUINOJSON_NAMESPACE -#if ARDUINOJSON_ENABLE_PROGMEM -namespace ARDUINOJSON_NAMESPACE { -class UnsafeFlashStringReader { - const char* _ptr; +class Filter { public: - explicit UnsafeFlashStringReader(const __FlashStringHelper* ptr) - : _ptr(reinterpret_cast(ptr)) {} - int read() { - return pgm_read_byte_near(_ptr++); + explicit Filter(VariantConstRef v) : _variant(v) {} + bool allow() const { + return _variant; } -}; -class SafeFlashStringReader { - const char* _ptr; - const char* _end; - public: - explicit SafeFlashStringReader(const __FlashStringHelper* ptr, size_t size) - : _ptr(reinterpret_cast(ptr)), _end(_ptr + size) {} - int read() { - if (_ptr < _end) - return pgm_read_byte_near(_ptr++); + bool allowArray() const { + return _variant == true || _variant.is(); + } + bool allowObject() const { + return _variant == true || _variant.is(); + } + bool allowValue() const { + return _variant == true; + } + template + Filter operator[](const TKey& key) const { + if (_variant == true) // "true" means "allow recursively" + return *this; else - return -1; + return Filter(_variant[key]); + } + private: + VariantConstRef _variant; +}; +struct AllowAllFilter { + bool allow() const { + return true; + } + bool allowArray() const { + return true; + } + bool allowObject() const { + return true; + } + bool allowValue() const { + return true; + } + template + AllowAllFilter operator[](const TKey&) const { + return AllowAllFilter(); } }; -inline UnsafeFlashStringReader makeReader(const __FlashStringHelper* input) { - return UnsafeFlashStringReader(input); -} -inline SafeFlashStringReader makeReader(const __FlashStringHelper* input, - size_t size) { - return SafeFlashStringReader(input, size); -} -} // namespace ARDUINOJSON_NAMESPACE -#endif -namespace ARDUINOJSON_NAMESPACE { +class NestingLimit { + public: + NestingLimit() : _value(ARDUINOJSON_DEFAULT_NESTING_LIMIT) {} + explicit NestingLimit(uint8_t n) : _value(n) {} + NestingLimit decrement() const { + ARDUINOJSON_ASSERT(_value > 0); + return NestingLimit(static_cast(_value - 1)); + } + bool reached() const { + return _value == 0; + } + private: + uint8_t _value; +}; +template +struct Reader { + public: + Reader(TSource& source) : _source(&source) {} + int read() { + return _source->read(); + } + size_t readBytes(char* buffer, size_t length) { + return _source->readBytes(buffer, length); + } + private: + TSource* _source; +}; +template +struct BoundedReader { +}; template class IteratorReader { TIterator _ptr, _end; @@ -4300,344 +4849,829 @@ class IteratorReader { else return -1; } + size_t readBytes(char* buffer, size_t length) { + size_t i = 0; + while (i < length && _ptr < _end) buffer[i++] = *_ptr++; + return i; + } +}; +template +struct void_ { + typedef void type; +}; +template +struct Reader::type> + : IteratorReader { + explicit Reader(const TSource& source) + : IteratorReader(source.begin(), + source.end()) {} +}; +template +struct IsCharOrVoid { + static const bool value = + is_same::value || is_same::value || + is_same::value || is_same::value; +}; +template +struct IsCharOrVoid : IsCharOrVoid {}; +template +struct Reader::value>::type> { + const char* _ptr; + public: + explicit Reader(const void* ptr) + : _ptr(ptr ? reinterpret_cast(ptr) : "") {} + int read() { + return static_cast(*_ptr++); + } + size_t readBytes(char* buffer, size_t length) { + for (size_t i = 0; i < length; i++) buffer[i] = *_ptr++; + return length; + } +}; +template +struct BoundedReader::value>::type> + : public IteratorReader { + public: + explicit BoundedReader(const void* ptr, size_t len) + : IteratorReader(reinterpret_cast(ptr), + reinterpret_cast(ptr) + len) {} +}; +template +struct Reader, void> : Reader { + explicit Reader(const ElementProxy& x) + : Reader(x.template as()) {} +}; +template +struct Reader, void> : Reader { + explicit Reader(const MemberProxy& x) + : Reader(x.template as()) {} +}; +template <> +struct Reader : Reader { + explicit Reader(VariantRef x) : Reader(x.as()) {} +}; +template <> +struct Reader : Reader { + explicit Reader(VariantConstRef x) + : Reader(x.as()) {} }; -template -inline IteratorReader makeReader( - const TInput& input) { - return IteratorReader(input.begin(), - input.end()); -} } // namespace ARDUINOJSON_NAMESPACE +#if ARDUINOJSON_ENABLE_ARDUINO_STREAM +#include namespace ARDUINOJSON_NAMESPACE { -struct NestingLimit { - NestingLimit() : value(ARDUINOJSON_DEFAULT_NESTING_LIMIT) {} - explicit NestingLimit(uint8_t n) : value(n) {} - uint8_t value; +template +struct Reader::value>::type> { + public: + explicit Reader(Stream& stream) : _stream(&stream) {} + int read() { + char c; + return _stream->readBytes(&c, 1) ? static_cast(c) : -1; + } + size_t readBytes(char* buffer, size_t length) { + return _stream->readBytes(buffer, length); + } + private: + Stream* _stream; }; } // namespace ARDUINOJSON_NAMESPACE +#endif +#if ARDUINOJSON_ENABLE_ARDUINO_STRING +namespace ARDUINOJSON_NAMESPACE { +template +struct Reader::value>::type> + : BoundedReader { + explicit Reader(const ::String& s) + : BoundedReader(s.c_str(), s.length()) {} +}; +} // namespace ARDUINOJSON_NAMESPACE +#endif +#if ARDUINOJSON_ENABLE_PROGMEM +namespace ARDUINOJSON_NAMESPACE { +template <> +struct Reader { + const char* _ptr; + public: + explicit Reader(const __FlashStringHelper* ptr) + : _ptr(reinterpret_cast(ptr)) {} + int read() { + return pgm_read_byte(_ptr++); + } + size_t readBytes(char* buffer, size_t length) { + memcpy_P(buffer, _ptr, length); + _ptr += length; + return length; + } +}; +template <> +struct BoundedReader { + const char* _ptr; + const char* _end; + public: + explicit BoundedReader(const __FlashStringHelper* ptr, size_t size) + : _ptr(reinterpret_cast(ptr)), _end(_ptr + size) {} + int read() { + if (_ptr < _end) + return pgm_read_byte(_ptr++); + else + return -1; + } + size_t readBytes(char* buffer, size_t length) { + size_t available = static_cast(_end - _ptr); + if (available < length) + length = available; + memcpy_P(buffer, _ptr, length); + _ptr += length; + return length; + } +}; +} // namespace ARDUINOJSON_NAMESPACE +#endif #if ARDUINOJSON_ENABLE_STD_STREAM #include namespace ARDUINOJSON_NAMESPACE { -class StdStreamReader { - std::istream& _stream; - char _current; +template +struct Reader::value>::type> { public: - explicit StdStreamReader(std::istream& stream) - : _stream(stream), _current(0) {} + explicit Reader(std::istream& stream) : _stream(&stream) {} int read() { - return _stream.get(); + return _stream->get(); + } + size_t readBytes(char* buffer, size_t length) { + _stream->read(buffer, static_cast(length)); + return static_cast(_stream->gcount()); } private: - StdStreamReader& operator=(const StdStreamReader&); // Visual Studio C4512 + std::istream* _stream; }; -inline StdStreamReader makeReader(std::istream& input) { - return StdStreamReader(input); -} } // namespace ARDUINOJSON_NAMESPACE #endif namespace ARDUINOJSON_NAMESPACE { +class StringCopier { + public: + void startString(MemoryPool* pool) { + pool->getFreeZone(&_ptr, &_capacity); + _size = 0; + } + const char* save(MemoryPool* pool) { + ARDUINOJSON_ASSERT(_ptr); + return pool->saveStringFromFreeZone(_size); + } + void append(const char* s) { + while (*s) append(*s++); + } + void append(const char* s, size_t n) { + while (n-- > 0) append(*s++); + } + void append(char c) { + if (!_ptr) + return; + if (_size >= _capacity) { + _ptr = 0; + return; + } + _ptr[_size++] = c; + } + bool isValid() { + return _ptr != 0; + } + const char* c_str() { + return _ptr; + } + typedef storage_policies::store_by_copy storage_policy; + private: + char* _ptr; + size_t _size; + size_t _capacity; +}; +class StringMover { + public: + StringMover(char* ptr) : _writePtr(ptr) {} + void startString(MemoryPool*) { + _startPtr = _writePtr; + } + const char* save(MemoryPool*) const { + return _startPtr; + } + void append(char c) { + *_writePtr++ = c; + } + bool isValid() const { + return true; + } + const char* c_str() const { + return _startPtr; + } + typedef storage_policies::store_by_address storage_policy; + private: + char* _writePtr; + char* _startPtr; +}; +template +struct StringStorage { + typedef StringCopier type; + static type create(TInput&) { + return type(); + } +}; +template +struct StringStorage::value>::type> { + typedef StringMover type; + static type create(TChar* input) { + return type(reinterpret_cast(input)); + } +}; +template +typename StringStorage::type makeStringStorage(TInput& input) { + return StringStorage::create(input); +} +template +typename StringStorage::type makeStringStorage(TChar* input) { + return StringStorage::create(input); +} template