From d70e421db359b72f58e52aae5ff200a5ce1e04e6 Mon Sep 17 00:00:00 2001 From: Antoni K Date: Wed, 16 Sep 2020 12:39:45 +0800 Subject: [PATCH 1/8] Fix typo Added an "l" to Compatible PC RGB... --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index dd860131b..2d62bc12f 100644 --- a/readme.md +++ b/readme.md @@ -72,7 +72,7 @@ WS2811 | 12v | 3-LED segments WS2815 | 12v | GS8208 | 12v | -## 🧊 Compatibe PC RGB Fans and ARGB accessories +## 🧊 Compatible PC RGB Fans and ARGB accessories Brand | Model | Comments |---|---|---| Corsair | HD120 Fan | Uses WS2812B, data-in only From e7709d8463f22cbacbbca817d048f60a16bbcbfa Mon Sep 17 00:00:00 2001 From: NeariX67 <31779313+NeariX67@users.noreply.github.com> Date: Wed, 16 Sep 2020 21:21:26 +0200 Subject: [PATCH 2/8] Upload TouchBrightnessControl Usermod for ESP32 (#1183) * Upload Usermod * Fix missing : public Usermod * Increased default threshold, added touchPin #define Co-authored-by: Aircoookie --- .../ESP32_TouchBrightnessControl/readme.md | 19 ++++ .../usermod_touchbrightness.h | 89 +++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 usermods/ESP32_TouchBrightnessControl/readme.md create mode 100644 usermods/ESP32_TouchBrightnessControl/usermod_touchbrightness.h diff --git a/usermods/ESP32_TouchBrightnessControl/readme.md b/usermods/ESP32_TouchBrightnessControl/readme.md new file mode 100644 index 000000000..f210b3320 --- /dev/null +++ b/usermods/ESP32_TouchBrightnessControl/readme.md @@ -0,0 +1,19 @@ +# ESP32 Touch Brightness Control + +Toggle On/Off with a long press (800ms) +Switch through 5 brightness levels (defined in usermod_touchbrightness.h, values 0-255) with a short (100ms) touch + +## Installation + +Copy 'usermod_touchbrightness.h' to the wled00 directory. +in 'usermod_list.cpp' add this: + +> #include "usermod_touchbrightness.h" +above "void registerUsermods()" + +and + +> usermods.add(new TouchBrightnessControl()); +inside the "registerUsermods()" function + + diff --git a/usermods/ESP32_TouchBrightnessControl/usermod_touchbrightness.h b/usermods/ESP32_TouchBrightnessControl/usermod_touchbrightness.h new file mode 100644 index 000000000..1b7795925 --- /dev/null +++ b/usermods/ESP32_TouchBrightnessControl/usermod_touchbrightness.h @@ -0,0 +1,89 @@ +// +// usermod_touchbrightness.h +// github.com/aircoookie/WLED +// +// Created by Justin Kühner on 14.09.2020. +// Copyright © 2020 NeariX. All rights reserved. +// https://github.com/NeariX67/ +// Discord: @NeariX#4799 + + +#pragma once + +#include "wled.h" + +#define threshold 40 //Increase value if touches falsely accur. Decrease value if actual touches are not recognized +#define touchPin T0 //T0 = D4 / GPIO4 + +//Define the 5 brightness levels +//Long press to turn off / on +#define brightness1 51 +#define brightness2 102 +#define brightness3 153 +#define brightness4 204 +#define brightness5 255 + + +#ifdef ESP32 + + +class TouchBrightnessControl : public Usermod { + private: + unsigned long lastTime = 0; //Interval + unsigned long lastTouch = 0; //Timestamp of last Touch + unsigned long lastRelease = 0; //Timestamp of last Touch release + boolean released = true; //current Touch state (touched/released) + uint16_t touchReading = 0; //sensor reading, maybe use uint8_t??? + uint16_t touchDuration = 0; //duration of last touch + public: + + void setup() { + lastTouch = millis(); + lastRelease = millis(); + lastTime = millis(); + } + + void loop() { + if (millis() - lastTime >= 50) { //Check every 50ms if a touch occurs + lastTime = millis(); + touchReading = touchRead(touchPin); //Read touch sensor on pin T0 (GPIO4 / D4) + + if(touchReading < threshold && released) { //Touch started + released = false; + lastTouch = millis(); + } + else if(touchReading >= threshold && !released) { //Touch released + released = true; + lastRelease = millis(); + touchDuration = lastRelease - lastTouch; //Calculate duration + } + + //Serial.println(touchDuration); + + if(touchDuration >= 800 && released) { //Toggle power if button press is longer than 800ms + touchDuration = 0; //Reset touch duration to avoid multiple actions on same touch + toggleOnOff(); + colorUpdated(2); //Refresh values + } + else if(touchDuration >= 100 && released) { //Switch to next brightness if touch is between 100 and 800ms + touchDuration = 0; //Reset touch duration to avoid multiple actions on same touch + if(bri < brightness1) { + bri = brightness1; + } else if(bri >= brightness1 && bri < brightness2) { + bri = brightness2; + } else if(bri >= brightness2 && bri < brightness3) { + bri = brightness3; + } else if(bri >= brightness3 && bri < brightness4) { + bri = brightness4; + } else if(bri >= brightness4 && bri < brightness5) { + bri = brightness5; + } else if(bri >= brightness5) { + bri = brightness1; + } + colorUpdated(2); //Refresh values + } + + } + } +}; +#endif From 10c22efbb5decd8bf1f2dd324f7070af46e4608a Mon Sep 17 00:00:00 2001 From: cschwinne Date: Thu, 17 Sep 2020 10:40:32 +0200 Subject: [PATCH 3/8] Fixed 170th pixel dark in E1.31 --- CHANGELOG.md | 5 +++++ wled00/e131.cpp | 2 +- wled00/wled.h | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5090a4b06..d342cb1ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ### Development versions after the 0.10.2 release +#### Build 2009170 + +- New WLED logo on Welcome screen (#1164) +- Fixed 170th pixel dark in E1.31 + #### Build 2009100 - Fixed sunrise mode not reinitializing diff --git a/wled00/e131.cpp b/wled00/e131.cpp index 7f67aadbe..c734d4392 100644 --- a/wled00/e131.cpp +++ b/wled00/e131.cpp @@ -137,7 +137,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, bool isArtnet){ uint16_t ledsInFirstUniverse = (MAX_CHANNELS_PER_UNIVERSE - DMXAddress) / 3; previousLeds = ledsInFirstUniverse + (previousUniverses - 1) * MAX_LEDS_PER_UNIVERSE; } - uint16_t ledsTotal = previousLeds + (dmxChannels - dmxOffset) / 3; + uint16_t ledsTotal = previousLeds + (dmxChannels - dmxOffset +1) / 3; for (uint16_t i = previousLeds; i < ledsTotal; i++) { setRealtimePixel(i, e131_data[dmxOffset++], e131_data[dmxOffset++], e131_data[dmxOffset++], 0); } diff --git a/wled00/wled.h b/wled00/wled.h index 11449e798..535de31fa 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2009100 +#define VERSION 2009170 // ESP8266-01 (blue) got too little storage space to work with all features of WLED. To use it, you must use ESP8266 Arduino Core v2.4.2 and the setting 512K(No SPIFFS). From edbe8131ab4da66836db4b2fafaa238429cb222b Mon Sep 17 00:00:00 2001 From: cschwinne Date: Sun, 20 Sep 2020 01:18:31 +0200 Subject: [PATCH 4/8] Reduce heap usage by 4kB --- CHANGELOG.md | 8 + wled00/FX.h | 6 +- wled00/const.h | 7 + wled00/e131.cpp | 2 +- wled00/fcn_declare.h | 6 +- wled00/hue.cpp | 23 +- wled00/json.cpp | 257 +- wled00/mqtt.cpp | 14 +- wled00/ntp.cpp | 184 +- wled00/set.cpp | 336 +- wled00/src/dependencies/json/ArduinoJson-v6.h | 4842 +++++++++++------ wled00/src/dependencies/json/AsyncJson-v6.h | 6 +- wled00/wled.cpp | 42 +- wled00/wled.h | 4 +- wled00/wled_eeprom.cpp | 2 +- wled00/wled_server.cpp | 100 +- wled00/ws.cpp | 6 +- wled00/xml.cpp | 357 +- 18 files changed, 3752 insertions(+), 2450 deletions(-) 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