From 5ee81210d98c2fd4a465e7d2f17abcd19f019794 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Mon, 18 Oct 2021 09:18:40 +0200 Subject: [PATCH 01/77] Update comments for string compare functions --- lwjson/src/include/lwjson/lwjson.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lwjson/src/include/lwjson/lwjson.h b/lwjson/src/include/lwjson/lwjson.h index 22cd303..72b31bb 100644 --- a/lwjson/src/include/lwjson/lwjson.h +++ b/lwjson/src/include/lwjson/lwjson.h @@ -195,7 +195,7 @@ lwjson_get_val_string(const lwjson_token_t* token, size_t* str_len) { /** * \brief Compare string token with user input string for a case-sensitive match * \param[in] token: Token with string type - * \param[out] str: String to compare + * \param[in] str: NULL-terminated string to compare * \return `1` if equal, `0` otherwise */ static inline uint8_t @@ -209,7 +209,8 @@ lwjson_string_compare(const lwjson_token_t* token, const char* str) { /** * \brief Compare string token with user input string for a case-sensitive match * \param[in] token: Token with string type - * \param[out] str: String to compare + * \param[in] str: NULL-terminated string to compare + * \param[in] len: Length of the string in bytes * \return `1` if equal, `0` otherwise */ static inline uint8_t From a880295b2d23f42ceda758e4daaaef488a1289ed Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Thu, 25 Nov 2021 08:02:10 +0100 Subject: [PATCH 02/77] Rework for win32 vscode --- .vscode/tasks.json | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 2e9fb60..a3c6ce0 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -4,16 +4,24 @@ { "type": "cppbuild", "label": "Build project", - "command": "C:\\MinGW\\bin\\gcc.exe", + "command": "gcc", // Must be in path "args": [ + //Global macro with "-D" prefix + "", + + // Source files "-g", "${workspaceFolder}\\lwjson\\src\\lwjson\\*.c", "${workspaceFolder}\\dev\\VisualStudio\\main.c", "${workspaceFolder}\\test\\*.c", + + // Include files "-I${workspaceFolder}\\dev\\VisualStudio\\", "-I${workspaceFolder}\\lwjson\\src\\include\\", + + // Final object "-o", - "${workspaceFolder}\\Debug\\output.exe" + "${workspaceFolder}\\project_vscode_compiled.exe" ], "options": { "cwd": "${workspaceFolder}" @@ -29,7 +37,7 @@ { "type": "shell", "label": "Run built code", - "command": "${workspaceFolder}\\Debug\\output.exe", + "command": "${workspaceFolder}\\project_vscode_compiled.exe", "problemMatcher": [], "dependsOn": [ "Build project" From 61fdadf9844a26d40e09a3f835df2d3608d9ceb0 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Thu, 25 Nov 2021 08:58:55 +0100 Subject: [PATCH 03/77] Update gitignore file --- .gitignore | 53 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/.gitignore b/.gitignore index a872aad..1f7d057 100644 --- a/.gitignore +++ b/.gitignore @@ -26,24 +26,26 @@ !docs/*.txt RTE/ -# IAR Settings -**/settings/*.crun -**/settings/*.dbgdt -**/settings/*.cspy -**/settings/*.cspy.* -**/settings/*.xcl -**/settings/*.dni -**/settings/*.wsdt -**/settings/*.wspos - -# IAR Debug Exe -**/Exe/*.sim - -# IAR Debug Obj -**/Obj/*.pbd -**/Obj/*.pbd.* -**/Obj/*.pbi -**/Obj/*.pbi.* +*debug + +# IAR Settings +**/settings/*.crun +**/settings/*.dbgdt +**/settings/*.cspy +**/settings/*.cspy.* +**/settings/*.xcl +**/settings/*.dni +**/settings/*.wsdt +**/settings/*.wspos + +# IAR Debug Exe +**/Exe/*.sim + +# IAR Debug Obj +**/Obj/*.pbd +**/Obj/*.pbd.* +**/Obj/*.pbi +**/Obj/*.pbi.* *.TMP /docs_src/x_Doxyfile.doxy @@ -69,6 +71,7 @@ RTE/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ +[Dd]ebug*/ x64/ x86/ bld/ @@ -274,7 +277,7 @@ ClientBin/ *.publishsettings orleans.codegen.cs -# Including strong name files can present a security risk +# Including strong name files can present a security risk # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk @@ -370,7 +373,7 @@ __pycache__/ # OpenCover UI analysis results OpenCover/ -# Azure Stream Analytics local run output +# Azure Stream Analytics local run output ASALocalRun/ # MSBuild Binary and Structured Log @@ -383,3 +386,13 @@ log_file.txt project.ioc mx.scratch *.tilen majerle + + +# Altium +Project outputs* +History/ +*.SchDocPreview +*.$$$Preview + +# VSCode projects +project_vscode_compiled.exe \ No newline at end of file From f3904506a8f0922447b4b661040a0f86e1028da3 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Fri, 26 Nov 2021 21:21:15 +0100 Subject: [PATCH 04/77] Update release workflow with link to changelog --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c4e4ac0..f43a9c1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,6 +22,6 @@ jobs: tag_name: ${{ github.ref }} release_name: Release ${{ github.ref }} body: | - See the CHANGELOG.md + See the [CHANGELOG](CHANGELOG.md) draft: false prerelease: false \ No newline at end of file From ab64ab6b1400d5671782901282b1e0aaa8f7ab56 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sun, 12 Dec 2021 21:37:05 +0100 Subject: [PATCH 05/77] CMake support for VSCode with ninja --- .gitignore | 2 ++ .vscode/c_cpp_properties.json | 11 ++++---- .vscode/launch.json | 38 +++++++++++-------------- .vscode/tasks.json | 53 ++++++++++++++++++++--------------- CMakeLists.txt | 29 +++++++++++++++++++ dev/VisualStudio/main.c | 8 ++---- 6 files changed, 86 insertions(+), 55 deletions(-) create mode 100644 CMakeLists.txt diff --git a/.gitignore b/.gitignore index 1f7d057..4674b8a 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ *.i *.txt !docs/*.txt +!CMakeLists.txt RTE/ *debug @@ -79,6 +80,7 @@ bld/ [Oo]bj/ [Ll]og/ _build/ +build/ # Visual Studio 2015/2017 cache/options directory .vs/ diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index f32f08f..331367c 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -3,19 +3,20 @@ { "name": "Win32", "includePath": [ - "${workspaceFolder}\\lwjson\\src\\include", - "${workspaceFolder}\\dev\\VisualStudio", - "${workspaceFolder}" + "${workspaceFolder}\\dev\\VisualStudio\\", + "${workspaceFolder}\\lwjson\\src\\include\\" ], "defines": [ + "WIN32", "_DEBUG", "UNICODE", "_UNICODE" ], - "compilerPath": "C:\\MinGW\\bin\\gcc.exe", + "compilerPath": "c:\\msys64\\mingw64\\bin\\gcc.exe", "cStandard": "gnu17", "cppStandard": "gnu++14", - "intelliSenseMode": "windows-gcc-x86" + "intelliSenseMode": "windows-gcc-x86", + "configurationProvider": "ms-vscode.cmake-tools" } ], "version": 4 diff --git a/.vscode/launch.json b/.vscode/launch.json index cd91592..e921cb0 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,26 +1,20 @@ { + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ - { - "name": "g++.exe - Launch program", - "type": "cppdbg", - "request": "launch", - "program": "${workspaceFolder}\\Debug\\output.exe", - "args": [], - "stopAtEntry": true, - "cwd": "${workspaceFolder}", - "environment": [], - "externalConsole": false, - "MIMode": "gdb", - "miDebuggerPath": "C:\\MinGW\\bin\\gdb.exe", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } - ], - "preLaunchTask": "g++.exe - Launch program" - } + { + "name": "(Windows) Launch", + "type": "cppvsdbg", + "request": "launch", + "program": "${workspaceFolder}\\build\\LwJSON.exe", + "miDebuggerPath": "c:\\msys64\\mingw64\\bin\\gdb.exe", + "args": [], + "stopAtEntry": false, + "cwd": "${fileDirname}", + "environment": [], + "console": "integratedTerminal" + } ] - } \ No newline at end of file +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index a3c6ce0..48c0214 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,27 +1,21 @@ { "version": "2.0.0", + + /* For this builds, you need + * + * - Ninja build system + * - MSYS2 compiler with ninja support + * - C/C++ extension for VSCode + * - CMake-Tools extension for VSCode + */ "tasks": [ { "type": "cppbuild", "label": "Build project", - "command": "gcc", // Must be in path + "command": "cmake", "args": [ - //Global macro with "-D" prefix - "", - - // Source files - "-g", - "${workspaceFolder}\\lwjson\\src\\lwjson\\*.c", - "${workspaceFolder}\\dev\\VisualStudio\\main.c", - "${workspaceFolder}\\test\\*.c", - - // Include files - "-I${workspaceFolder}\\dev\\VisualStudio\\", - "-I${workspaceFolder}\\lwjson\\src\\include\\", - - // Final object - "-o", - "${workspaceFolder}\\project_vscode_compiled.exe" + "--build", + "\"build\"" ], "options": { "cwd": "${workspaceFolder}" @@ -36,12 +30,25 @@ }, { "type": "shell", - "label": "Run built code", - "command": "${workspaceFolder}\\project_vscode_compiled.exe", + "label": "Clean project", + "command": "cmake", + "args": [ + "--build", + "\"build\"", + "--target", + "clean" + ], + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [] + }, + { + "type": "shell", + "label": "Run application", + "command": "${workspaceFolder}\\build\\LwJSON.exe", + "args": [], "problemMatcher": [], - "dependsOn": [ - "Build project" - ] - } + }, ] } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..7a29906 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.0.0) +project(LwJSON VERSION 0.1.0) + +include(CTest) +enable_testing() + +add_executable(${PROJECT_NAME} + lwjson/src/lwjson/lwjson.c + dev/VisualStudio/main.c + test/test.c + examples/example_minimal.c + examples/example_traverse.c + ) + +target_include_directories(${PROJECT_NAME} PRIVATE + dev/VisualStudio + lwjson/src/include + test + ) + +target_compile_definitions(${PROJECT_NAME} PRIVATE + WIN32 + _DEBUG + CONSOLE + ) + +set(CPACK_PROJECT_NAME ${PROJECT_NAME}) +set(CPACK_PROJECT_VERSION ${PROJECT_VERSION}) +include(CPack) diff --git a/dev/VisualStudio/main.c b/dev/VisualStudio/main.c index fc496b2..902958a 100644 --- a/dev/VisualStudio/main.c +++ b/dev/VisualStudio/main.c @@ -40,16 +40,14 @@ main() { printf("Could not open file..\r\n"); goto exit; } - file_size = GetFileSize(f, NULL); - if (file_size == INVALID_FILE_SIZE) { + if ((file_size = GetFileSize(f, NULL)) == INVALID_FILE_SIZE) { printf("Invalid file size..\r\n"); goto exit; } else if (file_size == 0) { printf("File is empty..\r\n"); goto exit; } - json_text = calloc((size_t)(file_size + 1), sizeof(*json_text)); - if (json_text == NULL) { + if ((json_text = calloc((size_t)(file_size + 1), sizeof(*json_text))) == NULL) { printf("Could not allocate memory..\r\n"); goto exit; } @@ -60,7 +58,7 @@ main() { /* Start parsing */ if (lwjson_parse(&lwjson, json_text) != lwjsonOK) { - printf("Could not parse input json\r\n"); + printf("Could not parse input JSON\r\n"); goto exit; } From 7157fa1c8867d661a1d89d9344018cab1ebd68da Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Mon, 13 Dec 2021 18:35:03 +0100 Subject: [PATCH 06/77] Update CMake and vscode --- .vscode/launch.json | 2 +- .vscode/settings.json | 5 ----- .vscode/tasks.json | 18 +++++++++++++++++- CMakeLists.txt | 2 +- 4 files changed, 19 insertions(+), 8 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/launch.json b/.vscode/launch.json index e921cb0..ee70253 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,7 +8,7 @@ "name": "(Windows) Launch", "type": "cppvsdbg", "request": "launch", - "program": "${workspaceFolder}\\build\\LwJSON.exe", + "program": "${workspaceFolder}\\build\\LwLibPROJECT.exe", "miDebuggerPath": "c:\\msys64\\mingw64\\bin\\gdb.exe", "args": [], "stopAtEntry": false, diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 83aff2f..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "files.associations": { - "lwjson.h": "c" - } -} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 48c0214..e981f20 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -28,6 +28,22 @@ "isDefault": true } }, + { + "type": "shell", + "label": "Rebuild project", + "command": "cmake", + "args": [ + "--build", + "\"build\"", + "--clean-first" + ], + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [ + "$gcc" + ], + }, { "type": "shell", "label": "Clean project", @@ -46,7 +62,7 @@ { "type": "shell", "label": "Run application", - "command": "${workspaceFolder}\\build\\LwJSON.exe", + "command": "${workspaceFolder}\\build\\LwLibPROJECT.exe", "args": [], "problemMatcher": [], }, diff --git a/CMakeLists.txt b/CMakeLists.txt index 7a29906..0b0a22e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.0.0) -project(LwJSON VERSION 0.1.0) +project(LwLibPROJECT VERSION 0.1.0) include(CTest) enable_testing() From 68d0902e7e286b163c00ab3dc1acd09a65f18855 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Tue, 14 Dec 2021 19:29:25 +0100 Subject: [PATCH 07/77] Update CMake and vscode tasks with cmake verbose output using ninja --- .vscode/tasks.json | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index e981f20..309d100 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -13,16 +13,11 @@ "type": "cppbuild", "label": "Build project", "command": "cmake", - "args": [ - "--build", - "\"build\"" - ], + "args": ["--build", "\"build\""], "options": { "cwd": "${workspaceFolder}" }, - "problemMatcher": [ - "$gcc" - ], + "problemMatcher": ["$gcc"], "group": { "kind": "build", "isDefault": true @@ -30,30 +25,19 @@ }, { "type": "shell", - "label": "Rebuild project", + "label": "Re-build project", "command": "cmake", - "args": [ - "--build", - "\"build\"", - "--clean-first" - ], + "args": ["--build", "\"build\"", "--clean-first", "-v"], "options": { "cwd": "${workspaceFolder}" }, - "problemMatcher": [ - "$gcc" - ], + "problemMatcher": ["$gcc"], }, { "type": "shell", "label": "Clean project", "command": "cmake", - "args": [ - "--build", - "\"build\"", - "--target", - "clean" - ], + "args": ["--build", "\"build\"", "--target", "clean"], "options": { "cwd": "${workspaceFolder}" }, From a3a4cb390e5c278a7105dee93e63b9402054d0a8 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Thu, 16 Dec 2021 21:38:07 +0100 Subject: [PATCH 08/77] Update library.json for Platform.IO --- library.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/library.json b/library.json index 7f47b8f..f245b62 100644 --- a/library.json +++ b/library.json @@ -23,9 +23,12 @@ "platforms": "*", "export": { "exclude": [ + ".github", + "dev" "docs", "**/.vs", - "**/Debug" + "**/Debug", + "build", ] } } \ No newline at end of file From 3eb9d78baa439367206c340d4661c152a76e9b3f Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Thu, 16 Dec 2021 21:53:44 +0100 Subject: [PATCH 09/77] Update launch.json to use cppdbg native use --- .vscode/launch.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index ee70253..8b95413 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,7 +6,7 @@ "configurations": [ { "name": "(Windows) Launch", - "type": "cppvsdbg", + "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}\\build\\LwLibPROJECT.exe", "miDebuggerPath": "c:\\msys64\\mingw64\\bin\\gdb.exe", From acf7ae4947ff3e25d0abdb198b157aba976da45f Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sun, 19 Dec 2021 16:03:28 +0100 Subject: [PATCH 10/77] Update export control for Platform.IO --- library.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library.json b/library.json index f245b62..7486e0e 100644 --- a/library.json +++ b/library.json @@ -24,11 +24,11 @@ "export": { "exclude": [ ".github", - "dev" + "dev", "docs", "**/.vs", "**/Debug", - "build", + "build" ] } } \ No newline at end of file From be0447892d159caffcb60b2029fa4f93ba88ed97 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Fri, 24 Dec 2021 18:57:03 +0100 Subject: [PATCH 11/77] Update CMake and split development and library --- CMakeLists.txt | 45 +++++++++++++++++++++++++++---------------- lwjson/CMakeLists.txt | 21 ++++++++++++++++++++ 2 files changed, 49 insertions(+), 17 deletions(-) create mode 100644 lwjson/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 0b0a22e..419eb69 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,29 +1,40 @@ cmake_minimum_required(VERSION 3.0.0) -project(LwLibPROJECT VERSION 0.1.0) -include(CTest) -enable_testing() +# Setup project +project(LwLibPROJECT) -add_executable(${PROJECT_NAME} - lwjson/src/lwjson/lwjson.c - dev/VisualStudio/main.c - test/test.c - examples/example_minimal.c - examples/example_traverse.c +# ------------------------------------------------- +# This CMakeLists.txt is used only if it is a top-level file. +# Purpose of it is to be able to compile project in standalone way only +# +# When library sources are to be included in another project +# user shall use /lwjson/CMakeLists.txt instead +if (NOT PROJECT_IS_TOP_LEVEL) + message(FATAL_ERROR "This CMakeLists.txt can only be used as top-level. Use /lwjson/CMakeLists.txt for library include purpose") +endif() + +# Set as executable +add_executable(${PROJECT_NAME}) + +# Add key executable block +target_sources(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/dev/VisualStudio/main.c + ${CMAKE_CURRENT_LIST_DIR}/test/test.c ) -target_include_directories(${PROJECT_NAME} PRIVATE - dev/VisualStudio - lwjson/src/include - test +# Add key include paths +target_include_directories(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/dev/VisualStudio ) -target_compile_definitions(${PROJECT_NAME} PRIVATE +# Compilation definition information +target_compile_definitions(${PROJECT_NAME} PUBLIC WIN32 _DEBUG CONSOLE + LWJSON_DEV ) -set(CPACK_PROJECT_NAME ${PROJECT_NAME}) -set(CPACK_PROJECT_VERSION ${PROJECT_VERSION}) -include(CPack) +# Add subdir with lwjson and link to project +add_subdirectory("lwjson" lwjson) +target_link_libraries(${PROJECT_NAME} lwjson) \ No newline at end of file diff --git a/lwjson/CMakeLists.txt b/lwjson/CMakeLists.txt new file mode 100644 index 0000000..9900398 --- /dev/null +++ b/lwjson/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.13) + +# Debug message +message("LWJSON: Entering lib source CMakeLists.txt") + +# Set library name +set(LWJSON_LIB_NAME "lwjson") + +# Register library to the system +add_library(${LWJSON_LIB_NAME} INTERFACE) + +# Setup generic source files +target_sources(${LWJSON_LIB_NAME} INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/src/lwjson/lwjson.c + ${CMAKE_CURRENT_LIST_DIR}/src/lwjson/lwjson.c + ) + +# Setup include directories +target_include_directories(${LWJSON_LIB_NAME} INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/src/include + ) From 40a5ca97a9b1cfd3bfe711a60dda098b8978bd2e Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Fri, 24 Dec 2021 19:44:26 +0100 Subject: [PATCH 12/77] Move dvp one folder up and update CMakeLists.txt --- CMakeLists.txt | 4 ++-- dev/{VisualStudio => }/lwjson_dev.sln | 0 dev/{VisualStudio => }/lwjson_dev.vcxproj | 12 ++++++------ dev/{VisualStudio => }/lwjson_dev.vcxproj.filters | 10 +++++----- dev/{VisualStudio => }/lwjson_opts.h | 0 dev/{VisualStudio => }/main.c | 0 6 files changed, 13 insertions(+), 13 deletions(-) rename dev/{VisualStudio => }/lwjson_dev.sln (100%) rename dev/{VisualStudio => }/lwjson_dev.vcxproj (94%) rename dev/{VisualStudio => }/lwjson_dev.vcxproj.filters (83%) rename dev/{VisualStudio => }/lwjson_opts.h (100%) rename dev/{VisualStudio => }/main.c (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 419eb69..f3a8683 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,13 +18,13 @@ add_executable(${PROJECT_NAME}) # Add key executable block target_sources(${PROJECT_NAME} PUBLIC - ${CMAKE_CURRENT_LIST_DIR}/dev/VisualStudio/main.c + ${CMAKE_CURRENT_LIST_DIR}/dev/main.c ${CMAKE_CURRENT_LIST_DIR}/test/test.c ) # Add key include paths target_include_directories(${PROJECT_NAME} PUBLIC - ${CMAKE_CURRENT_LIST_DIR}/dev/VisualStudio + ${CMAKE_CURRENT_LIST_DIR}/dev ) # Compilation definition information diff --git a/dev/VisualStudio/lwjson_dev.sln b/dev/lwjson_dev.sln similarity index 100% rename from dev/VisualStudio/lwjson_dev.sln rename to dev/lwjson_dev.sln diff --git a/dev/VisualStudio/lwjson_dev.vcxproj b/dev/lwjson_dev.vcxproj similarity index 94% rename from dev/VisualStudio/lwjson_dev.vcxproj rename to dev/lwjson_dev.vcxproj index 6c6711d..adbbd6e 100644 --- a/dev/VisualStudio/lwjson_dev.vcxproj +++ b/dev/lwjson_dev.vcxproj @@ -72,7 +72,7 @@ true - ..\..\lwjson\src\include\;.;$(IncludePath) + ..\lwjson\src\include\;.;$(IncludePath) true @@ -143,11 +143,11 @@ - - - - - + + + + + diff --git a/dev/VisualStudio/lwjson_dev.vcxproj.filters b/dev/lwjson_dev.vcxproj.filters similarity index 83% rename from dev/VisualStudio/lwjson_dev.vcxproj.filters rename to dev/lwjson_dev.vcxproj.filters index 10e9bd6..f5326a2 100644 --- a/dev/VisualStudio/lwjson_dev.vcxproj.filters +++ b/dev/lwjson_dev.vcxproj.filters @@ -24,19 +24,19 @@ Source Files - + Source Files - + Source Files - + Source Files - + Source Files\Examples - + Source Files\Examples diff --git a/dev/VisualStudio/lwjson_opts.h b/dev/lwjson_opts.h similarity index 100% rename from dev/VisualStudio/lwjson_opts.h rename to dev/lwjson_opts.h diff --git a/dev/VisualStudio/main.c b/dev/main.c similarity index 100% rename from dev/VisualStudio/main.c rename to dev/main.c From 2cc333fc645d75e9bb188db74947294c8d05ca23 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Fri, 24 Dec 2021 19:58:29 +0100 Subject: [PATCH 13/77] Update Changelog file for CMakeLists --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4041a17..bedcb9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Develop +- Split CMakeLists.txt files between library and executable + ## 1.5.0 - Add string compare feature From 38aabc0dff89ddc08f4519a687214f50ba516de0 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Fri, 24 Dec 2021 20:11:18 +0100 Subject: [PATCH 14/77] Update getting started --- docs/get-started/index.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/get-started/index.rst b/docs/get-started/index.rst index f38dc9e..afe5836 100644 --- a/docs/get-started/index.rst +++ b/docs/get-started/index.rst @@ -45,10 +45,10 @@ This is used when you do not have yet local copy on your machine. Update cloned to latest version """"""""""""""""""""""""""""""" -* Open console and navigate to path in the system where your resources repository is. Use command ``cd your_path`` -* Run ``git pull origin master --recurse-submodules`` command to pull latest changes and to fetch latest changes from submodules on ``master`` branch -* Run ``git pull origin develop --recurse-submodules`` command to pull latest changes and to fetch latest changes from submodules on ``develop`` branch -* Run ``git submodule foreach git pull origin master`` to update & merge all submodules +* Open console and navigate to path in the system where your repository is located. Use command ``cd your_path`` +* Run ``git pull origin master`` command to get latest changes on ``master`` branch +* Run ``git pull origin develop`` command to get latest changes on ``develop`` branch +* Run ``git submodule update --init --remote`` to update submodules to latest version .. note:: This is preferred option to use when you want to evaluate library and run prepared examples. From 3b71b2f0e66a5132457cf04a41bb48ac04b92ea7 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Mon, 27 Dec 2021 17:50:20 +0100 Subject: [PATCH 15/77] Update CMakeLists.txt for each library and split core and optional modules --- .vscode/c_cpp_properties.json | 2 +- CMakeLists.txt | 3 ++- lwjson/CMakeLists.txt | 33 ++++++++++++++++++++++----------- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 331367c..67291f2 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -3,7 +3,7 @@ { "name": "Win32", "includePath": [ - "${workspaceFolder}\\dev\\VisualStudio\\", + "${workspaceFolder}\\dev\\", "${workspaceFolder}\\lwjson\\src\\include\\" ], "defines": [ diff --git a/CMakeLists.txt b/CMakeLists.txt index f3a8683..29541a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,4 +37,5 @@ target_compile_definitions(${PROJECT_NAME} PUBLIC # Add subdir with lwjson and link to project add_subdirectory("lwjson" lwjson) -target_link_libraries(${PROJECT_NAME} lwjson) \ No newline at end of file +target_link_libraries(${PROJECT_NAME} lwjson) +target_link_libraries(${PROJECT_NAME} lwjson_debug) \ No newline at end of file diff --git a/lwjson/CMakeLists.txt b/lwjson/CMakeLists.txt index 9900398..5ba1d6a 100644 --- a/lwjson/CMakeLists.txt +++ b/lwjson/CMakeLists.txt @@ -1,21 +1,32 @@ cmake_minimum_required(VERSION 3.13) -# Debug message -message("LWJSON: Entering lib source CMakeLists.txt") - -# Set library name -set(LWJSON_LIB_NAME "lwjson") - -# Register library to the system -add_library(${LWJSON_LIB_NAME} INTERFACE) +#Debug message +message("Entering ${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt") # Setup generic source files -target_sources(${LWJSON_LIB_NAME} INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/src/lwjson/lwjson.c +set(lwjson_core_SRCS ${CMAKE_CURRENT_LIST_DIR}/src/lwjson/lwjson.c ) +# Debug sources +set(lwjson_debug_SRCS + ${CMAKE_CURRENT_LIST_DIR}/src/lwjson/lwjson_debug.c + ) + # Setup include directories -target_include_directories(${LWJSON_LIB_NAME} INTERFACE +set(lwjson_include_DIRS ${CMAKE_CURRENT_LIST_DIR}/src/include ) + +# Register core library to the system +add_library(lwjson INTERFACE) +target_sources(lwjson PUBLIC ${lwjson_core_SRCS}) +target_include_directories(lwjson INTERFACE ${lwjson_include_DIRS}) + +# Register lwjson debug module +add_library(lwjson_debug INTERFACE) +target_sources(lwjson_debug PUBLIC ${lwjson_debug_SRCS}) +target_include_directories(lwjson_debug INTERFACE ${lwjson_include_DIRS}) + +#Debug message +message("Exiting ${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt") \ No newline at end of file From 244e6892a430b8a47b29cf72bd9abce46bcef182 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Thu, 13 Jan 2022 20:56:29 +0100 Subject: [PATCH 16/77] Update library.json --- library.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library.json b/library.json index 7486e0e..5bf7fdf 100644 --- a/library.json +++ b/library.json @@ -28,7 +28,8 @@ "docs", "**/.vs", "**/Debug", - "build" + "build", + "**/build" ] } } \ No newline at end of file From 8d7de542a7ed6c916f195c7f2b4c043b079c64fc Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Fri, 14 Jan 2022 23:07:44 +0100 Subject: [PATCH 17/77] Update license year to 2022 --- CHANGELOG.md | 1 + LICENSE | 2 +- dev/lwjson_opts.h | 2 +- lwjson/src/include/lwjson/lwjson.h | 2 +- lwjson/src/include/lwjson/lwjson_opt.h | 2 +- lwjson/src/include/lwjson/lwjson_opts_template.h | 2 +- lwjson/src/lwjson/lwjson.c | 2 +- lwjson/src/lwjson/lwjson_debug.c | 2 +- 8 files changed, 8 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bedcb9a..9beccd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Develop - Split CMakeLists.txt files between library and executable +- Change license year to 2022 ## 1.5.0 diff --git a/LICENSE b/LICENSE index aa60317..5625f63 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 Tilen MAJERLE +Copyright (c) 2022 Tilen MAJERLE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/dev/lwjson_opts.h b/dev/lwjson_opts.h index 2a27683..a23c9fa 100644 --- a/dev/lwjson_opts.h +++ b/dev/lwjson_opts.h @@ -4,7 +4,7 @@ */ /* - * Copyright (c) 2020 Tilen MAJERLE + * Copyright (c) 2022 Tilen MAJERLE * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation diff --git a/lwjson/src/include/lwjson/lwjson.h b/lwjson/src/include/lwjson/lwjson.h index 72b31bb..005439f 100644 --- a/lwjson/src/include/lwjson/lwjson.h +++ b/lwjson/src/include/lwjson/lwjson.h @@ -4,7 +4,7 @@ */ /* - * Copyright (c) 2020 Tilen MAJERLE + * Copyright (c) 2022 Tilen MAJERLE * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation diff --git a/lwjson/src/include/lwjson/lwjson_opt.h b/lwjson/src/include/lwjson/lwjson_opt.h index f36288f..b5a04b8 100644 --- a/lwjson/src/include/lwjson/lwjson_opt.h +++ b/lwjson/src/include/lwjson/lwjson_opt.h @@ -4,7 +4,7 @@ */ /* - * Copyright (c) 2020 Tilen MAJERLE + * Copyright (c) 2022 Tilen MAJERLE * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation diff --git a/lwjson/src/include/lwjson/lwjson_opts_template.h b/lwjson/src/include/lwjson/lwjson_opts_template.h index e4c0006..6db700c 100644 --- a/lwjson/src/include/lwjson/lwjson_opts_template.h +++ b/lwjson/src/include/lwjson/lwjson_opts_template.h @@ -4,7 +4,7 @@ */ /* - * Copyright (c) 2020 Tilen MAJERLE + * Copyright (c) 2022 Tilen MAJERLE * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation diff --git a/lwjson/src/lwjson/lwjson.c b/lwjson/src/lwjson/lwjson.c index a1659c0..4360db3 100644 --- a/lwjson/src/lwjson/lwjson.c +++ b/lwjson/src/lwjson/lwjson.c @@ -4,7 +4,7 @@ */ /* - * Copyright (c) 2020 Tilen MAJERLE + * Copyright (c) 2022 Tilen MAJERLE * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation diff --git a/lwjson/src/lwjson/lwjson_debug.c b/lwjson/src/lwjson/lwjson_debug.c index 7b9ddf5..7eb572b 100644 --- a/lwjson/src/lwjson/lwjson_debug.c +++ b/lwjson/src/lwjson/lwjson_debug.c @@ -4,7 +4,7 @@ */ /* - * Copyright (c) 2020 Tilen MAJERLE + * Copyright (c) 2022 Tilen MAJERLE * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation From 54f4fd7d342f22ce8fa3da8936d44d5fecea69ff Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sat, 15 Jan 2022 00:41:45 +0100 Subject: [PATCH 18/77] Update .vscode files --- .vscode/extensions.json | 7 +++++++ .vscode/tasks.json | 12 ++---------- 2 files changed, 9 insertions(+), 10 deletions(-) create mode 100644 .vscode/extensions.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..6a07920 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + "ms-vscode.cpptools", + "ms-vscode.cmake-tools", + "twxs.cmake", + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 309d100..a4527df 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,19 +1,11 @@ { "version": "2.0.0", - - /* For this builds, you need - * - * - Ninja build system - * - MSYS2 compiler with ninja support - * - C/C++ extension for VSCode - * - CMake-Tools extension for VSCode - */ "tasks": [ { "type": "cppbuild", "label": "Build project", "command": "cmake", - "args": ["--build", "\"build\""], + "args": ["--build", "\"build\"", "-j", "8"], "options": { "cwd": "${workspaceFolder}" }, @@ -27,7 +19,7 @@ "type": "shell", "label": "Re-build project", "command": "cmake", - "args": ["--build", "\"build\"", "--clean-first", "-v"], + "args": ["--build", "\"build\"", "--clean-first", "-v", "-j", "8"], "options": { "cwd": "${workspaceFolder}" }, From f4167a0d982e8f45f12ac2c51228a4733c7251f0 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Tue, 18 Jan 2022 22:35:33 +0100 Subject: [PATCH 19/77] Update CMakeLists.txt to v3.22 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 29541a4..7593ea7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.0.0) +cmake_minimum_required(VERSION 3.22) # Setup project project(LwLibPROJECT) From f9f218580b2fecf7d500559bc98cd2fb243ba293 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Tue, 18 Jan 2022 22:41:32 +0100 Subject: [PATCH 20/77] Update CMakeLists.txt to v3.22 --- lwjson/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lwjson/CMakeLists.txt b/lwjson/CMakeLists.txt index 5ba1d6a..b5858c3 100644 --- a/lwjson/CMakeLists.txt +++ b/lwjson/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.13) +cmake_minimum_required(VERSION 3.22) #Debug message message("Entering ${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt") From af4e0893c763bf99e82398b8559f160838a30ada Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sat, 22 Jan 2022 14:52:18 +0100 Subject: [PATCH 21/77] Fix GCC warning and update code style for consistency --- CHANGELOG.md | 1 + lwjson/src/lwjson/lwjson.c | 10 ++++------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9beccd7..aab47dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Split CMakeLists.txt files between library and executable - Change license year to 2022 +- Fix GCC warning for incompatible comparison types ## 1.5.0 diff --git a/lwjson/src/lwjson/lwjson.c b/lwjson/src/lwjson/lwjson.c index 4360db3..be1973c 100644 --- a/lwjson/src/lwjson/lwjson.c +++ b/lwjson/src/lwjson/lwjson.c @@ -508,21 +508,19 @@ lwjson_parse_ex(lwjson_t* lw, const void* json_data, size_t json_len) { if (*pobj.p == (to->type == LWJSON_TYPE_OBJECT ? '}' : ']')) { lwjson_token_t* parent = to->next; to->next = NULL; - to = parent; ++pobj.p; - /* End of string, check if properly terminated */ - if (to == NULL) { + /* End of string if to == NULL (no parent), check if properly terminated */ + if ((to = parent) == NULL) { prv_skip_blank(&pobj); - res = (pobj.p == NULL || *pobj.p == '\0' || (pobj.p - pobj.start) == pobj.len) ? lwjsonOK : lwjsonERR; + res = (pobj.p == NULL || *pobj.p == '\0' || (size_t)(pobj.p - pobj.start) == pobj.len) ? lwjsonOK : lwjsonERR; goto ret; } continue; } /* Allocate new token */ - t = prv_alloc_token(lw); - if (t == NULL) { + if ((t = prv_alloc_token(lw)) == NULL) { res = lwjsonERRMEM; goto ret; } From 7b5e3cbacde77cda0ffae4bd2c5a645c894e580e Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sun, 30 Jan 2022 10:06:27 +0100 Subject: [PATCH 22/77] Update code with astyle --- CHANGELOG.md | 1 + lwjson/src/lwjson/lwjson.c | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aab47dd..dd3ad09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Split CMakeLists.txt files between library and executable - Change license year to 2022 - Fix GCC warning for incompatible comparison types +- Update code style with astyle ## 1.5.0 diff --git a/lwjson/src/lwjson/lwjson.c b/lwjson/src/lwjson/lwjson.c index be1973c..97b8b04 100644 --- a/lwjson/src/lwjson/lwjson.c +++ b/lwjson/src/lwjson/lwjson.c @@ -68,7 +68,7 @@ prv_skip_blank(lwjson_int_str_t* pobj) { if (*pobj->p == ' ' || *pobj->p == '\t' || *pobj->p == '\r' || *pobj->p == '\n' || *pobj->p == '\f') { ++pobj->p; #if LWJSON_CFG_COMMENTS - /* Check for comments and remove them */ + /* Check for comments and remove them */ } else if (*pobj->p == '/') { ++pobj->p; if (pobj->p != NULL && *pobj->p == '*') { @@ -138,8 +138,8 @@ prv_parse_string(lwjson_int_str_t* pobj, const char** pout, size_t* poutlen) { ++pobj->p; for (size_t i = 0; i < 4; ++i, ++len) { if (!((*pobj->p >= '0' && *pobj->p <= '9') - || (*pobj->p >= 'a' && *pobj->p <= 'f') - || (*pobj->p >= 'A' && *pobj->p <= 'F'))) { + || (*pobj->p >= 'a' && *pobj->p <= 'f') + || (*pobj->p >= 'A' && *pobj->p <= 'F'))) { return lwjsonERRJSON; } if (i < 3) { @@ -352,7 +352,7 @@ prv_find(const lwjson_token_t* parent, const char* path) { /* Check if index requested */ if (segment_len > 1) { - const lwjson_token_t *t; + const lwjson_token_t* t; size_t index = 0; /* Parse number */ @@ -418,10 +418,10 @@ prv_check_valid_char_after_open_bracket(lwjson_int_str_t* pobj, lwjson_token_t* } if (*pobj->p == '\0' || (t->type == LWJSON_TYPE_OBJECT - && (*pobj->p != '"' && *pobj->p != '}')) + && (*pobj->p != '"' && *pobj->p != '}')) || (t->type == LWJSON_TYPE_ARRAY - && (*pobj->p != '"' && *pobj->p != ']' && *pobj->p != '[' && *pobj->p != '{' && *pobj->p != '-' - && (*pobj->p < '0' || *pobj->p > '9') && *pobj->p != 't' && *pobj->p != 'n' && *pobj->p != 'f'))) { + && (*pobj->p != '"' && *pobj->p != ']' && *pobj->p != '[' && *pobj->p != '{' && *pobj->p != '-' + && (*pobj->p < '0' || *pobj->p > '9') && *pobj->p != 't' && *pobj->p != 'n' && *pobj->p != 'f'))) { res = lwjsonERRJSON; } return res; From fddbd64f289e1e84ca8c7630406d83fc6c24026c Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sun, 6 Feb 2022 22:35:36 +0100 Subject: [PATCH 23/77] Update link to font-awesome and license to 2022 --- docs/conf.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 6216731..b3b6178 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -23,7 +23,7 @@ # -- Project information ----------------------------------------------------- project = 'LwJSON' -copyright = '2020, Tilen MAJERLE' +copyright = '2022, Tilen MAJERLE' author = 'Tilen MAJERLE' # Try to get branch at which this is running @@ -115,9 +115,10 @@ html_css_files = [ 'css/common.css', 'css/custom.css', + 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css', ] html_js_files = [ - 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css' + '' ] master_doc = 'index' From 57238eed61b557df87d12a7645d1e3ef508f4e4c Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Wed, 16 Feb 2022 20:19:28 +0100 Subject: [PATCH 24/77] Update master branch to main --- docs/get-started/index.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/get-started/index.rst b/docs/get-started/index.rst index afe5836..11f14f6 100644 --- a/docs/get-started/index.rst +++ b/docs/get-started/index.rst @@ -13,10 +13,10 @@ Download library Library is primarly hosted on `Github `_. -You can get it with: +You can get it by: * Downloading latest release from `releases area `_ on Github -* Cloning ``master`` branch for latest stable version +* Cloning ``main`` branch for latest stable version * Cloning ``develop`` branch for latest development Download from releases @@ -34,11 +34,11 @@ This is used when you do not have yet local copy on your machine. * Make sure ``git`` is installed. * Open console and navigate to path in the system to clone repository to. Use command ``cd your_path`` -* Clone repository with one of available ``3`` options +* Clone repository with one of available options below * Run ``git clone --recurse-submodules https://github.com/MaJerle/lwjson`` command to clone entire repository, including submodules * Run ``git clone --recurse-submodules --branch develop https://github.com/MaJerle/lwjson`` to clone `development` branch, including submodules - * Run ``git clone --recurse-submodules --branch master https://github.com/MaJerle/lwjson`` to clone `latest stable` branch, including submodules + * Run ``git clone --recurse-submodules --branch main https://github.com/MaJerle/lwjson`` to clone `latest stable` branch, including submodules * Navigate to ``examples`` directory and run favourite example @@ -46,7 +46,7 @@ Update cloned to latest version """"""""""""""""""""""""""""""" * Open console and navigate to path in the system where your repository is located. Use command ``cd your_path`` -* Run ``git pull origin master`` command to get latest changes on ``master`` branch +* Run ``git pull origin main`` command to get latest changes on ``main`` branch * Run ``git pull origin develop`` command to get latest changes on ``develop`` branch * Run ``git submodule update --init --remote`` to update submodules to latest version From f5100b6e14d0bf4ebfe155011b59e414346a65c5 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sat, 19 Feb 2022 00:14:53 +0100 Subject: [PATCH 25/77] Update requirements file for latest rtd theme --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index eb5e0fd..834b1bb 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -2,7 +2,7 @@ breathe>=4.9.1 colorama docutils==0.16 sphinx>=3.5.1 -sphinx_rtd_theme +sphinx_rtd_theme>=1.0.0 sphinx-tabs sphinxcontrib-svg2pdfconverter sphinx-sitemap From 2a6526fbf4503f94f4f503aea23891497c45a0fe Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sat, 19 Feb 2022 01:47:05 +0100 Subject: [PATCH 26/77] Update requirements file for latest rtd theme --- docs/conf.py | 10 +- docs/index.rst | 19 ++ docs/static/dark-light/checked.svg | 1 + docs/static/dark-light/common-dark-light.css | 143 ++++++++ docs/static/dark-light/dark-mode-toggle.mjs | 329 +++++++++++++++++++ docs/static/dark-light/dark.css | 36 ++ docs/static/dark-light/light.css | 24 ++ docs/static/dark-light/moon.png | Bin 0 -> 3511 bytes docs/static/dark-light/moon.svg | 7 + docs/static/dark-light/sun.png | Bin 0 -> 1897 bytes docs/static/dark-light/sun.svg | 5 + docs/static/dark-light/unchecked.svg | 1 + 12 files changed, 569 insertions(+), 6 deletions(-) create mode 100644 docs/static/dark-light/checked.svg create mode 100644 docs/static/dark-light/common-dark-light.css create mode 100644 docs/static/dark-light/dark-mode-toggle.mjs create mode 100644 docs/static/dark-light/dark.css create mode 100644 docs/static/dark-light/light.css create mode 100644 docs/static/dark-light/moon.png create mode 100644 docs/static/dark-light/moon.svg create mode 100644 docs/static/dark-light/sun.png create mode 100644 docs/static/dark-light/sun.svg create mode 100644 docs/static/dark-light/unchecked.svg diff --git a/docs/conf.py b/docs/conf.py index b3b6178..048d9d2 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -121,15 +121,13 @@ '' ] +# Master index file master_doc = 'index' -# -# Breathe configuration -# -# -# +# --- Breathe configuration ----------------------------------------------------- breathe_projects = { "lwjson": "_build/xml/" } breathe_default_project = "lwjson" -breathe_default_members = ('members', 'undoc-members') \ No newline at end of file +breathe_default_members = ('members', 'undoc-members') +breathe_show_enumvalue_initializer = True \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index 2d9cc78..27b8f08 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -65,8 +65,27 @@ Table of contents .. toctree:: :maxdepth: 2 + :caption: Contents self get-started/index user-manual/index api-reference/index + +.. toctree:: + :maxdepth: 2 + :caption: Other projects + :hidden: + + LwDTC - DateTimeCron + LwESP - ESP-AT library + LwGPS - GPS NMEA parser + LwGSM - GSM-AT library + LwJSON - JSON parser + LwMEM - Memory manager + LwOW - OneWire with UART + LwPKT - Packet protocol + LwPRINTF - Printf + LwRB - Ring buffer + LwSHELL - Shell + LwUTIL - Utility functions diff --git a/docs/static/dark-light/checked.svg b/docs/static/dark-light/checked.svg new file mode 100644 index 0000000..a78af82 --- /dev/null +++ b/docs/static/dark-light/checked.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/static/dark-light/common-dark-light.css b/docs/static/dark-light/common-dark-light.css new file mode 100644 index 0000000..9a2dc1d --- /dev/null +++ b/docs/static/dark-light/common-dark-light.css @@ -0,0 +1,143 @@ +/** + * @license + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:root { + --heading-color: red; + --duration: 0.5s; + --timing: ease; +} + +*, +::before, +::after { + box-sizing: border-box; +} + +body { + margin: 0; + transition: + color var(--duration) var(--timing), + background-color var(--duration) var(--timing); + font-family: sans-serif; + font-size: 12pt; + background-color: var(--background-color); + color: var(--text-color); + display: flex; + justify-content: center; +} + +main { + margin: 1rem; + max-width: 30rem; + position: relative; +} + +h1 { + color: var(--heading-color); + text-shadow: 0.1rem 0.1rem 0.1rem var(--shadow-color); + transition: text-shadow var(--duration) var(--timing); +} + +img { + max-width: 100%; + height: auto; + transition: filter var(--duration) var(--timing); +} + +p { + line-height: 1.5; + word-wrap: break-word; + overflow-wrap: break-word; + hyphens: auto; +} + +fieldset { + border: solid 0.1rem; + box-shadow: 0.1rem 0.1rem 0.1rem var(--shadow-color); + transition: box-shadow var(--duration) var(--timing); +} + +div { + padding: 0.5rem; +} + +aside { + position: absolute; + right: 0; + padding: 0.5rem; +} + +aside:nth-of-type(1) { + top: 0; +} + +aside:nth-of-type(2) { + top: 3rem; +} + +aside:nth-of-type(3) { + top: 7rem; +} + +aside:nth-of-type(4) { + top: 12rem; +} + +#content select, +#content button, +#content input[type="text"], +#content input[type="search"] { + width: 15rem; +} + +dark-mode-toggle { + --dark-mode-toggle-remember-icon-checked: url("checked.svg"); + --dark-mode-toggle-remember-icon-unchecked: url("unchecked.svg"); + --dark-mode-toggle-remember-font: 0.75rem "Helvetica"; + --dark-mode-toggle-legend-font: bold 0.85rem "Helvetica"; + --dark-mode-toggle-label-font: 0.85rem "Helvetica"; + --dark-mode-toggle-color: var(--text-color); + --dark-mode-toggle-background-color: none; + + margin-bottom: 1.5rem; +} + +#dark-mode-toggle-1 { + --dark-mode-toggle-dark-icon: url("sun.png"); + --dark-mode-toggle-light-icon: url("moon.png"); +} + +#dark-mode-toggle-2 { + --dark-mode-toggle-dark-icon: url("sun.svg"); + --dark-mode-toggle-light-icon: url("moon.svg"); + --dark-mode-toggle-icon-size: 2rem; + --dark-mode-toggle-icon-filter: invert(100%); +} + +#dark-mode-toggle-3, +#dark-mode-toggle-4 { + --dark-mode-toggle-dark-icon: url("moon.png"); + --dark-mode-toggle-light-icon: url("sun.png"); +} + +#dark-mode-toggle-3 { + --dark-mode-toggle-remember-filter: invert(100%); +} + +#dark-mode-toggle-4 { + --dark-mode-toggle-active-mode-background-color: var(--accent-color); + --dark-mode-toggle-remember-filter: invert(100%); +} diff --git a/docs/static/dark-light/dark-mode-toggle.mjs b/docs/static/dark-light/dark-mode-toggle.mjs new file mode 100644 index 0000000..da22262 --- /dev/null +++ b/docs/static/dark-light/dark-mode-toggle.mjs @@ -0,0 +1,329 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// @license © 2019 Google LLC. Licensed under the Apache License, Version 2.0. +const doc = document; +const store = localStorage; +const PREFERS_COLOR_SCHEME = 'prefers-color-scheme'; +const MEDIA = 'media'; +const LIGHT = 'light'; +const DARK = 'dark'; +const MQ_DARK = `(${PREFERS_COLOR_SCHEME}:${DARK})`; +const MQ_LIGHT = `(${PREFERS_COLOR_SCHEME}:${LIGHT})`; +const LINK_REL_STYLESHEET = 'link[rel=stylesheet]'; +const REMEMBER = 'remember'; +const LEGEND = 'legend'; +const TOGGLE = 'toggle'; +const SWITCH = 'switch'; +const APPEARANCE = 'appearance'; +const PERMANENT = 'permanent'; +const MODE = 'mode'; +const COLOR_SCHEME_CHANGE = 'colorschemechange'; +const PERMANENT_COLOR_SCHEME = 'permanentcolorscheme'; +const ALL = 'all'; +const NOT_ALL = 'not all'; +const NAME = 'dark-mode-toggle'; +const DEFAULT_URL = 'https://googlechromelabs.github.io/dark-mode-toggle/demo/'; + +// See https://html.spec.whatwg.org/multipage/common-dom-interfaces.html ↵ +// #reflecting-content-attributes-in-idl-attributes. +const installStringReflection = (obj, attrName, propName = attrName) => { + Object.defineProperty(obj, propName, { + enumerable: true, + get() { + const value = this.getAttribute(attrName); + return value === null ? '' : value; + }, + set(v) { + this.setAttribute(attrName, v); + }, + }); +}; + +const installBoolReflection = (obj, attrName, propName = attrName) => { + Object.defineProperty(obj, propName, { + enumerable: true, + get() { + return this.hasAttribute(attrName); + }, + set(v) { + if (v) { + this.setAttribute(attrName, ''); + } else { + this.removeAttribute(attrName); + } + }, + }); +}; + +const template = doc.createElement('template'); +// ⚠️ Note: this is a minified version of `src/template-contents.tpl`. +// Compress the CSS with https://cssminifier.com/, then paste it here. +// eslint-disable-next-line max-len +template.innerHTML = `
`; + +export class DarkModeToggle extends HTMLElement { + static get observedAttributes() { + return [MODE, APPEARANCE, PERMANENT, LEGEND, LIGHT, DARK, REMEMBER]; + } + + constructor() { + super(); + + installStringReflection(this, MODE); + installStringReflection(this, APPEARANCE); + installStringReflection(this, LEGEND); + installStringReflection(this, LIGHT); + installStringReflection(this, DARK); + installStringReflection(this, REMEMBER); + + installBoolReflection(this, PERMANENT); + + this._darkCSS = null; + this._lightCSS = null; + + doc.addEventListener(COLOR_SCHEME_CHANGE, (event) => { + this.mode = event.detail.colorScheme; + this._updateRadios(); + this._updateCheckbox(); + }); + + doc.addEventListener(PERMANENT_COLOR_SCHEME, (event) => { + this.permanent = event.detail.permanent; + this._permanentCheckbox.checked = this.permanent; + }); + + this._initializeDOM(); + } + + _initializeDOM() { + const shadowRoot = this.attachShadow({mode: 'open'}); + shadowRoot.appendChild(template.content.cloneNode(true)); + + // We need to support `media="(prefers-color-scheme: dark)"` (with space) + // and `media="(prefers-color-scheme:dark)"` (without space) + this._darkCSS = doc.querySelectorAll(`${LINK_REL_STYLESHEET}[${MEDIA}*=${PREFERS_COLOR_SCHEME}][${MEDIA}*="${DARK}"]`); + this._lightCSS = doc.querySelectorAll(`${LINK_REL_STYLESHEET}[${MEDIA}*=${PREFERS_COLOR_SCHEME}][${MEDIA}*="${LIGHT}"]`); + + // Get DOM references. + this._lightRadio = shadowRoot.querySelector('[part=lightRadio]'); + this._lightLabel = shadowRoot.querySelector('[part=lightLabel]'); + this._darkRadio = shadowRoot.querySelector('[part=darkRadio]'); + this._darkLabel = shadowRoot.querySelector('[part=darkLabel]'); + this._darkCheckbox = shadowRoot.querySelector('[part=toggleCheckbox]'); + this._checkboxLabel = shadowRoot.querySelector('[part=toggleLabel]'); + this._legendLabel = shadowRoot.querySelector('legend'); + this._permanentAside = shadowRoot.querySelector('aside'); + this._permanentCheckbox = + shadowRoot.querySelector('[part=permanentCheckbox]'); + this._permanentLabel = shadowRoot.querySelector('[part=permanentLabel]'); + + // Does the browser support native `prefers-color-scheme`? + const hasNativePrefersColorScheme = + matchMedia(MQ_DARK).media !== NOT_ALL; + // Listen to `prefers-color-scheme` changes. + if (hasNativePrefersColorScheme) { + matchMedia(MQ_DARK).addListener(({matches}) => { + this.mode = matches ? DARK : LIGHT; + this._dispatchEvent(COLOR_SCHEME_CHANGE, {colorScheme: this.mode}); + }); + } + // Set initial state, giving preference to a remembered value, then the + // native value (if supported), and eventually defaulting to a light + // experience. + const rememberedValue = store.getItem(NAME); + if (rememberedValue && [DARK, LIGHT].includes(rememberedValue)) { + this.mode = rememberedValue; + this._permanentCheckbox.checked = true; + this.permanent = true; + } else if (hasNativePrefersColorScheme) { + this.mode = matchMedia(MQ_LIGHT).matches ? LIGHT : DARK; + } + if (!this.mode) { + this.mode = LIGHT; + } + if (this.permanent && !rememberedValue) { + store.setItem(NAME, this.mode); + } + + // Default to toggle appearance. + if (!this.appearance) { + this.appearance = TOGGLE; + } + + // Update the appearance to either of toggle or switch. + this._updateAppearance(); + + // Update the radios + this._updateRadios(); + + // Make the checkbox reflect the state of the radios + this._updateCheckbox(); + + // Synchronize the behavior of the radio and the checkbox. + [this._lightRadio, this._darkRadio].forEach((input) => { + input.addEventListener('change', () => { + this.mode = this._lightRadio.checked ? LIGHT : DARK; + this._updateCheckbox(); + this._dispatchEvent(COLOR_SCHEME_CHANGE, {colorScheme: this.mode}); + }); + }); + this._darkCheckbox.addEventListener('change', () => { + this.mode = this._darkCheckbox.checked ? DARK : LIGHT; + this._updateRadios(); + this._dispatchEvent(COLOR_SCHEME_CHANGE, {colorScheme: this.mode}); + }); + + // Make remembering the last mode optional + this._permanentCheckbox.addEventListener('change', () => { + this.permanent = this._permanentCheckbox.checked; + this._dispatchEvent(PERMANENT_COLOR_SCHEME, { + permanent: this.permanent, + }); + }); + + // Finally update the mode and let the world know what's going on + this._updateMode(); + this._dispatchEvent(COLOR_SCHEME_CHANGE, {colorScheme: this.mode}); + this._dispatchEvent(PERMANENT_COLOR_SCHEME, { + permanent: this.permanent, + }); + } + + attributeChangedCallback(name, oldValue, newValue) { + if (name === MODE) { + if (![LIGHT, DARK].includes(newValue)) { + throw new RangeError(`Allowed values: "${LIGHT}" and "${DARK}".`); + } + // Only show the dialog programmatically on devices not capable of hover + // and only if there is a label + if (matchMedia('(hover:none)').matches && this.remember) { + this._showPermanentAside(); + } + if (this.permanent) { + store.setItem(NAME, this.mode); + } + this._updateRadios(); + this._updateCheckbox(); + this._updateMode(); + } else if (name === APPEARANCE) { + if (![TOGGLE, SWITCH].includes(newValue)) { + throw new RangeError(`Allowed values: "${TOGGLE}" and "${SWITCH}".`); + } + this._updateAppearance(); + } else if (name === PERMANENT) { + if (this.permanent) { + store.setItem(NAME, this.mode); + } else { + store.removeItem(NAME); + } + this._permanentCheckbox.checked = this.permanent; + } else if (name === LEGEND) { + this._legendLabel.textContent = newValue; + } else if (name === REMEMBER) { + this._permanentLabel.textContent = newValue; + } else if (name === LIGHT) { + this._lightLabel.textContent = newValue; + if (this.mode === LIGHT) { + this._checkboxLabel.textContent = newValue; + } + } else if (name === DARK) { + this._darkLabel.textContent = newValue; + if (this.mode === DARK) { + this._checkboxLabel.textContent = newValue; + } + } + } + + _dispatchEvent(type, value) { + this.dispatchEvent(new CustomEvent(type, { + bubbles: true, + composed: true, + detail: value, + })); + } + + _updateAppearance() { + // Hide or show the light-related affordances dependent on the appearance, + // which can be "switch" or "toggle". + const appearAsToggle = this.appearance === TOGGLE; + this._lightRadio.hidden = appearAsToggle; + this._lightLabel.hidden = appearAsToggle; + this._darkRadio.hidden = appearAsToggle; + this._darkLabel.hidden = appearAsToggle; + this._darkCheckbox.hidden = !appearAsToggle; + this._checkboxLabel.hidden = !appearAsToggle; + } + + _updateRadios() { + if (this.mode === LIGHT) { + this._lightRadio.checked = true; + } else { + this._darkRadio.checked = true; + } + } + + _updateCheckbox() { + if (this.mode === LIGHT) { + this._checkboxLabel.style.setProperty(`--${NAME}-checkbox-icon`, + `var(--${NAME}-light-icon,url("${DEFAULT_URL}moon.png"))`); + this._checkboxLabel.textContent = this.light; + if (!this.light) { + this._checkboxLabel.ariaLabel = DARK; + } + this._darkCheckbox.checked = false; + } else { + this._checkboxLabel.style.setProperty(`--${NAME}-checkbox-icon`, + `var(--${NAME}-dark-icon,url("${DEFAULT_URL}sun.png"))`); + this._checkboxLabel.textContent = this.dark; + if (!this.dark) { + this._checkboxLabel.ariaLabel = LIGHT; + } + this._darkCheckbox.checked = true; + } + } + + _updateMode() { + if (this.mode === LIGHT) { + this._lightCSS.forEach((link) => { + link.media = ALL; + link.disabled = false; + }); + this._darkCSS.forEach((link) => { + link.media = NOT_ALL; + link.disabled = true; + }); + } else { + this._darkCSS.forEach((link) => { + link.media = ALL; + link.disabled = false; + }); + this._lightCSS.forEach((link) => { + link.media = NOT_ALL; + link.disabled = true; + }); + } + } + + _showPermanentAside() { + this._permanentAside.style.visibility = 'visible'; + setTimeout(() => { + this._permanentAside.style.visibility = 'hidden'; + }, 3000); + } +} + +customElements.define(NAME, DarkModeToggle); \ No newline at end of file diff --git a/docs/static/dark-light/dark.css b/docs/static/dark-light/dark.css new file mode 100644 index 0000000..6ed8cfb --- /dev/null +++ b/docs/static/dark-light/dark.css @@ -0,0 +1,36 @@ +/** + * @license + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:root { + color-scheme: dark; /* stylelint-disable-line property-no-unknown */ + + --background-color: rgb(15 15 15); + --text-color: rgb(240 240 240); + --shadow-color: rgb(240 240 240 / 50%); + --accent-color: rgb(0 0 240 / 50%); +} + +img { + filter: grayscale(50%); +} + +.icon { + filter: invert(100%); +} + +a { + color: yellow; +} diff --git a/docs/static/dark-light/light.css b/docs/static/dark-light/light.css new file mode 100644 index 0000000..f73cf7b --- /dev/null +++ b/docs/static/dark-light/light.css @@ -0,0 +1,24 @@ +/** + * @license + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:root { + color-scheme: light; /* stylelint-disable-line property-no-unknown */ + + --background-color: rgb(240 240 240); + --text-color: rgb(15 15 15); + --shadow-color: rgb(15 15 15 / 50%); + --accent-color: rgb(240 0 0 / 50%); +} diff --git a/docs/static/dark-light/moon.png b/docs/static/dark-light/moon.png new file mode 100644 index 0000000000000000000000000000000000000000..0ad57d90044ef1f66d56d9208e6a6b3db43c7c55 GIT binary patch literal 3511 zcmV;o4M_5dP)&$gC;R#7483 zCOQ(#aN&4eN@Z(kZ-$3`=JnGy{=i6EE z+C}x)H}upq;?zpr(pvJ*F6YlY+RjMs$t2UsTG_}!oMNS&QmdLjl#ytel2DS4T%3$O zqKhYog=&g~PJ@75i+nkId1`ufQE+f?a&TR2Y;0<3YIAEqXlQ6=W@cz+QeQ&B=sPftxvO;AiZN=iydNJvIT zMnptJLPA16KR-M?JU2Ht(U1jQ0000ebW%=J00j;qAu=~YM^jf{c6fS+ii?z^s;jHK zz{khc)!5nH;O*}4^Ys1xDKt-&000ZVNklUtM#Tq?2nq-dihzl!syx2>oOA9??Sh-)CC-{Pt9NKy|N5o6mjElpDG=J1l34x@yS6z9n{JvkDWvm$x}rFIcCp%TpG%3Zkw) z+t+WGRz9w;Z)|LCZ86N*-pLhg+p+uFPFLEZx?qjE#6G99U0EfUU{oe~dpB=8&M8ZW z)afSm*&=+pQuq1mx63OZDeJm2;O(8gf8gXP|r6bKi-4M~&KciE|x0 z&oaTKHtX;~W3dnnn$Y8&F%{M@AGJ-edm(tx0gDwi2K_F0s{wN~oOeql4BOaf4B9A6 z8x0qQ4}od5#LWqur^?0V+HgGv9dyg8vd4gT@&%hhu7Vw^+*cQ{S-6tS!oM4ZkGNOM zLpHg)Ie}@|*e|m%28~3G!W_6Yt|p4UZ_%$B=KbnDA5{4Ep@lJLa*vp_4%q^u@^K4e zrP*CJ-J@>Ra>N{)PuZ+|SmFD+u%H;U@c?4NC@huBX5~nCK;Z!tW)Xb@3*!L(UX=GiluV7@xW?z;u;QEExI`)aF3XDPF>Cgb_)){S3<^W zC8KDJ=t&EZj}Fn6wlYR>=g*tQ+B4?XuG%2OY715`Da@+BF7^NdsotCD>BX? zLvG+BCfJm{-FnZSn5+i57&ODisCfib`GW5%&d@h$rmSz&{C6xXRT!$_Qs7bmi|c5( z((KeM4YvGuB+WohU?8* zB83YKS1MO5-%=+B0sCZcsg;JZsB83<&4TO-%WmFQ@U?S*GYp$@6!69dOi@)`qb~|m z$ikaMF7Hsvm2xk`xSAx8O;}>G=qoVXTu!|!N($uK43{dE-a5P|X2znBK?7djL_`&K zi&@jvFv(0ya_kCBjn8{NcBYFzf<3^#_1_+dm;z0EjY z957tzD*v~|6}aq!sqtVGsRa{*V401+D2!(Y~mMJjiYN+CFAL`O=vs^9}Js4Y^B-W(m0Yu<6!n~KK zii^6zeTn511@?VRi`FHA?+mgm zrQ@me^z@@g)6>)G)Ob?EYowvBQpGjv!WCG{6t!EGHaz+eJWj62LnGuql8`)^ysr1ksAVwOXn-Dgy1QH)gdcTUQjg|>j&R3Zfc6qma5A;?$A2PA@1=yT5Oix8!T)zP>!?IhJ@0+$vmS!yhEOI(EiCHrT z6e{Y1h3hLUN8BhR4*6aTFwR&msOqdW!1@LdFHAtBEdRz=`-6SC(1t z?&+5wMcpI&%B1&wV{WzbtW{-oIXz;Q3y%)n@hb1YgKDs{&Zf&L>#G>4zrlMQ8zMW# zHxQ=D<-&u*Tv^T9R59dS6*gSJ74JO}v(9ES<1wO19wk}AH|wE6s$9LIb~VW_s6uM> zP#2R%E@m^?$ry*_iBy^+?%-g|S6L{aXd6(s#3{0C?zG-(sjTQ&d^9mJkw}cFQ}wQkCN|*wE>eF>EE->c?3gY_hJ)aM^2&!e-?xRR)dXbjsEJTH6!0 zSkh}#5EU$3fk|UHs4yzi(ei5_&PP6mVatzHycBJN)vm}`t_lj*tQ;pAGL{X)-eDJU zZBb2DWbbk+n}u=T%u+^3Mr|aXRGYoaF%6>JQOfX z?!-u(Ci>$B;+0 zoFF=#N=+nW_FCrBnzk8*>iSgrQttU$9AxR3b1FLeD;Va*J)!nWvD?`Q>Np zgeb<%FQ!eCRQWH(HuYxw*{hc?U%h-qJm)gTe8PlOX{O1*i#$=m3@d@FD9GG!;~*=PX#{vZFL)t@YdevSYD002ovPDHLkV1gN2&RhTh literal 0 HcmV?d00001 diff --git a/docs/static/dark-light/moon.svg b/docs/static/dark-light/moon.svg new file mode 100644 index 0000000..fad89a4 --- /dev/null +++ b/docs/static/dark-light/moon.svg @@ -0,0 +1,7 @@ + + + + moon + + + diff --git a/docs/static/dark-light/sun.png b/docs/static/dark-light/sun.png new file mode 100644 index 0000000000000000000000000000000000000000..40c9b362bd4efe10a77721de66ea94b12437db56 GIT binary patch literal 1897 zcmV-v2bTDWP)!ye&-ZTBt zRs7RL{nau3#6|zXE&seB|G6&zxe))dEB~|s|F0PTtpWe00{@`^LH*>T0000EbW%=J z00s~oFGyc_ny|y&@b)K_;4lCH2C7L!K~#9!?VF2k+d2$}>-$YP%bZ!JY1w)Iw>%WB zgvplJm0ToPv46#Y4Q)Ph=-R3Swh^8{TqUu9~k`lAeZFnad>@@<67%+9A6*g zRFS90Hv~Dio*v&2;ms>=!N0XZ8{*EdDqLK!Hl^`cYJXy;hcNsc~d90NnXIDiP=NMF#JJ93`a!>*bLYRFsu^$%@lx$?C@6 zX@f{aF}aLxg9`wJwdy@rmTakkxK@P5tG|&T52?)UvuxcYsYETWne>Krj;X1gtDMb-{62(6$%q&tE@D^tZ6vj=#;3M}Q^`FK__Nl{q_oyuHlun z+hk~4<}!Ah417}VR$;j(;ck*?KeNGmm1BDp?k1VTjOE9E72Q!By`g1ox5w@az;XRq zgT>@y`vtDf)$zy%3wM)zJnmmySwo%{gSW|xuiIUH-6+XQh0fE$-7uMbeZ(pah89($ z5W&M>2D{~jO_`^05ERe~S|5X*uTKOfsk+FL$On1=5^^acU>{$%ok0=+G!?-=2i00z ztT9{xbPQ%s`}n%EXCslV6nlLON`k&$YP>UpeL_rf;F;?IV|oBhQgxaC>2(KX5p*0~ zO}i9YKAx7exqxleH}t)>IdPM1U$K0r-Q31xmaAO-z*+?S}fp+!A^tzttk?xd3lZTuqVa>o}OBKxJ{>#tm?(^ zo#3{e1$+yAxUHikFKe}ItMCq!E|r};cbinpkW;EvtAEB7BK)S@U#jo1XLLYm2?_b`ZW8CuCQV+ z|9i{Zj}ih3`eHvfDp9UZpEVfsueeEsT-u8L;7p-GziVp8E$saU<9znKm?Nrm|Lqv@ z?A40srnA{&vb3gWZgP1wUl0%ZE^frFJ^yx-L8y~`D+_QYu-MFKmOvd=xM!Kogsv=W zu#gG8+ESEe<|HlDz=mQ!NfT9XHBqH~%dNeAE!1fiGPFl&8w=H8TR301Y_iINSC%G& z9ogN%)*9ttX5mf)Koy5R=5fOiH@*DKXkcbd+@aTjTPk+55GGGN3#p2{s1>nW)~RI? zw_C;}cQXl@vV}~4#))MBKvikPL*WJiz|DV7JpPfv5CWQsxib9;ns+gI08*Vta%|uw zmYgiGa+bx*m~9N3cgfJam)W9>?Pav+w6vyWYP_HWCVX;rU9eiqU{OxWyV2s^D7BW^ zqSh)4nX+Bx$JKA6>!d+3+2C*GM(k$RBRji_16G!snCJ0>7WVA)=jJ$k# zPp1)`HPoDZ87p`^QYGVXW00000NkvXXu0mjf*I}_| literal 0 HcmV?d00001 diff --git a/docs/static/dark-light/sun.svg b/docs/static/dark-light/sun.svg new file mode 100644 index 0000000..0b18941 --- /dev/null +++ b/docs/static/dark-light/sun.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/docs/static/dark-light/unchecked.svg b/docs/static/dark-light/unchecked.svg new file mode 100644 index 0000000..6702330 --- /dev/null +++ b/docs/static/dark-light/unchecked.svg @@ -0,0 +1 @@ + \ No newline at end of file From ee44b4e984d38f9d6bd34cc7f5bd7bd40c2e5475 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Mon, 21 Feb 2022 19:14:12 +0100 Subject: [PATCH 27/77] docs: Mention VSCode support --- docs/conf.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 048d9d2..d015a01 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -28,10 +28,20 @@ # Try to get branch at which this is running # and try to determine which version to display in sphinx -# Version is using git tag if on master or "latest-develop" if on develop branch +# Version is using git tag if on master/main or "latest-develop" if on develop branch version = '' git_branch = '' +def cmd_exec_print(t): + print("cmd > ", t, "\n", os.popen(t).read().strip(), "\n") + +# Print demo data here +cmd_exec_print('git branch') +cmd_exec_print('git describe') +cmd_exec_print('git describe --tags') +cmd_exec_print('git describe --tags --abbrev=0') +cmd_exec_print('git describe --tags --abbrev=1') + # Get current branch res = os.popen('git branch').read().strip() for line in res.split("\n"): @@ -41,17 +51,18 @@ # Decision for display version git_branch = git_branch.replace('(HEAD detached at ', '').replace(')', '') if git_branch.find('master') >= 0 or git_branch.find('main') >= 0: - version = os.popen('git describe --tags --abbrev=0').read().strip() - if version == '': - version = 'v0.0.0' -elif git_branch.find('develop') != -1 and not (git_branch.find('develop-') >= 0 or git_branch.find('develop/') >= 0): + #version = os.popen('git describe --tags --abbrev=0').read().strip() + version = 'latest-stable' +elif git_branch.find('develop-') >= 0 or git_branch.find('develop/') >= 0: + version = 'branch-' + git_branch +elif git_branch == 'develop' or git_branch == 'origin/develop': version = 'latest-develop' else: - version = 'branch-' + git_branch + version = os.popen('git describe --tags --abbrev=0').read().strip() # For debugging purpose only print("GIT BRANCH: " + git_branch) -print("GIT VERSION: " + version) +print("PROJ VERSION: " + version) # -- General configuration --------------------------------------------------- From 80a4b99ce2be0a54bd289cadc21115ec6543780f Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Tue, 22 Feb 2022 22:04:34 +0100 Subject: [PATCH 28/77] Update tasks.json to add docs commands --- .vscode/tasks.json | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index a4527df..4a5e5eb 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -42,5 +42,32 @@ "args": [], "problemMatcher": [], }, + { + "label": "Docs: Install python plugins from requirements.txt file", + "type": "shell", + "command": "python -m pip install -r requirements.txt", + "options": { + "cwd": "${workspaceFolder}/docs" + }, + "problemMatcher": [] + }, + { + "label": "Docs: Generate html", + "type": "shell", + "command": ".\\make html", + "options": { + "cwd": "${workspaceFolder}/docs" + }, + "problemMatcher": [] + }, + { + "label": "Docs: Clean build directory", + "type": "shell", + "command": ".\\make clean", + "options": { + "cwd": "${workspaceFolder}/docs" + }, + "problemMatcher": [] + }, ] } \ No newline at end of file From 80f8142f97e6d03792735ebc7f14f10f5fc59617 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Fri, 4 Mar 2022 11:46:39 +0100 Subject: [PATCH 29/77] Add inner directive for doxygengroup --- docs/api-reference/lwjson_opt.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/api-reference/lwjson_opt.rst b/docs/api-reference/lwjson_opt.rst index 4bed334..754f4dc 100644 --- a/docs/api-reference/lwjson_opt.rst +++ b/docs/api-reference/lwjson_opt.rst @@ -9,4 +9,5 @@ When any of the settings shall be modified, it shall be done in dedicated applic .. note:: Check :ref:`getting_started` for guidelines on how to create and use configuration file. -.. doxygengroup:: LWJSON_OPT \ No newline at end of file +.. doxygengroup:: LWJSON_OPT + :inner: \ No newline at end of file From 7dd512b05ca54543b32018556c4e753150183e7b Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Tue, 12 Apr 2022 21:16:53 +0200 Subject: [PATCH 30/77] Add first files for streaming JSON --- lwjson/CMakeLists.txt | 1 + lwjson/src/include/lwjson/lwjson.h | 26 +++++++++++++++++ lwjson/src/lwjson/lwjson_stream.c | 45 ++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 lwjson/src/lwjson/lwjson_stream.c diff --git a/lwjson/CMakeLists.txt b/lwjson/CMakeLists.txt index b5858c3..c005663 100644 --- a/lwjson/CMakeLists.txt +++ b/lwjson/CMakeLists.txt @@ -6,6 +6,7 @@ message("Entering ${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt") # Setup generic source files set(lwjson_core_SRCS ${CMAKE_CURRENT_LIST_DIR}/src/lwjson/lwjson.c + ${CMAKE_CURRENT_LIST_DIR}/src/lwjson/lwjson_stream.c ) # Debug sources diff --git a/lwjson/src/include/lwjson/lwjson.h b/lwjson/src/include/lwjson/lwjson.h index 005439f..39fe44e 100644 --- a/lwjson/src/include/lwjson/lwjson.h +++ b/lwjson/src/include/lwjson/lwjson.h @@ -132,6 +132,32 @@ lwjsonr_t lwjson_free(lwjson_t* lw); void lwjson_print_token(const lwjson_token_t* token); void lwjson_print_json(const lwjson_t* lw); +/** + * \brief Stream parsing stack object + */ +typedef struct { + lwjson_type_t type; + char name[32]; /*!< Last known dictionary name. Not used for array types + TODO: Add conditional compilation to decrease memory size even more */ +} lwjson_stream_stack_t; + +/** + * \brief LwJSON streaming structure + */ +typedef struct { + lwjson_stream_stack_t stack[16]; /*!< Stack used for parsing */ + size_t stack_pos; /*!< Current stack position */ + + /* State */ + union { + struct { + char buff[512]; /*!< Buffer to write temporary data */ + size_t buff_pos; /*!< Buffer position for next write (length of bytes in buffer) */ + } str; /*!< String structure */ + /* Todo: Add other types */ + } data; /*!< Data union used to parse various */ +} lwjson_stream_t; + /** * \brief Get number of tokens used to parse JSON * \param[in] lw: Pointer to LwJSON instance diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c new file mode 100644 index 0000000..800b423 --- /dev/null +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -0,0 +1,45 @@ +/** + * \file lwjson_stream.c + * \brief Lightweight JSON format parser + */ + +/* + * Copyright (c) 2022 Tilen MAJERLE + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE + * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * This file is part of LwJSON - Lightweight JSON format parser. + * + * Author: Tilen MAJERLE + * Version: v1.5.0 + */ +#include +#include "lwjson/lwjson.h" + +/** + * \brief Initialize LwJSON stream object before parsing takes place + * \param[in,out] js: Stream JSON structure + * \return \ref lwjsonOK on success, member of \ref lwjsonr_t otherwise + */ +lwjsonr_t +lwjson_stream_init(lwjson_stream_t* js) { + return lwjsonOK; +} From 1cf36a68ba735c4d733c5db068e0d398dd244f1c Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Wed, 13 Apr 2022 14:58:31 +0200 Subject: [PATCH 31/77] Update vscode c_cpp_properties.json to use cmake as provider --- .vscode/c_cpp_properties.json | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 67291f2..69fa685 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -2,16 +2,6 @@ "configurations": [ { "name": "Win32", - "includePath": [ - "${workspaceFolder}\\dev\\", - "${workspaceFolder}\\lwjson\\src\\include\\" - ], - "defines": [ - "WIN32", - "_DEBUG", - "UNICODE", - "_UNICODE" - ], "compilerPath": "c:\\msys64\\mingw64\\bin\\gcc.exe", "cStandard": "gnu17", "cppStandard": "gnu++14", From ccd0ec984195be3946f3a278f6e1e6d19d97ed06 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Thu, 14 Apr 2022 16:21:30 +0200 Subject: [PATCH 32/77] Add rough prototypes for streamer --- lwjson/src/include/lwjson/lwjson.h | 30 +++++++++++++++++-- lwjson/src/lwjson/lwjson_debug.c | 2 +- lwjson/src/lwjson/lwjson_stream.c | 48 ++++++++++++++++++++++++++++-- 3 files changed, 74 insertions(+), 6 deletions(-) diff --git a/lwjson/src/include/lwjson/lwjson.h b/lwjson/src/include/lwjson/lwjson.h index 39fe44e..6443c55 100644 --- a/lwjson/src/include/lwjson/lwjson.h +++ b/lwjson/src/include/lwjson/lwjson.h @@ -132,22 +132,46 @@ lwjsonr_t lwjson_free(lwjson_t* lw); void lwjson_print_token(const lwjson_token_t* token); void lwjson_print_json(const lwjson_t* lw); +/** + * \brief Object type for streaming parser + */ +typedef enum { + LWJSON_STREAM_TYPE_NONE, + LWJSON_STREAM_TYPE_OBJECT, + LWJSON_STREAM_TYPE_OBJECT_END, + LWJSON_STREAM_TYPE_ARRAY, + LWJSON_STREAM_TYPE_ARRAY_END, + LWJSON_STREAM_TYPE_KEY, + LWJSON_STREAM_TYPE_STRING, + LWJSON_STREAM_TYPE_TRUE, + LWJSON_STREAM_TYPE_FALSE, + LWJSON_STREAM_TYPE_NULL, + LWJSON_STREAM_TYPE_INT, + LWJSON_STREAM_TYPE_REAL, +} lwjson_stream_type_t; + /** * \brief Stream parsing stack object */ typedef struct { - lwjson_type_t type; + lwjson_stream_type_t type; /*!< Streaming type - current value */ char name[32]; /*!< Last known dictionary name. Not used for array types TODO: Add conditional compilation to decrease memory size even more */ } lwjson_stream_stack_t; +typedef enum { + LWJSON_STREAM_STATE_WAITINGFIRSTCHAR = 0x00,/*!< State to wait for very first opening character */ +} lwjson_stream_state_t; + /** * \brief LwJSON streaming structure */ typedef struct { - lwjson_stream_stack_t stack[16]; /*!< Stack used for parsing */ + lwjson_stream_stack_t stack[16]; /*!< Stack used for parsing. TODO: Add conditional compilation flag */ size_t stack_pos; /*!< Current stack position */ + lwjson_stream_state_t parse_state; /*!< Parser state */ + /* State */ union { struct { @@ -156,7 +180,7 @@ typedef struct { } str; /*!< String structure */ /* Todo: Add other types */ } data; /*!< Data union used to parse various */ -} lwjson_stream_t; +} lwjson_stream_parser_t; /** * \brief Get number of tokens used to parse JSON diff --git a/lwjson/src/lwjson/lwjson_debug.c b/lwjson/src/lwjson/lwjson_debug.c index 7eb572b..f237e92 100644 --- a/lwjson/src/lwjson/lwjson_debug.c +++ b/lwjson/src/lwjson/lwjson_debug.c @@ -131,4 +131,4 @@ void lwjson_print_json(const lwjson_t* lw) { lwjson_token_print_t p = { 0 }; prv_print_token(&p, lwjson_get_first_token(lw)); -} \ No newline at end of file +} diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index 800b423..69d89bb 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -34,12 +34,56 @@ #include #include "lwjson/lwjson.h" +lwjson_stream_type_t +prv_stack_push(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { + if (jsp->stack_pos < LWJSON_ARRAYSIZE(jsp->stack)) { + jsp->stack[jsp->stack_pos++].type = type; + /* TODO: Copy name in case of non-array object */ + return 1; + } + return 0; +} + +lwjson_stream_type_t +prv_stack_pop(lwjson_stream_parser_t* jsp) { + if (jsp->stack_pos > 0) { + /* TODO: Check the return values */ + return jsp->stack[jsp->stack_pos--].type; + } + /* TODO: Replace 0 with define */ + return 0; +} + /** * \brief Initialize LwJSON stream object before parsing takes place - * \param[in,out] js: Stream JSON structure + * \param[in,out] jsp: Stream JSON structure * \return \ref lwjsonOK on success, member of \ref lwjsonr_t otherwise */ lwjsonr_t -lwjson_stream_init(lwjson_stream_t* js) { +lwjson_stream_init(lwjson_stream_parser_t* jsp) { + jsp->parse_state = LWJSON_STREAM_STATE_WAITINGFIRSTCHAR; return lwjsonOK; } + +/** + * \brief Parse JSON string in streaming mode + * \param[in,out] jsp: Stream JSON structure + * \param[in] c: Character to parse + * \return lwjsonr_t + */ +lwjsonr_t +lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { + switch (jsp->parse_state) { + + /* + * Waiting for very first valid characters, + * that is used to indicate start of JSON stream + */ + case LWJSON_STREAM_STATE_WAITINGFIRSTCHAR: { + if (c == '{' || c == '[') { + + } + break; + } + } +} From 2063661181277b0cdd72c98f64a257dc51e1c029 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Thu, 14 Apr 2022 21:51:17 +0200 Subject: [PATCH 33/77] Add more code for parser - first switch statements with simple code --- .vscode/launch.json | 2 +- .vscode/tasks.json | 3 + dev/main.c | 19 +++++- lwjson/src/include/lwjson/lwjson.h | 11 +++- lwjson/src/lwjson/lwjson_stream.c | 100 +++++++++++++++++++++++++++-- test/json/custom_stream.json | 6 ++ 6 files changed, 130 insertions(+), 11 deletions(-) create mode 100644 test/json/custom_stream.json diff --git a/.vscode/launch.json b/.vscode/launch.json index 8b95413..51aec30 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -12,7 +12,7 @@ "miDebuggerPath": "c:\\msys64\\mingw64\\bin\\gdb.exe", "args": [], "stopAtEntry": false, - "cwd": "${fileDirname}", + "cwd": "${workspaceFolder}", "environment": [], "console": "integratedTerminal" } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 4a5e5eb..7b3751b 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -40,6 +40,9 @@ "label": "Run application", "command": "${workspaceFolder}\\build\\LwLibPROJECT.exe", "args": [], + "options": { + "cwd": "${workspaceFolder}" + }, "problemMatcher": [], }, { diff --git a/dev/main.c b/dev/main.c index 902958a..61f10f4 100644 --- a/dev/main.c +++ b/dev/main.c @@ -4,9 +4,13 @@ #include "windows.h" #include "lwjson/lwjson.h" +/* Classic parser */ static lwjson_token_t tokens[4096]; static lwjson_t lwjson; +/* Stream parser */ +static lwjson_stream_parser_t stream_parser; + extern void test_run(void); extern void example_minimal_run(void); extern void example_traverse_run(void); @@ -19,16 +23,16 @@ main() { char* json_text = NULL; const lwjson_token_t* tkn; - test_run(); + //test_run(); //example_minimal_run(); //example_traverse_run(); - return 0; + //return 0; printf("\n---\n"); /* Init JSON */ lwjson_init(&lwjson, tokens, LWJSON_ARRAYSIZE(tokens)); - f = CreateFile(TEXT("..\\..\\test\\json\\custom.json"), + f = CreateFile(TEXT("test\\json\\custom_stream.json"), GENERIC_READ, // open for reading 0, // do not share NULL, // no security @@ -56,11 +60,20 @@ main() { goto exit; } + /* Now parse as a stream */ + lwjson_stream_init(&stream_parser); + for (const char* str = json_text; str != NULL && *str != '\0'; ++str) { + lwjson_stream_parse(&stream_parser, *str); + } + return 0; + /* Start parsing */ + printf("Parsing JSON with full text\r\n"); if (lwjson_parse(&lwjson, json_text) != lwjsonOK) { printf("Could not parse input JSON\r\n"); goto exit; } + printf("Full JSON parsed\r\n"); /* Dump result */ lwjson_print_json(&lwjson); diff --git a/lwjson/src/include/lwjson/lwjson.h b/lwjson/src/include/lwjson/lwjson.h index 6443c55..66435c0 100644 --- a/lwjson/src/include/lwjson/lwjson.h +++ b/lwjson/src/include/lwjson/lwjson.h @@ -107,6 +107,10 @@ typedef enum { lwjsonERRJSON, /*!< Error JSON format */ lwjsonERRMEM, /*!< Memory error */ lwjsonERRPAR, /*!< Parameter error */ + + lwjsonSTREAMNONE, /*!< No new info to process - parsing in progress */ + lwjsonSTREAMINPROGRESS, /*!< Stream token parsing is in progress */ + lwjsonSTREAMDONE, /*!< Streaming parser is done */ } lwjsonr_t; /** @@ -161,6 +165,8 @@ typedef struct { typedef enum { LWJSON_STREAM_STATE_WAITINGFIRSTCHAR = 0x00,/*!< State to wait for very first opening character */ + LWJSON_STREAM_STATE_PARSING, /*!< In parsing of the first char state */ + LWJSON_STREAM_STATE_PARSING_STRING, /*!< Parse string primitive */ } lwjson_stream_state_t; /** @@ -175,13 +181,16 @@ typedef struct { /* State */ union { struct { - char buff[512]; /*!< Buffer to write temporary data */ + char buff[512]; /*!< Buffer to write temporary data. TODO: Size to be variable with define */ size_t buff_pos; /*!< Buffer position for next write (length of bytes in buffer) */ } str; /*!< String structure */ /* Todo: Add other types */ } data; /*!< Data union used to parse various */ } lwjson_stream_parser_t; +lwjsonr_t lwjson_stream_init(lwjson_stream_parser_t* jsp); +lwjsonr_t lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c); + /** * \brief Get number of tokens used to parse JSON * \param[in] lw: Pointer to LwJSON instance diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index 69d89bb..ecfe943 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -33,8 +33,9 @@ */ #include #include "lwjson/lwjson.h" +#include -lwjson_stream_type_t +uint8_t prv_stack_push(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { if (jsp->stack_pos < LWJSON_ARRAYSIZE(jsp->stack)) { jsp->stack[jsp->stack_pos++].type = type; @@ -47,11 +48,18 @@ prv_stack_push(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { lwjson_stream_type_t prv_stack_pop(lwjson_stream_parser_t* jsp) { if (jsp->stack_pos > 0) { - /* TODO: Check the return values */ - return jsp->stack[jsp->stack_pos--].type; + jsp->stack_pos--; + return jsp->stack[jsp->stack_pos].type; } - /* TODO: Replace 0 with define */ - return 0; + return LWJSON_STREAM_TYPE_NONE; +} + +lwjson_stream_type_t +prv_stack_get_top(lwjson_stream_parser_t* jsp) { + if (jsp->stack_pos > 0) { + return jsp->stack[jsp->stack_pos - 1].type; + } + return LWJSON_STREAM_TYPE_NONE; } /** @@ -73,17 +81,97 @@ lwjson_stream_init(lwjson_stream_parser_t* jsp) { */ lwjsonr_t lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { + /* Get first character first */ + if (jsp->parse_state == LWJSON_STREAM_STATE_WAITINGFIRSTCHAR + && c != '{' && c != '[') { + return lwjsonOK; + } + + /* + * Determine what to do from parsing state + */ switch (jsp->parse_state) { /* * Waiting for very first valid characters, * that is used to indicate start of JSON stream */ - case LWJSON_STREAM_STATE_WAITINGFIRSTCHAR: { + case LWJSON_STREAM_STATE_WAITINGFIRSTCHAR: + case LWJSON_STREAM_STATE_PARSING: { + /* Determine start or object or an array */ if (c == '{' || c == '[') { + printf("Stack push for %s\r\n", c == '{' ? "object" : "array"); + if (!prv_stack_push(jsp, c == '{' ? LWJSON_STREAM_TYPE_OBJECT : LWJSON_STREAM_TYPE_ARRAY)) { + return lwjsonERRMEM; + } + jsp->parse_state = LWJSON_STREAM_STATE_PARSING; + + /* Determine end or object or an array */ + } else if (c == '}' || c == '}') { + printf("Stack pop for %s\r\n", c == '}' ? "object" : "array"); + if (!prv_stack_pop(jsp)) { + return lwjsonERR; + } + + /* Determine start of string - can be key or regular string (in array or after key) */ + } else if (c == '"') { + jsp->parse_state = LWJSON_STREAM_STATE_PARSING_STRING; + printf("Start of string parsing\r\n"); + + /* Check for end of key character */ + } else if (c == ':') { + lwjson_stream_type_t t = prv_stack_get_top(jsp); + + /* + * Color character means end of key. + * + * Valid key can only happen if its parent (top of current stack) + * is an object - otherwise trigger and error + */ + if (t == LWJSON_STREAM_TYPE_OBJECT) { + if (!prv_stack_push(jsp, LWJSON_STREAM_TYPE_KEY)) { + return lwjsonERRMEM; + } + } else { + printf("Error - wrong ':' character\r\n"); + } + /* Check if this is start of number */ + } else if (c == '-' || (c >= '0' && c <= '9')) { + + /* Check if it is start of either "true", "false" or "null" */ + } else if (c == 't' || c == 'f' || c == 'n') { } break; } + + /* + * Parse any type of string in a sequence + * + * It is used for key or string in an object or an array + */ + case LWJSON_STREAM_STATE_PARSING_STRING: { + /* + * Quote character may trigger end of string, + * or if backslasled before - it is part of string + */ + if (c == '"') { + lwjson_stream_type_t t = prv_stack_get_top(jsp); + if (t == LWJSON_STREAM_TYPE_OBJECT) { + printf("End of key parsing\r\n"); + } else if (t == LWJSON_STREAM_TYPE_ARRAY) { + printf("End of string in array parsing\r\n"); + } else if (t == LWJSON_STREAM_TYPE_KEY) { + printf("End of string after key parsing (in object)\r\n"); + prv_stack_pop(jsp); + } + jsp->parse_state = LWJSON_STREAM_STATE_PARSING; + } + break; + } + + /* TODO: Add other case statements */ + default: + break; } } diff --git a/test/json/custom_stream.json b/test/json/custom_stream.json new file mode 100644 index 0000000..d2e2dd1 --- /dev/null +++ b/test/json/custom_stream.json @@ -0,0 +1,6 @@ +{ + "test": "abc", + "array": [ + "123", "def", "ghi" + ] +} \ No newline at end of file From 1bb93726f1043ea8b561057bb9fd62a0f09fbaf8 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sat, 16 Apr 2022 00:27:53 +0200 Subject: [PATCH 34/77] Add simple code to properly parse strings and keys (at least) --- .vscode/settings.json | 6 +++ lwjson/src/lwjson/lwjson_stream.c | 66 ++++++++++++++++++++++++++----- test/json/custom_stream.json | 9 ++++- 3 files changed, 70 insertions(+), 11 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..80d947b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "files.associations": { + "string.h": "c", + "lwjson.h": "c" + } +} \ No newline at end of file diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index ecfe943..777b967 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -33,12 +33,42 @@ */ #include #include "lwjson/lwjson.h" + +#if defined(LWJSON_DEV) #include +#define DEBUG_STRING_PREFIX_SPACES " " +#define LWJSON_DEBUG(jsp, ...) do { \ + if ((jsp) != NULL) { \ + printf("%.*s", (int)(4 * (jsp)->stack_pos), DEBUG_STRING_PREFIX_SPACES); \ + } \ + printf(__VA_ARGS__); \ +} while (0) + +/* Strings for debug */ +static const char* type_strings[] = { + [LWJSON_STREAM_TYPE_NONE] = "none", + [LWJSON_STREAM_TYPE_OBJECT] = "object", + [LWJSON_STREAM_TYPE_OBJECT_END] = "object_end", + [LWJSON_STREAM_TYPE_ARRAY] = "array", + [LWJSON_STREAM_TYPE_ARRAY_END] = "array_end", + [LWJSON_STREAM_TYPE_KEY] = "key", + [LWJSON_STREAM_TYPE_STRING] = "string", + [LWJSON_STREAM_TYPE_TRUE] = "true", + [LWJSON_STREAM_TYPE_FALSE] = "false", + [LWJSON_STREAM_TYPE_NULL] = "null", + [LWJSON_STREAM_TYPE_INT] = "int", + [LWJSON_STREAM_TYPE_REAL] = "real", +}; +#else +#define LWJSON_DEBUG(jsp, ...) +#endif /* defined(LWJSON_DEV) */ uint8_t prv_stack_push(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { if (jsp->stack_pos < LWJSON_ARRAYSIZE(jsp->stack)) { - jsp->stack[jsp->stack_pos++].type = type; + jsp->stack[jsp->stack_pos].type = type; + LWJSON_DEBUG(jsp, "Pushed to stack: %s\r\n", type_strings[type]); + jsp->stack_pos++; /* TODO: Copy name in case of non-array object */ return 1; } @@ -49,6 +79,7 @@ lwjson_stream_type_t prv_stack_pop(lwjson_stream_parser_t* jsp) { if (jsp->stack_pos > 0) { jsp->stack_pos--; + LWJSON_DEBUG(jsp, "Popped from stack: %s\r\n", type_strings[jsp->stack[jsp->stack_pos].type]); return jsp->stack[jsp->stack_pos].type; } return LWJSON_STREAM_TYPE_NONE; @@ -100,23 +131,35 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { case LWJSON_STREAM_STATE_PARSING: { /* Determine start or object or an array */ if (c == '{' || c == '[') { - printf("Stack push for %s\r\n", c == '{' ? "object" : "array"); if (!prv_stack_push(jsp, c == '{' ? LWJSON_STREAM_TYPE_OBJECT : LWJSON_STREAM_TYPE_ARRAY)) { return lwjsonERRMEM; } jsp->parse_state = LWJSON_STREAM_STATE_PARSING; /* Determine end or object or an array */ - } else if (c == '}' || c == '}') { - printf("Stack pop for %s\r\n", c == '}' ? "object" : "array"); + } else if (c == '}' || c == ']') { if (!prv_stack_pop(jsp)) { return lwjsonERR; } + if (prv_stack_get_top(jsp) == LWJSON_STREAM_TYPE_KEY) { + prv_stack_pop(jsp); + } /* Determine start of string - can be key or regular string (in array or after key) */ } else if (c == '"') { +#if defined(LWJSON_DEV) + lwjson_stream_type_t t = prv_stack_get_top(jsp); + if (t == LWJSON_STREAM_TYPE_OBJECT) { + LWJSON_DEBUG(jsp, "Start of string parsing - expected key name in an object\r\n"); + } else if (t == LWJSON_STREAM_TYPE_KEY) { + LWJSON_DEBUG(jsp, "Start of string parsing - string value associated to previous key in an object\r\n"); + } else if (t == LWJSON_STREAM_TYPE_ARRAY) { + LWJSON_DEBUG(jsp, "Start of string parsing - string entry in an array\r\n"); + } +#endif /* defined(LWJSON_DEV) */ + jsp->parse_state = LWJSON_STREAM_STATE_PARSING_STRING; - printf("Start of string parsing\r\n"); + memset(&jsp->data.str, 0x00, sizeof(jsp->data.str)); /* Check for end of key character */ } else if (c == ':') { @@ -133,7 +176,7 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { return lwjsonERRMEM; } } else { - printf("Error - wrong ':' character\r\n"); + LWJSON_DEBUG(jsp, "Error - wrong ':' character\r\n"); } /* Check if this is start of number */ } else if (c == '-' || (c >= '0' && c <= '9')) { @@ -158,14 +201,17 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { if (c == '"') { lwjson_stream_type_t t = prv_stack_get_top(jsp); if (t == LWJSON_STREAM_TYPE_OBJECT) { - printf("End of key parsing\r\n"); - } else if (t == LWJSON_STREAM_TYPE_ARRAY) { - printf("End of string in array parsing\r\n"); + LWJSON_DEBUG(jsp, "End of string parsing - object key name: \"%s\"\r\n", jsp->data.str.buff); } else if (t == LWJSON_STREAM_TYPE_KEY) { - printf("End of string after key parsing (in object)\r\n"); + LWJSON_DEBUG(jsp, "End of string parsing - string value associated to previous key in an object: \"%s\"\r\n", jsp->data.str.buff); prv_stack_pop(jsp); + } else if (t == LWJSON_STREAM_TYPE_ARRAY) { + LWJSON_DEBUG(jsp, "End of string parsing - an array string entry: \"%s\"\r\n", jsp->data.str.buff); } jsp->parse_state = LWJSON_STREAM_STATE_PARSING; + } else { + jsp->data.str.buff[jsp->data.str.buff_pos++] = c; + /* TODO: Add 0 if necessary */ } break; } diff --git a/test/json/custom_stream.json b/test/json/custom_stream.json index d2e2dd1..adb54ac 100644 --- a/test/json/custom_stream.json +++ b/test/json/custom_stream.json @@ -1,6 +1,13 @@ { "test": "abc", "array": [ - "123", "def", "ghi" + "123", + "def", + "ghi" + ], + "array_in_array": [ + ["1", "2", "3"], + ["4", "5", "6"], + ["7", "8", "9"] ] } \ No newline at end of file From 19052dbe74c8ebb95fa34df291bbbf9e7cf2a30d Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sat, 16 Apr 2022 09:58:06 +0200 Subject: [PATCH 35/77] Add support for primitive parsing (number or logical values) --- lwjson/src/include/lwjson/lwjson.h | 7 +++- lwjson/src/lwjson/lwjson_stream.c | 53 ++++++++++++++++++++++++++---- test/json/custom_stream.json | 53 +++++++++++++++++++++++------- 3 files changed, 93 insertions(+), 20 deletions(-) diff --git a/lwjson/src/include/lwjson/lwjson.h b/lwjson/src/include/lwjson/lwjson.h index 66435c0..9ea5544 100644 --- a/lwjson/src/include/lwjson/lwjson.h +++ b/lwjson/src/include/lwjson/lwjson.h @@ -165,8 +165,9 @@ typedef struct { typedef enum { LWJSON_STREAM_STATE_WAITINGFIRSTCHAR = 0x00,/*!< State to wait for very first opening character */ - LWJSON_STREAM_STATE_PARSING, /*!< In parsing of the first char state */ + LWJSON_STREAM_STATE_PARSING, /*!< In parsing of the first char state - detecting next character state */ LWJSON_STREAM_STATE_PARSING_STRING, /*!< Parse string primitive */ + LWJSON_STREAM_STATE_PARSING_PRIMITIVE, /*!< Parse any primitive that is non-string, either "true", "false", "null" or a number */ } lwjson_stream_state_t; /** @@ -184,6 +185,10 @@ typedef struct { char buff[512]; /*!< Buffer to write temporary data. TODO: Size to be variable with define */ size_t buff_pos; /*!< Buffer position for next write (length of bytes in buffer) */ } str; /*!< String structure */ + struct { + char buff[32]; /*!< Temporary write buffer */ + size_t buff_pos; /*!< Buffer position for next write */ + } prim; /*!< Primitive object */ /* Todo: Add other types */ } data; /*!< Data union used to parse various */ } lwjson_stream_parser_t; diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index 777b967..831de48 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -93,6 +93,8 @@ prv_stack_get_top(lwjson_stream_parser_t* jsp) { return LWJSON_STREAM_TYPE_NONE; } +#define prv_is_space_char_ext(c) ((c) == ' ' || (c) == '\t' || (c) == '\r' || (c) == '\n' || (c) == '\f') + /** * \brief Initialize LwJSON stream object before parsing takes place * \param[in,out] jsp: Stream JSON structure @@ -178,12 +180,14 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { } else { LWJSON_DEBUG(jsp, "Error - wrong ':' character\r\n"); } - /* Check if this is start of number */ - } else if (c == '-' || (c >= '0' && c <= '9')) { - - /* Check if it is start of either "true", "false" or "null" */ - } else if (c == 't' || c == 'f' || c == 'n') { - + /* Check if this is start of number or "true", "false" or "null" */ + } else if (c == '-' || (c >= '0' && c <= '9') + || c == 't' || c == 'f' || c == 'n') { + LWJSON_DEBUG(jsp, "Start of primitive parsing parsing - %s, %c\r\n", + (c == '-' || (c >= '0' && c <= '9')) ? "number" : "true,false,null", c); + jsp->parse_state = LWJSON_STREAM_STATE_PARSING_PRIMITIVE; + memset(&jsp->data.prim, 0x00, sizeof(jsp->data.prim)); + jsp->data.prim.buff[jsp->data.prim.buff_pos++] = c; } break; } @@ -197,17 +201,23 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { /* * Quote character may trigger end of string, * or if backslasled before - it is part of string + * + * TODO: Handle backslash */ if (c == '"') { lwjson_stream_type_t t = prv_stack_get_top(jsp); +#if defined(LWJSON_DEV) if (t == LWJSON_STREAM_TYPE_OBJECT) { LWJSON_DEBUG(jsp, "End of string parsing - object key name: \"%s\"\r\n", jsp->data.str.buff); } else if (t == LWJSON_STREAM_TYPE_KEY) { LWJSON_DEBUG(jsp, "End of string parsing - string value associated to previous key in an object: \"%s\"\r\n", jsp->data.str.buff); - prv_stack_pop(jsp); } else if (t == LWJSON_STREAM_TYPE_ARRAY) { LWJSON_DEBUG(jsp, "End of string parsing - an array string entry: \"%s\"\r\n", jsp->data.str.buff); } +#endif /* defined(LWJSON_DEV) */ + if (t == LWJSON_STREAM_TYPE_KEY) { + prv_stack_pop(jsp); + } jsp->parse_state = LWJSON_STREAM_STATE_PARSING; } else { jsp->data.str.buff[jsp->data.str.buff_pos++] = c; @@ -216,6 +226,35 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { break; } + /* + * Parse any type of primitive that is not a string. + * + * true, false, null or any number primitive + */ + case LWJSON_STREAM_STATE_PARSING_PRIMITIVE: { + /* Any character except space, comma, or end of array/object are valid */ + if (!prv_is_space_char_ext(c) + && c != ',' && c != ']' && c != '}') { + jsp->data.prim.buff[jsp->data.prim.buff_pos++] = c; + } else { + lwjson_stream_type_t t = prv_stack_get_top(jsp); +#if defined(LWJSON_DEV) + if (t == LWJSON_STREAM_TYPE_OBJECT) { + /* TODO: Handle error - primitive cannot be just after object */ + } else if (t == LWJSON_STREAM_TYPE_KEY) { + LWJSON_DEBUG(jsp, "End of primitive parsing - string value associated to previous key in an object: \"%s\"\r\n", jsp->data.str.buff); + } else if (t == LWJSON_STREAM_TYPE_ARRAY) { + LWJSON_DEBUG(jsp, "End of primitive parsing - an array string entry: \"%s\"\r\n", jsp->data.str.buff); + } +#endif /* defined(LWJSON_DEV) */ + if (t == LWJSON_STREAM_TYPE_KEY) { + prv_stack_pop(jsp); + } + jsp->parse_state = LWJSON_STREAM_STATE_PARSING; + } + break; + } + /* TODO: Add other case statements */ default: break; diff --git a/test/json/custom_stream.json b/test/json/custom_stream.json index adb54ac..043477b 100644 --- a/test/json/custom_stream.json +++ b/test/json/custom_stream.json @@ -1,13 +1,42 @@ { - "test": "abc", - "array": [ - "123", - "def", - "ghi" - ], - "array_in_array": [ - ["1", "2", "3"], - ["4", "5", "6"], - ["7", "8", "9"] - ] -} \ No newline at end of file + "test": "abc", + "array": [ + "123", + "def", + "ghi" + ], + "array_in_array": [ + ["1", "2", "3"], + ["4", "5", "6"], + ["7", "8", "9"] + ] +} + +[ + { + "first_name": "Jeanette", + "last_name": "Penddreth", + "email": "jpenddreth0@census.gov", + "gender": "Female", + "ip_address": "26.58.193.2", + "obj": { + "abc": { + "def": { + "gdf": "this is final value" + } + } + } + }, { + "first_name": "Giavani", + "last_name": "Frediani", + "email": "gfrediani1@senate.gov", + "gender": "Male", + "ip_address": "229\\\".179.4.212", + "num": 123, + "num2": 123.43, + "num3": -123E13, + "true": true, + "false": false, + "null": null + } +] \ No newline at end of file From 038a781a8c17588fd2679904b428af9f5bc06b47 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sat, 16 Apr 2022 12:42:02 +0200 Subject: [PATCH 36/77] Add callback function and optimize how strings are treated --- dev/main.c | 9 ++- lwjson/src/include/lwjson/lwjson.h | 16 ++++- lwjson/src/lwjson/lwjson_stream.c | 101 ++++++++++++++++++++++++----- test/json/custom_stream.json | 5 +- 4 files changed, 109 insertions(+), 22 deletions(-) diff --git a/dev/main.c b/dev/main.c index 61f10f4..c5a4047 100644 --- a/dev/main.c +++ b/dev/main.c @@ -15,6 +15,8 @@ extern void test_run(void); extern void example_minimal_run(void); extern void example_traverse_run(void); +static void jsp_stream_callback(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type); + int main() { HANDLE f; @@ -61,7 +63,7 @@ main() { } /* Now parse as a stream */ - lwjson_stream_init(&stream_parser); + lwjson_stream_init(&stream_parser, jsp_stream_callback); for (const char* str = json_text; str != NULL && *str != '\0'; ++str) { lwjson_stream_parse(&stream_parser, *str); } @@ -92,3 +94,8 @@ main() { } return 0; } + +static void +jsp_stream_callback(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { + +} diff --git a/lwjson/src/include/lwjson/lwjson.h b/lwjson/src/include/lwjson/lwjson.h index 9ea5544..c708372 100644 --- a/lwjson/src/include/lwjson/lwjson.h +++ b/lwjson/src/include/lwjson/lwjson.h @@ -170,15 +170,26 @@ typedef enum { LWJSON_STREAM_STATE_PARSING_PRIMITIVE, /*!< Parse any primitive that is non-string, either "true", "false", "null" or a number */ } lwjson_stream_state_t; +/* Forward declaration */ +struct lwjson_stream_parser; + +/** + * \brief Callback function for various events + * + */ +typedef void (*lwjson_stream_parser_callback_fn)(struct lwjson_stream_parser* jsp, lwjson_stream_type_t type); + /** * \brief LwJSON streaming structure */ -typedef struct { +typedef struct lwjson_stream_parser { lwjson_stream_stack_t stack[16]; /*!< Stack used for parsing. TODO: Add conditional compilation flag */ size_t stack_pos; /*!< Current stack position */ lwjson_stream_state_t parse_state; /*!< Parser state */ + lwjson_stream_parser_callback_fn evt_fn; /*!< Event function for user */ + /* State */ union { struct { @@ -191,9 +202,10 @@ typedef struct { } prim; /*!< Primitive object */ /* Todo: Add other types */ } data; /*!< Data union used to parse various */ + char prev_c; /*!< History of characters */ } lwjson_stream_parser_t; -lwjsonr_t lwjson_stream_init(lwjson_stream_parser_t* jsp); +lwjsonr_t lwjson_stream_init(lwjson_stream_parser_t* jsp, lwjson_stream_parser_callback_fn evt_fn); lwjsonr_t lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c); /** diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index 831de48..df36be6 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -63,6 +63,21 @@ static const char* type_strings[] = { #define LWJSON_DEBUG(jsp, ...) #endif /* defined(LWJSON_DEV) */ +#define SEND_EVT(jsp, type) if ((jsp) != NULL && (jsp)->evt_fn != NULL) { (jsp)->evt_fn((jsp), (type)); } + +/** + * \brief Check if character is a space character (with extended chars) + * \param[in] c: Character to check + * \return `1` if considered extended space, `0` otherwise + */ +#define prv_is_space_char_ext(c) ((c) == ' ' || (c) == '\t' || (c) == '\r' || (c) == '\n' || (c) == '\f') + +/** + * \brief Push "parent" state to the artificial stack + * \param jsp: JSON stream parser instance + * \param type: Stream type to be pushed on stack + * \return `1` on success, `0` otherwise + */ uint8_t prv_stack_push(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { if (jsp->stack_pos < LWJSON_ARRAYSIZE(jsp->stack)) { @@ -75,6 +90,11 @@ prv_stack_push(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { return 0; } +/** + * \brief Pop value from stack (remove it) and return its value + * \param jsp: JSON stream parser instance + * \return Member of \ref lwjson_stream_type_t enumeration + */ lwjson_stream_type_t prv_stack_pop(lwjson_stream_parser_t* jsp) { if (jsp->stack_pos > 0) { @@ -85,6 +105,11 @@ prv_stack_pop(lwjson_stream_parser_t* jsp) { return LWJSON_STREAM_TYPE_NONE; } +/** + * \brief Get top type value currently on the stack + * \param jsp: JSON stream parser instance + * \return Member of \ref lwjson_stream_type_t enumeration + */ lwjson_stream_type_t prv_stack_get_top(lwjson_stream_parser_t* jsp) { if (jsp->stack_pos > 0) { @@ -93,16 +118,16 @@ prv_stack_get_top(lwjson_stream_parser_t* jsp) { return LWJSON_STREAM_TYPE_NONE; } -#define prv_is_space_char_ext(c) ((c) == ' ' || (c) == '\t' || (c) == '\r' || (c) == '\n' || (c) == '\f') - /** * \brief Initialize LwJSON stream object before parsing takes place * \param[in,out] jsp: Stream JSON structure * \return \ref lwjsonOK on success, member of \ref lwjsonr_t otherwise */ lwjsonr_t -lwjson_stream_init(lwjson_stream_parser_t* jsp) { +lwjson_stream_init(lwjson_stream_parser_t* jsp, lwjson_stream_parser_callback_fn evt_fn) { + memset(jsp, 0x00, sizeof(*jsp)); jsp->parse_state = LWJSON_STREAM_STATE_WAITINGFIRSTCHAR; + jsp->evt_fn = evt_fn; return lwjsonOK; } @@ -137,15 +162,36 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { return lwjsonERRMEM; } jsp->parse_state = LWJSON_STREAM_STATE_PARSING; + SEND_EVT(jsp, c == '{' ? LWJSON_STREAM_TYPE_OBJECT : LWJSON_STREAM_TYPE_ARRAY); /* Determine end or object or an array */ } else if (c == '}' || c == ']') { + lwjson_stream_type_t t = prv_stack_get_top(jsp); + + /* + * If it is a key last entry on closing area, + * it is an error - an example: {"key":} + */ + if (t == LWJSON_STREAM_TYPE_KEY) { + LWJSON_DEBUG(jsp, "ERROR - key should not be followed by ] without value for a key\r\n"); + return lwjsonERRJSON; + } + + /* Now remove the array or object from stack */ if (!prv_stack_pop(jsp)) { - return lwjsonERR; + return lwjsonERRMEM; } + + /* + * Check if above is a key type + * and remove it too as we finished with processing of potential case. + * + * {"key":{"abc":1}} - remove "key" part + */ if (prv_stack_get_top(jsp) == LWJSON_STREAM_TYPE_KEY) { prv_stack_pop(jsp); } + SEND_EVT(jsp, c == '}' ? LWJSON_STREAM_TYPE_OBJECT_END : LWJSON_STREAM_TYPE_ARRAY_END); /* Determine start of string - can be key or regular string (in array or after key) */ } else if (c == '"') { @@ -159,7 +205,6 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { LWJSON_DEBUG(jsp, "Start of string parsing - string entry in an array\r\n"); } #endif /* defined(LWJSON_DEV) */ - jsp->parse_state = LWJSON_STREAM_STATE_PARSING_STRING; memset(&jsp->data.str, 0x00, sizeof(jsp->data.str)); @@ -168,17 +213,13 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { lwjson_stream_type_t t = prv_stack_get_top(jsp); /* - * Color character means end of key. - * - * Valid key can only happen if its parent (top of current stack) - * is an object - otherwise trigger and error + * Color can only be followed by key on the stack + * + * It is clear JSON error if this is not the case */ - if (t == LWJSON_STREAM_TYPE_OBJECT) { - if (!prv_stack_push(jsp, LWJSON_STREAM_TYPE_KEY)) { - return lwjsonERRMEM; - } - } else { - LWJSON_DEBUG(jsp, "Error - wrong ':' character\r\n"); + if (t != LWJSON_STREAM_TYPE_KEY) { + LWJSON_DEBUG(jsp, "Error - wrong ':' character\r\n"); + return lwjsonERRJSON; } /* Check if this is start of number or "true", "false" or "null" */ } else if (c == '-' || (c >= '0' && c <= '9') @@ -204,8 +245,11 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { * * TODO: Handle backslash */ - if (c == '"') { + if (c == '"' && jsp->prev_c != '\\') { lwjson_stream_type_t t = prv_stack_get_top(jsp); + + /* TODO: Send callback to user */ + #if defined(LWJSON_DEV) if (t == LWJSON_STREAM_TYPE_OBJECT) { LWJSON_DEBUG(jsp, "End of string parsing - object key name: \"%s\"\r\n", jsp->data.str.buff); @@ -215,13 +259,28 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { LWJSON_DEBUG(jsp, "End of string parsing - an array string entry: \"%s\"\r\n", jsp->data.str.buff); } #endif /* defined(LWJSON_DEV) */ - if (t == LWJSON_STREAM_TYPE_KEY) { + + /* + * When top of stack is object - string is treated as a key + * When top of stack is a key - string is a value for a key - notify user and pop the value for key + * When top of stack is an array - string is one type - notify user and don't do anything + */ + if (t == LWJSON_STREAM_TYPE_OBJECT) { + SEND_EVT(jsp, LWJSON_STREAM_TYPE_KEY); + if (!prv_stack_push(jsp, LWJSON_STREAM_TYPE_KEY)) { + return lwjsonERRMEM; + } + } else if (t == LWJSON_STREAM_TYPE_KEY) { + SEND_EVT(jsp, LWJSON_STREAM_TYPE_STRING); prv_stack_pop(jsp); + /* Next character to wait for is either space or comma or end of object */ + } else if (t == LWJSON_STREAM_TYPE_ARRAY) { + SEND_EVT(jsp, LWJSON_STREAM_TYPE_STRING); } jsp->parse_state = LWJSON_STREAM_STATE_PARSING; } else { jsp->data.str.buff[jsp->data.str.buff_pos++] = c; - /* TODO: Add 0 if necessary */ + /* TODO: If buffer lower than needed for user - send multiple events */ } break; } @@ -238,6 +297,11 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { jsp->data.prim.buff[jsp->data.prim.buff_pos++] = c; } else { lwjson_stream_type_t t = prv_stack_get_top(jsp); + + /* TODO: Send callback to user */ + /* Find first type of primitive - then send */ + //SEND_EVT(jsp, LWJSON_STREAM_TYPE_); + #if defined(LWJSON_DEV) if (t == LWJSON_STREAM_TYPE_OBJECT) { /* TODO: Handle error - primitive cannot be just after object */ @@ -259,4 +323,5 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { default: break; } + jsp->prev_c = c; /* Save current c as previous for next round */ } diff --git a/test/json/custom_stream.json b/test/json/custom_stream.json index 043477b..002a250 100644 --- a/test/json/custom_stream.json +++ b/test/json/custom_stream.json @@ -37,6 +37,9 @@ "num3": -123E13, "true": true, "false": false, - "null": null + "null": null, + "key": "test", + "lala": 123, + "blabla": -123 } ] \ No newline at end of file From 04ebb7c930db7bd0f47ee7ff1066a1f8d6b0fc5c Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sun, 17 Apr 2022 03:20:46 +0200 Subject: [PATCH 37/77] Add demo callback function to extract data --- dev/main.c | 49 +++++++++++++++++++++- lwjson/src/include/lwjson/lwjson.h | 4 +- lwjson/src/lwjson/lwjson_stream.c | 67 +++++++++++++++++++++++------- 3 files changed, 103 insertions(+), 17 deletions(-) diff --git a/dev/main.c b/dev/main.c index c5a4047..fcb4b4e 100644 --- a/dev/main.c +++ b/dev/main.c @@ -65,7 +65,9 @@ main() { /* Now parse as a stream */ lwjson_stream_init(&stream_parser, jsp_stream_callback); for (const char* str = json_text; str != NULL && *str != '\0'; ++str) { - lwjson_stream_parse(&stream_parser, *str); + if (lwjson_stream_parse(&stream_parser, *str) != lwjsonOK) { + break; + } } return 0; @@ -97,5 +99,50 @@ main() { static void jsp_stream_callback(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { + /* Get current weather icon */ + if (type == LWJSON_STREAM_TYPE_STRING /* Make sure current type is a string */ + && jsp->stack_pos >= 6 + /* Its previously parsed key has to be icon */ + && (jsp->stack[jsp->stack_pos - 1].type == LWJSON_STREAM_TYPE_KEY && strcmp(jsp->stack[jsp->stack_pos - 1].name, "icon") == 0) + && (jsp->stack[jsp->stack_pos - 2].type == LWJSON_STREAM_TYPE_OBJECT) + && (jsp->stack[jsp->stack_pos - 3].type == LWJSON_STREAM_TYPE_ARRAY) + && (jsp->stack[jsp->stack_pos - 4].type == LWJSON_STREAM_TYPE_KEY && strcmp(jsp->stack[jsp->stack_pos - 4].name, "weather") == 0) + && (jsp->stack[jsp->stack_pos - 5].type == LWJSON_STREAM_TYPE_OBJECT) + && (jsp->stack[jsp->stack_pos - 6].type == LWJSON_STREAM_TYPE_KEY && strcmp(jsp->stack[jsp->stack_pos - 6].name, "current") == 0)) { + + printf("GOT ICON string for current weather: %s\r\n", jsp->data.str.buff); + } + + /* Get hourly temperature */ + if (type == LWJSON_STREAM_TYPE_NUMBER + && jsp->stack_pos >= 4 + + /* Get chain of previously parsed entries */ + && (jsp->stack[jsp->stack_pos - 1].type == LWJSON_STREAM_TYPE_KEY && strcmp(jsp->stack[jsp->stack_pos - 1].name, "temp") == 0) + && (jsp->stack[jsp->stack_pos - 2].type == LWJSON_STREAM_TYPE_OBJECT) + && (jsp->stack[jsp->stack_pos - 3].type == LWJSON_STREAM_TYPE_ARRAY) + && (jsp->stack[jsp->stack_pos - 4].type == LWJSON_STREAM_TYPE_KEY && strcmp(jsp->stack[jsp->stack_pos - 4].name, "hourly") == 0)) { + + printf("Hour %d forecast for temperature: %s\r\n", (int)jsp->stack[jsp->stack_pos - 3].index, jsp->data.str.buff); + } + + /* Get daily temperatures */ + if (type == LWJSON_STREAM_TYPE_NUMBER + && jsp->stack_pos >= 6 + + /* Get chain of previously parsed entries */ + && (jsp->stack[jsp->stack_pos - 1].type == LWJSON_STREAM_TYPE_KEY) + && (jsp->stack[jsp->stack_pos - 2].type == LWJSON_STREAM_TYPE_OBJECT) + && (jsp->stack[jsp->stack_pos - 3].type == LWJSON_STREAM_TYPE_KEY && strcmp(jsp->stack[jsp->stack_pos - 3].name, "temp") == 0) + && (jsp->stack[jsp->stack_pos - 4].type == LWJSON_STREAM_TYPE_OBJECT) + && (jsp->stack[jsp->stack_pos - 5].type == LWJSON_STREAM_TYPE_ARRAY) + && (jsp->stack[jsp->stack_pos - 6].type == LWJSON_STREAM_TYPE_KEY && strcmp(jsp->stack[jsp->stack_pos - 6].name, "daily") == 0)) { + + if (strcmp(jsp->stack[jsp->stack_pos - 1].name, "min") == 0) { + printf("Day %d temp minimum: %s\r\n", (int)jsp->stack[jsp->stack_pos - 5].index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[jsp->stack_pos - 1].name, "max") == 0) { + printf("Day %d temp maximum: %s\r\n", (int)jsp->stack[jsp->stack_pos - 5].index, jsp->data.str.buff); + } + } } diff --git a/lwjson/src/include/lwjson/lwjson.h b/lwjson/src/include/lwjson/lwjson.h index c708372..6883d7a 100644 --- a/lwjson/src/include/lwjson/lwjson.h +++ b/lwjson/src/include/lwjson/lwjson.h @@ -150,8 +150,7 @@ typedef enum { LWJSON_STREAM_TYPE_TRUE, LWJSON_STREAM_TYPE_FALSE, LWJSON_STREAM_TYPE_NULL, - LWJSON_STREAM_TYPE_INT, - LWJSON_STREAM_TYPE_REAL, + LWJSON_STREAM_TYPE_NUMBER, } lwjson_stream_type_t; /** @@ -161,6 +160,7 @@ typedef struct { lwjson_stream_type_t type; /*!< Streaming type - current value */ char name[32]; /*!< Last known dictionary name. Not used for array types TODO: Add conditional compilation to decrease memory size even more */ + uint16_t index; /*!< Current index when type is an array */ } lwjson_stream_stack_t; typedef enum { diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index df36be6..6aed916 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -34,7 +34,7 @@ #include #include "lwjson/lwjson.h" -#if defined(LWJSON_DEV) +#if defined(LWJSON_DEVV) #include #define DEBUG_STRING_PREFIX_SPACES " " #define LWJSON_DEBUG(jsp, ...) do { \ @@ -56,8 +56,7 @@ static const char* type_strings[] = { [LWJSON_STREAM_TYPE_TRUE] = "true", [LWJSON_STREAM_TYPE_FALSE] = "false", [LWJSON_STREAM_TYPE_NULL] = "null", - [LWJSON_STREAM_TYPE_INT] = "int", - [LWJSON_STREAM_TYPE_REAL] = "real", + [LWJSON_STREAM_TYPE_NUMBER] = "number", }; #else #define LWJSON_DEBUG(jsp, ...) @@ -82,9 +81,9 @@ uint8_t prv_stack_push(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { if (jsp->stack_pos < LWJSON_ARRAYSIZE(jsp->stack)) { jsp->stack[jsp->stack_pos].type = type; - LWJSON_DEBUG(jsp, "Pushed to stack: %s\r\n", type_strings[type]); + jsp->stack[jsp->stack_pos].index = 0; jsp->stack_pos++; - /* TODO: Copy name in case of non-array object */ + LWJSON_DEBUG(jsp, "Pushed to stack: %s\r\n", type_strings[type]); return 1; } return 0; @@ -98,9 +97,12 @@ prv_stack_push(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { lwjson_stream_type_t prv_stack_pop(lwjson_stream_parser_t* jsp) { if (jsp->stack_pos > 0) { - jsp->stack_pos--; - LWJSON_DEBUG(jsp, "Popped from stack: %s\r\n", type_strings[jsp->stack[jsp->stack_pos].type]); - return jsp->stack[jsp->stack_pos].type; + lwjson_stream_type_t t = jsp->stack[--jsp->stack_pos].type; + LWJSON_DEBUG(jsp, "Popped from stack: %s\r\n", type_strings[t]); + if (jsp->stack_pos > 0 && jsp->stack[jsp->stack_pos - 1].type == LWJSON_STREAM_TYPE_ARRAY) { + jsp->stack[jsp->stack_pos - 1].index++; + } + return t; } return LWJSON_STREAM_TYPE_NONE; } @@ -145,6 +147,7 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { return lwjsonOK; } +start_over: /* * Determine what to do from parsing state */ @@ -159,6 +162,7 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { /* Determine start or object or an array */ if (c == '{' || c == '[') { if (!prv_stack_push(jsp, c == '{' ? LWJSON_STREAM_TYPE_OBJECT : LWJSON_STREAM_TYPE_ARRAY)) { + LWJSON_DEBUG(jsp, "Cannot push object/array to stack\r\n"); return lwjsonERRMEM; } jsp->parse_state = LWJSON_STREAM_STATE_PARSING; @@ -224,7 +228,7 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { /* Check if this is start of number or "true", "false" or "null" */ } else if (c == '-' || (c >= '0' && c <= '9') || c == 't' || c == 'f' || c == 'n') { - LWJSON_DEBUG(jsp, "Start of primitive parsing parsing - %s, %c\r\n", + LWJSON_DEBUG(jsp, "Start of primitive parsing parsing - %s, First char: %c\r\n", (c == '-' || (c >= '0' && c <= '9')) ? "number" : "true,false,null", c); jsp->parse_state = LWJSON_STREAM_STATE_PARSING_PRIMITIVE; memset(&jsp->data.prim, 0x00, sizeof(jsp->data.prim)); @@ -267,9 +271,18 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { */ if (t == LWJSON_STREAM_TYPE_OBJECT) { SEND_EVT(jsp, LWJSON_STREAM_TYPE_KEY); - if (!prv_stack_push(jsp, LWJSON_STREAM_TYPE_KEY)) { + if (prv_stack_push(jsp, LWJSON_STREAM_TYPE_KEY)) { + size_t len = jsp->data.str.buff_pos; + if (len > (sizeof(jsp->stack->name) - 1)) { + len = sizeof(jsp->stack->name) - 1; + } + memcpy(jsp->stack[jsp->stack_pos - 1].name, jsp->data.str.buff, len); + jsp->stack[jsp->stack_pos - 1].name[len] = '\0'; + } else { + LWJSON_DEBUG(jsp, "Cannot push key to stack\r\n"); return lwjsonERRMEM; } + } else if (t == LWJSON_STREAM_TYPE_KEY) { SEND_EVT(jsp, LWJSON_STREAM_TYPE_STRING); prv_stack_pop(jsp); @@ -279,6 +292,8 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { } jsp->parse_state = LWJSON_STREAM_STATE_PARSING; } else { + /* TODO: Check other backslash elements */ + jsp->data.str.buff[jsp->data.str.buff_pos++] = c; /* TODO: If buffer lower than needed for user - send multiple events */ } @@ -298,10 +313,6 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { } else { lwjson_stream_type_t t = prv_stack_get_top(jsp); - /* TODO: Send callback to user */ - /* Find first type of primitive - then send */ - //SEND_EVT(jsp, LWJSON_STREAM_TYPE_); - #if defined(LWJSON_DEV) if (t == LWJSON_STREAM_TYPE_OBJECT) { /* TODO: Handle error - primitive cannot be just after object */ @@ -311,10 +322,37 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { LWJSON_DEBUG(jsp, "End of primitive parsing - an array string entry: \"%s\"\r\n", jsp->data.str.buff); } #endif /* defined(LWJSON_DEV) */ + + /* + * This is the end of primitive parsing + * + * It is assumed that buffer for primitive can handle at least + * true, false, null or all number characters (that being real or int number) + */ + if (jsp->data.prim.buff_pos == 4 && strncmp(jsp->data.prim.buff, "true", 4) == 0) { + LWJSON_DEBUG(jsp, "Primitive parsed as %s\r\n", "true"); + SEND_EVT(jsp, LWJSON_STREAM_TYPE_TRUE); + } else if (jsp->data.prim.buff_pos == 4 && strncmp(jsp->data.prim.buff, "null", 4) == 0) { + LWJSON_DEBUG(jsp, "Primitive parsed as %s\r\n", "null"); + SEND_EVT(jsp, LWJSON_STREAM_TYPE_NULL); + } else if (jsp->data.prim.buff_pos == 5 && strncmp(jsp->data.prim.buff, "false", 5) == 0) { + LWJSON_DEBUG(jsp, "Primitive parsed as %s\r\n", "false"); + SEND_EVT(jsp, LWJSON_STREAM_TYPE_FALSE); + } else { + LWJSON_DEBUG(jsp, "Primitive parsed - can only be a number\r\n"); + SEND_EVT(jsp, LWJSON_STREAM_TYPE_NUMBER); + } if (t == LWJSON_STREAM_TYPE_KEY) { prv_stack_pop(jsp); } + + /* + * Received character is not part of the primitive and must be processed again + * + * Set state to default state and start from beginning + */ jsp->parse_state = LWJSON_STREAM_STATE_PARSING; + goto start_over; } break; } @@ -324,4 +362,5 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { break; } jsp->prev_c = c; /* Save current c as previous for next round */ + return lwjsonOK; } From fa13bcd5ee3b773f399ef8801bd5877a4ff76201 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sun, 17 Apr 2022 03:21:54 +0200 Subject: [PATCH 38/77] Add file reference to onecall test --- dev/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/main.c b/dev/main.c index fcb4b4e..42eef21 100644 --- a/dev/main.c +++ b/dev/main.c @@ -34,7 +34,7 @@ main() { /* Init JSON */ lwjson_init(&lwjson, tokens, LWJSON_ARRAYSIZE(tokens)); - f = CreateFile(TEXT("test\\json\\custom_stream.json"), + f = CreateFile(TEXT("test\\json\\weather_onecall.json"), GENERIC_READ, // open for reading 0, // do not share NULL, // no security From f16820a61b11f3d4bb00e3f5f4dbe268964d77d1 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sun, 17 Apr 2022 11:05:06 +0200 Subject: [PATCH 39/77] Remove entry on stack once pop-ed --- lwjson/src/lwjson/lwjson_stream.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index 6aed916..24b18a1 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -98,7 +98,10 @@ lwjson_stream_type_t prv_stack_pop(lwjson_stream_parser_t* jsp) { if (jsp->stack_pos > 0) { lwjson_stream_type_t t = jsp->stack[--jsp->stack_pos].type; + jsp->stack[jsp->stack_pos].type = LWJSON_STREAM_TYPE_NONE; LWJSON_DEBUG(jsp, "Popped from stack: %s\r\n", type_strings[t]); + + /* Take care of array to indicate number of entries */ if (jsp->stack_pos > 0 && jsp->stack[jsp->stack_pos - 1].type == LWJSON_STREAM_TYPE_ARRAY) { jsp->stack[jsp->stack_pos - 1].index++; } From 51fdd315b05a3ce078fea8abba69d44bf6c7e349 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sun, 17 Apr 2022 11:05:31 +0200 Subject: [PATCH 40/77] Improve demo code for oneweatherapi --- dev/main.c | 105 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 80 insertions(+), 25 deletions(-) diff --git a/dev/main.c b/dev/main.c index 42eef21..9cb39cc 100644 --- a/dev/main.c +++ b/dev/main.c @@ -114,35 +114,90 @@ jsp_stream_callback(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { printf("GOT ICON string for current weather: %s\r\n", jsp->data.str.buff); } - /* Get hourly temperature */ - if (type == LWJSON_STREAM_TYPE_NUMBER - && jsp->stack_pos >= 4 - - /* Get chain of previously parsed entries */ - && (jsp->stack[jsp->stack_pos - 1].type == LWJSON_STREAM_TYPE_KEY && strcmp(jsp->stack[jsp->stack_pos - 1].name, "temp") == 0) - && (jsp->stack[jsp->stack_pos - 2].type == LWJSON_STREAM_TYPE_OBJECT) - && (jsp->stack[jsp->stack_pos - 3].type == LWJSON_STREAM_TYPE_ARRAY) - && (jsp->stack[jsp->stack_pos - 4].type == LWJSON_STREAM_TYPE_KEY && strcmp(jsp->stack[jsp->stack_pos - 4].name, "hourly") == 0)) { - - printf("Hour %d forecast for temperature: %s\r\n", (int)jsp->stack[jsp->stack_pos - 3].index, jsp->data.str.buff); + /* + * Chcek for hourly forecast + * + * Define the stack sequence which gives us to this point + */ + if (jsp->stack_pos >= 5 + /* First build the order... */ + && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT + && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY + && jsp->stack[2].type == LWJSON_STREAM_TYPE_ARRAY + && jsp->stack[3].type == LWJSON_STREAM_TYPE_OBJECT + && jsp->stack[4].type == LWJSON_STREAM_TYPE_KEY + + /* .. then analyze the strings */ + && strcmp(jsp->stack[1].name, "hourly") == 0) { + + if (strcmp(jsp->stack[4].name, "dt") == 0) { + printf("Hour %d forecast for dt: %u\r\n", (int)jsp->stack[jsp->stack_pos - 3].index, (unsigned)atoll(jsp->data.str.buff)); + } else if (strcmp(jsp->stack[4].name, "temp") == 0) { + printf("Hour %d forecast for temp: %f\r\n", (int)jsp->stack[jsp->stack_pos - 3].index, strtod(jsp->data.str.buff, NULL)); + } else if (strcmp(jsp->stack[4].name, "feels_like") == 0) { + printf("Hour %d forecast for feels_like: %f\r\n", (int)jsp->stack[jsp->stack_pos - 3].index, strtod(jsp->data.str.buff, NULL)); + } else if (strcmp(jsp->stack[4].name, "pressure") == 0) { + printf("Hour %d forecast for pressure: %d\r\n", (int)jsp->stack[jsp->stack_pos - 3].index, (int)atoll(jsp->data.str.buff)); + } else if (strcmp(jsp->stack[4].name, "humidity") == 0) { + printf("Hour %d forecast for humidity: %d\r\n", (int)jsp->stack[jsp->stack_pos - 3].index, (int)atoll(jsp->data.str.buff)); + } } /* Get daily temperatures */ - if (type == LWJSON_STREAM_TYPE_NUMBER - && jsp->stack_pos >= 6 - - /* Get chain of previously parsed entries */ - && (jsp->stack[jsp->stack_pos - 1].type == LWJSON_STREAM_TYPE_KEY) - && (jsp->stack[jsp->stack_pos - 2].type == LWJSON_STREAM_TYPE_OBJECT) - && (jsp->stack[jsp->stack_pos - 3].type == LWJSON_STREAM_TYPE_KEY && strcmp(jsp->stack[jsp->stack_pos - 3].name, "temp") == 0) - && (jsp->stack[jsp->stack_pos - 4].type == LWJSON_STREAM_TYPE_OBJECT) - && (jsp->stack[jsp->stack_pos - 5].type == LWJSON_STREAM_TYPE_ARRAY) - && (jsp->stack[jsp->stack_pos - 6].type == LWJSON_STREAM_TYPE_KEY && strcmp(jsp->stack[jsp->stack_pos - 6].name, "daily") == 0)) { + if (jsp->stack_pos >= 5 + /* First build the order... */ + && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT + && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY + && jsp->stack[2].type == LWJSON_STREAM_TYPE_ARRAY + && jsp->stack[3].type == LWJSON_STREAM_TYPE_OBJECT + && jsp->stack[4].type == LWJSON_STREAM_TYPE_KEY + + /* .. then analyze the strings */ + && strcmp(jsp->stack[1].name, "daily") == 0) { - if (strcmp(jsp->stack[jsp->stack_pos - 1].name, "min") == 0) { - printf("Day %d temp minimum: %s\r\n", (int)jsp->stack[jsp->stack_pos - 5].index, jsp->data.str.buff); - } else if (strcmp(jsp->stack[jsp->stack_pos - 1].name, "max") == 0) { - printf("Day %d temp maximum: %s\r\n", (int)jsp->stack[jsp->stack_pos - 5].index, jsp->data.str.buff); + /* Analyze objects for temp and feels like object */ + if (jsp->stack_pos >= 7 + && jsp->stack[5].type == LWJSON_STREAM_TYPE_OBJECT + && jsp->stack[6].type == LWJSON_STREAM_TYPE_KEY) { + if (strcmp(jsp->stack[4].name, "temp") == 0) { + /* Parsing of temp object */ + if (strcmp(jsp->stack[6].name, "min") == 0) { + printf("Day %d temp min: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[6].name, "max") == 0) { + printf("Day %d temp max: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[6].name, "day") == 0) { + printf("Day %d temp day: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[6].name, "night") == 0) { + printf("Day %d temp night: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[6].name, "eve") == 0) { + printf("Day %d temp eve: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[6].name, "morn") == 0) { + printf("Day %d temp morn: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); + } + } else if (strcmp(jsp->stack[4].name, "feels_like") == 0) { + /* Parsing of feels-like objects */ + if (strcmp(jsp->stack[6].name, "day") == 0) { + printf("Day %d temp_feels_like day: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[6].name, "night") == 0) { + printf("Day %d temp_feels_like night: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[6].name, "eve") == 0) { + printf("Day %d temp_feels_like eve: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[6].name, "morn") == 0) { + printf("Day %d temp_feels_like morn: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); + } + } + } else if (strcmp(jsp->stack[4].name, "dt") == 0) { + printf("Day %d dt: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[4].name, "pressure") == 0) { + printf("Day %d pressure: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[4].name, "humidity") == 0) { + printf("Day %d humidity: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[4].name, "clouds") == 0) { + printf("Day %d clouds: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[4].name, "snow") == 0) { + printf("Day %d snow: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[4].name, "uvi") == 0) { + printf("Day %d uvi: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); } } } From 4f17a7f607dc21ac18acb9a82e5569402af23768 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sun, 17 Apr 2022 13:53:35 +0200 Subject: [PATCH 41/77] Increase array index for non-key values --- lwjson/src/lwjson/lwjson_stream.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index 24b18a1..32ddb49 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -292,6 +292,7 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { /* Next character to wait for is either space or comma or end of object */ } else if (t == LWJSON_STREAM_TYPE_ARRAY) { SEND_EVT(jsp, LWJSON_STREAM_TYPE_STRING); + jsp->stack[jsp->stack_pos - 1].index++; } jsp->parse_state = LWJSON_STREAM_STATE_PARSING; } else { @@ -347,6 +348,8 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { } if (t == LWJSON_STREAM_TYPE_KEY) { prv_stack_pop(jsp); + } else if (t == LWJSON_STREAM_TYPE_ARRAY) { + jsp->stack[jsp->stack_pos - 1].index++; } /* From 594e1962db51a6a75a28d50f7e4cdea7d6176b40 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sun, 17 Apr 2022 13:54:23 +0200 Subject: [PATCH 42/77] Increase code example for openweather API one-call --- dev/main.c | 232 +++++++++++++++++++++------------ test/json/custom_stream.json | 34 +---- test/json/weather_onecall.json | 2 +- 3 files changed, 154 insertions(+), 114 deletions(-) diff --git a/dev/main.c b/dev/main.c index 9cb39cc..9cee551 100644 --- a/dev/main.c +++ b/dev/main.c @@ -99,105 +99,177 @@ main() { static void jsp_stream_callback(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { - /* Get current weather icon */ - if (type == LWJSON_STREAM_TYPE_STRING /* Make sure current type is a string */ - && jsp->stack_pos >= 6 - - /* Its previously parsed key has to be icon */ - && (jsp->stack[jsp->stack_pos - 1].type == LWJSON_STREAM_TYPE_KEY && strcmp(jsp->stack[jsp->stack_pos - 1].name, "icon") == 0) - && (jsp->stack[jsp->stack_pos - 2].type == LWJSON_STREAM_TYPE_OBJECT) - && (jsp->stack[jsp->stack_pos - 3].type == LWJSON_STREAM_TYPE_ARRAY) - && (jsp->stack[jsp->stack_pos - 4].type == LWJSON_STREAM_TYPE_KEY && strcmp(jsp->stack[jsp->stack_pos - 4].name, "weather") == 0) - && (jsp->stack[jsp->stack_pos - 5].type == LWJSON_STREAM_TYPE_OBJECT) - && (jsp->stack[jsp->stack_pos - 6].type == LWJSON_STREAM_TYPE_KEY && strcmp(jsp->stack[jsp->stack_pos - 6].name, "current") == 0)) { - - printf("GOT ICON string for current weather: %s\r\n", jsp->data.str.buff); +#if 0 + if (jsp->stack_pos >= 4 + && (type == LWJSON_STREAM_TYPE_STRING || type == LWJSON_STREAM_TYPE_NUMBER) + && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT + && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY + && jsp->stack[2].type == LWJSON_STREAM_TYPE_ARRAY + && jsp->stack[3].type == LWJSON_STREAM_TYPE_ARRAY + && strcmp(jsp->stack[1].name, "array_in_array") == 0) { + printf("Index: %u, Index: %u, value: %s\r\n", + (int)jsp->stack[2].index, (int)jsp->stack[3].index, + jsp->data.str.buff + ); } + return; +#endif - /* - * Chcek for hourly forecast + /* + * Take care of key-value pairs immediately at the start of object * - * Define the stack sequence which gives us to this point + * Values such as latitude and longitude are parsed here */ - if (jsp->stack_pos >= 5 - /* First build the order... */ + if (jsp->stack_pos == 2 + && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT + && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY) { + + if (strcmp(jsp->stack[1].name, "lat") == 0) { + printf("Latitude weather: %f\r\n", strtod(jsp->data.str.buff, NULL)); + } else if (strcmp(jsp->stack[1].name, "lon") == 0) { + printf("Longitude weather: %f\r\n", strtod(jsp->data.str.buff, NULL)); + } else if (strcmp(jsp->stack[1].name, "timezone_offset") == 0) { + printf("Timezone offset: %u\r\n", (unsigned)atoll(jsp->data.str.buff)); + } else if (strcmp(jsp->stack[1].name, "timezone") == 0) { + printf("Timezone: %s\r\n", jsp->data.str.buff); + } + } + + /* + * Handle current object - single object with multiple key-value pairs + */ + if (jsp->stack_pos >= 4 && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY - && jsp->stack[2].type == LWJSON_STREAM_TYPE_ARRAY - && jsp->stack[3].type == LWJSON_STREAM_TYPE_OBJECT - && jsp->stack[4].type == LWJSON_STREAM_TYPE_KEY - - /* .. then analyze the strings */ - && strcmp(jsp->stack[1].name, "hourly") == 0) { - - if (strcmp(jsp->stack[4].name, "dt") == 0) { - printf("Hour %d forecast for dt: %u\r\n", (int)jsp->stack[jsp->stack_pos - 3].index, (unsigned)atoll(jsp->data.str.buff)); - } else if (strcmp(jsp->stack[4].name, "temp") == 0) { - printf("Hour %d forecast for temp: %f\r\n", (int)jsp->stack[jsp->stack_pos - 3].index, strtod(jsp->data.str.buff, NULL)); - } else if (strcmp(jsp->stack[4].name, "feels_like") == 0) { - printf("Hour %d forecast for feels_like: %f\r\n", (int)jsp->stack[jsp->stack_pos - 3].index, strtod(jsp->data.str.buff, NULL)); - } else if (strcmp(jsp->stack[4].name, "pressure") == 0) { - printf("Hour %d forecast for pressure: %d\r\n", (int)jsp->stack[jsp->stack_pos - 3].index, (int)atoll(jsp->data.str.buff)); - } else if (strcmp(jsp->stack[4].name, "humidity") == 0) { - printf("Hour %d forecast for humidity: %d\r\n", (int)jsp->stack[jsp->stack_pos - 3].index, (int)atoll(jsp->data.str.buff)); + && jsp->stack[2].type == LWJSON_STREAM_TYPE_OBJECT + && jsp->stack[3].type == LWJSON_STREAM_TYPE_KEY) { + /* Check for current weather */ + if (strcmp(jsp->stack[1].name, "current") == 0) { + /* + * Process the "weather part" + */ + if (jsp->stack_pos >= 7 + && jsp->stack[4].type == LWJSON_STREAM_TYPE_ARRAY + && jsp->stack[5].type == LWJSON_STREAM_TYPE_OBJECT + && jsp->stack[6].type == LWJSON_STREAM_TYPE_KEY + && strcmp(jsp->stack[3].name, "weather") == 0) { + + if (strcmp(jsp->stack[6].name, "id") == 0) { + printf("Current weather %d id: %u\r\n", (int)jsp->stack[4].index, (unsigned)atoll(jsp->data.str.buff)); + } else if (strcmp(jsp->stack[6].name, "main") == 0) { + printf("Current weather %d main: %s\r\n", (int)jsp->stack[4].index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[6].name, "description") == 0) { + printf("Current weather %d description: %s\r\n", (int)jsp->stack[4].index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[6].name, "icon") == 0) { + printf("Current weather %d icon: %s\r\n", (int)jsp->stack[4].index, jsp->data.str.buff); + } + } else if (strcmp(jsp->stack[3].name, "dt") == 0) { + printf("Current weather dt: %u\r\n", (unsigned)atoll(jsp->data.str.buff)); + } else if (strcmp(jsp->stack[3].name, "sunrise") == 0) { + printf("Current weather sunrise: %u\r\n", (unsigned)atoll(jsp->data.str.buff)); + } else if (strcmp(jsp->stack[3].name, "sunset") == 0) { + printf("Current weather sunset: %u\r\n", (unsigned)atoll(jsp->data.str.buff)); + } else if (strcmp(jsp->stack[3].name, "temp") == 0) { + printf("Current weather temp: %f\r\n", strtod(jsp->data.str.buff, NULL)); + } else if (strcmp(jsp->stack[3].name, "feels_like") == 0) { + printf("Current weather feels_like: %f\r\n", strtod(jsp->data.str.buff, NULL)); + } else if (strcmp(jsp->stack[3].name, "pressure") == 0) { + printf("Current weather pressure: %u\r\n", (unsigned)atoll(jsp->data.str.buff)); + } else if (strcmp(jsp->stack[3].name, "humidity") == 0) { + printf("Current weather humidity: %u\r\n", (unsigned)atoll(jsp->data.str.buff)); + } else if (strcmp(jsp->stack[3].name, "uvi") == 0) { + printf("Current weather uvi: %u\r\n", (unsigned)atoll(jsp->data.str.buff)); + } else if (strcmp(jsp->stack[3].name, "clouds") == 0) { + printf("Current weather clouds: %u\r\n", (unsigned)atoll(jsp->data.str.buff)); + } else if (strcmp(jsp->stack[3].name, "visibility") == 0) { + printf("Current weather visibility: %u\r\n", (unsigned)atoll(jsp->data.str.buff)); + } else if (strcmp(jsp->stack[3].name, "wind_speed") == 0) { + printf("Current weather wind_speed: %f\r\n", strtod(jsp->data.str.buff, NULL)); + } else if (strcmp(jsp->stack[3].name, "wind_deg") == 0) { + printf("Current weather wind_deg: %u\r\n", (unsigned)atoll(jsp->data.str.buff)); + } } } - /* Get daily temperatures */ + /* + * Process the various object in specific JSON order + */ if (jsp->stack_pos >= 5 /* First build the order... */ && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY && jsp->stack[2].type == LWJSON_STREAM_TYPE_ARRAY && jsp->stack[3].type == LWJSON_STREAM_TYPE_OBJECT - && jsp->stack[4].type == LWJSON_STREAM_TYPE_KEY - - /* .. then analyze the strings */ - && strcmp(jsp->stack[1].name, "daily") == 0) { + && jsp->stack[4].type == LWJSON_STREAM_TYPE_KEY) { - /* Analyze objects for temp and feels like object */ - if (jsp->stack_pos >= 7 - && jsp->stack[5].type == LWJSON_STREAM_TYPE_OBJECT - && jsp->stack[6].type == LWJSON_STREAM_TYPE_KEY) { - if (strcmp(jsp->stack[4].name, "temp") == 0) { - /* Parsing of temp object */ - if (strcmp(jsp->stack[6].name, "min") == 0) { - printf("Day %d temp min: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); - } else if (strcmp(jsp->stack[6].name, "max") == 0) { - printf("Day %d temp max: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); - } else if (strcmp(jsp->stack[6].name, "day") == 0) { - printf("Day %d temp day: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); - } else if (strcmp(jsp->stack[6].name, "night") == 0) { - printf("Day %d temp night: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); - } else if (strcmp(jsp->stack[6].name, "eve") == 0) { - printf("Day %d temp eve: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); - } else if (strcmp(jsp->stack[6].name, "morn") == 0) { - printf("Day %d temp morn: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); + /* + * Handle daily forecast objects + */ + if (strcmp(jsp->stack[1].name, "daily") == 0) { + /* Analyze objects for temp and feels like object */ + if (jsp->stack_pos >= 7 + && jsp->stack[5].type == LWJSON_STREAM_TYPE_OBJECT + && jsp->stack[6].type == LWJSON_STREAM_TYPE_KEY) { + if (strcmp(jsp->stack[4].name, "temp") == 0) { + /* Parsing of temp object */ + if (strcmp(jsp->stack[6].name, "min") == 0) { + printf("Day %2d temp min: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[6].name, "max") == 0) { + printf("Day %2d temp max: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[6].name, "day") == 0) { + printf("Day %2d temp day: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[6].name, "night") == 0) { + printf("Day %2d temp night: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[6].name, "eve") == 0) { + printf("Day %2d temp eve: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[6].name, "morn") == 0) { + printf("Day %2d temp morn: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); + } + } else if (strcmp(jsp->stack[4].name, "feels_like") == 0) { + /* Parsing of feels-like objects */ + if (strcmp(jsp->stack[6].name, "day") == 0) { + printf("Day %2d temp_feels_like day: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[6].name, "night") == 0) { + printf("Day %2d temp_feels_like night: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[6].name, "eve") == 0) { + printf("Day %2d temp_feels_like eve: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[6].name, "morn") == 0) { + printf("Day %2d temp_feels_like morn: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); + } } + } else if (strcmp(jsp->stack[jsp->stack_pos - 1].name, "dt") == 0) { + printf("Day %2d dt: %u\r\n", (int)jsp->stack[2].index, (unsigned)atoll(jsp->data.str.buff)); + } else if (strcmp(jsp->stack[jsp->stack_pos - 1].name, "pressure") == 0) { + printf("Day %2d pressure: %u\r\n", (int)jsp->stack[2].index, (unsigned)atoll(jsp->data.str.buff)); + } else if (strcmp(jsp->stack[jsp->stack_pos - 1].name, "humidity") == 0) { + printf("Day %2d humidity: %u\r\n", (int)jsp->stack[2].index, (unsigned)atoll(jsp->data.str.buff)); + } else if (strcmp(jsp->stack[jsp->stack_pos - 1].name, "clouds") == 0) { + printf("Day %2d clouds: %u\r\n", (int)jsp->stack[2].index, (unsigned)atoll(jsp->data.str.buff)); + } else if (strcmp(jsp->stack[jsp->stack_pos - 1].name, "snow") == 0) { + printf("Day %2d snow: %f\r\n", (int)jsp->stack[2].index, strtod(jsp->data.str.buff, NULL)); + } else if (strcmp(jsp->stack[jsp->stack_pos - 1].name, "rain") == 0) { + printf("Day %2d rain: %f\r\n", (int)jsp->stack[2].index, strtod(jsp->data.str.buff, NULL)); + } else if (strcmp(jsp->stack[jsp->stack_pos - 1].name, "uvi") == 0) { + printf("Day %2d uvi: %f\r\n", (int)jsp->stack[2].index, strtod(jsp->data.str.buff, NULL)); + } + } else if (strcmp(jsp->stack[1].name, "hourly") == 0) { + if (strcmp(jsp->stack[4].name, "dt") == 0) { + printf("Hour %2d forecast for dt: %u\r\n", (int)jsp->stack[jsp->stack_pos - 3].index, (unsigned)atoll(jsp->data.str.buff)); + } else if (strcmp(jsp->stack[4].name, "temp") == 0) { + printf("Hour %2d forecast for temp: %f\r\n", (int)jsp->stack[jsp->stack_pos - 3].index, strtod(jsp->data.str.buff, NULL)); } else if (strcmp(jsp->stack[4].name, "feels_like") == 0) { - /* Parsing of feels-like objects */ - if (strcmp(jsp->stack[6].name, "day") == 0) { - printf("Day %d temp_feels_like day: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); - } else if (strcmp(jsp->stack[6].name, "night") == 0) { - printf("Day %d temp_feels_like night: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); - } else if (strcmp(jsp->stack[6].name, "eve") == 0) { - printf("Day %d temp_feels_like eve: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); - } else if (strcmp(jsp->stack[6].name, "morn") == 0) { - printf("Day %d temp_feels_like morn: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); - } + printf("Hour %2d forecast for feels_like: %f\r\n", (int)jsp->stack[jsp->stack_pos - 3].index, strtod(jsp->data.str.buff, NULL)); + } else if (strcmp(jsp->stack[4].name, "pressure") == 0) { + printf("Hour %2d forecast for pressure: %d\r\n", (int)jsp->stack[jsp->stack_pos - 3].index, (int)atoll(jsp->data.str.buff)); + } else if (strcmp(jsp->stack[4].name, "humidity") == 0) { + printf("Hour %2d forecast for humidity: %d\r\n", (int)jsp->stack[jsp->stack_pos - 3].index, (int)atoll(jsp->data.str.buff)); + } + } else if (strcmp(jsp->stack[1].name, "minutely") == 0) { + if (strcmp(jsp->stack[4].name, "dt") == 0) { + printf("Minute %2d forecast for dt: %u\r\n", (int)jsp->stack[jsp->stack_pos - 3].index, (unsigned)atoll(jsp->data.str.buff)); + } else if (strcmp(jsp->stack[4].name, "precipitation") == 0) { + printf("Minute %2d forecast for precipitation: %u\r\n", (int)jsp->stack[jsp->stack_pos - 3].index, (unsigned)atoll(jsp->data.str.buff)); } - } else if (strcmp(jsp->stack[4].name, "dt") == 0) { - printf("Day %d dt: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); - } else if (strcmp(jsp->stack[4].name, "pressure") == 0) { - printf("Day %d pressure: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); - } else if (strcmp(jsp->stack[4].name, "humidity") == 0) { - printf("Day %d humidity: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); - } else if (strcmp(jsp->stack[4].name, "clouds") == 0) { - printf("Day %d clouds: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); - } else if (strcmp(jsp->stack[4].name, "snow") == 0) { - printf("Day %d snow: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); - } else if (strcmp(jsp->stack[4].name, "uvi") == 0) { - printf("Day %d uvi: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); } } } diff --git a/test/json/custom_stream.json b/test/json/custom_stream.json index 002a250..fe4f850 100644 --- a/test/json/custom_stream.json +++ b/test/json/custom_stream.json @@ -10,36 +10,4 @@ ["4", "5", "6"], ["7", "8", "9"] ] -} - -[ - { - "first_name": "Jeanette", - "last_name": "Penddreth", - "email": "jpenddreth0@census.gov", - "gender": "Female", - "ip_address": "26.58.193.2", - "obj": { - "abc": { - "def": { - "gdf": "this is final value" - } - } - } - }, { - "first_name": "Giavani", - "last_name": "Frediani", - "email": "gfrediani1@senate.gov", - "gender": "Male", - "ip_address": "229\\\".179.4.212", - "num": 123, - "num2": 123.43, - "num3": -123E13, - "true": true, - "false": false, - "null": null, - "key": "test", - "lala": 123, - "blabla": -123 - } -] \ No newline at end of file +} \ No newline at end of file diff --git a/test/json/weather_onecall.json b/test/json/weather_onecall.json index 6a76f0a..0eaa186 100644 --- a/test/json/weather_onecall.json +++ b/test/json/weather_onecall.json @@ -1 +1 @@ -{"lat":45.57,"lon":15.19,"timezone":"Europe/Ljubljana","timezone_offset":3600,"current":{"dt":1607029923,"sunrise":1606976516,"sunset":1607008607,"temp":273.15,"feels_like":270.73,"pressure":1008,"humidity":97,"dew_point":272.78,"uvi":0,"clouds":100,"visibility":7000,"wind_speed":0.54,"wind_deg":279,"weather":[{"id":701,"main":"Mist","description":"mist","icon":"50n"}]},"minutely":[{"dt":1607029980,"precipitation":0},{"dt":1607030040,"precipitation":0},{"dt":1607030100,"precipitation":0},{"dt":1607030160,"precipitation":0},{"dt":1607030220,"precipitation":0},{"dt":1607030280,"precipitation":0},{"dt":1607030340,"precipitation":0},{"dt":1607030400,"precipitation":0},{"dt":1607030460,"precipitation":0},{"dt":1607030520,"precipitation":0},{"dt":1607030580,"precipitation":0},{"dt":1607030640,"precipitation":0},{"dt":1607030700,"precipitation":0},{"dt":1607030760,"precipitation":0},{"dt":1607030820,"precipitation":0},{"dt":1607030880,"precipitation":0},{"dt":1607030940,"precipitation":0},{"dt":1607031000,"precipitation":0},{"dt":1607031060,"precipitation":0},{"dt":1607031120,"precipitation":0},{"dt":1607031180,"precipitation":0},{"dt":1607031240,"precipitation":0},{"dt":1607031300,"precipitation":0},{"dt":1607031360,"precipitation":0},{"dt":1607031420,"precipitation":0},{"dt":1607031480,"precipitation":0},{"dt":1607031540,"precipitation":0},{"dt":1607031600,"precipitation":0},{"dt":1607031660,"precipitation":0},{"dt":1607031720,"precipitation":0},{"dt":1607031780,"precipitation":0},{"dt":1607031840,"precipitation":0},{"dt":1607031900,"precipitation":0},{"dt":1607031960,"precipitation":0},{"dt":1607032020,"precipitation":0},{"dt":1607032080,"precipitation":0},{"dt":1607032140,"precipitation":0},{"dt":1607032200,"precipitation":0},{"dt":1607032260,"precipitation":0},{"dt":1607032320,"precipitation":0},{"dt":1607032380,"precipitation":0},{"dt":1607032440,"precipitation":0},{"dt":1607032500,"precipitation":0},{"dt":1607032560,"precipitation":0},{"dt":1607032620,"precipitation":0},{"dt":1607032680,"precipitation":0},{"dt":1607032740,"precipitation":0},{"dt":1607032800,"precipitation":0},{"dt":1607032860,"precipitation":0},{"dt":1607032920,"precipitation":0},{"dt":1607032980,"precipitation":0},{"dt":1607033040,"precipitation":0},{"dt":1607033100,"precipitation":0},{"dt":1607033160,"precipitation":0},{"dt":1607033220,"precipitation":0},{"dt":1607033280,"precipitation":0},{"dt":1607033340,"precipitation":0},{"dt":1607033400,"precipitation":0},{"dt":1607033460,"precipitation":0},{"dt":1607033520,"precipitation":0},{"dt":1607033580,"precipitation":0}],"hourly":[{"dt":1607029200,"temp":273.15,"feels_like":270.73,"pressure":1008,"humidity":97,"dew_point":272.78,"uvi":0,"clouds":100,"visibility":10000,"wind_speed":0.54,"wind_deg":279,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0.29},{"dt":1607032800,"temp":272.59,"feels_like":269.76,"pressure":1007,"humidity":97,"dew_point":272.22,"uvi":0,"clouds":100,"visibility":10000,"wind_speed":1.01,"wind_deg":281,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0.28},{"dt":1607036400,"temp":271.72,"feels_like":269.22,"pressure":1006,"humidity":96,"dew_point":271.23,"uvi":0,"clouds":100,"visibility":10000,"wind_speed":0.35,"wind_deg":160,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0.25},{"dt":1607040000,"temp":271.02,"feels_like":268.22,"pressure":1005,"humidity":96,"dew_point":270.53,"uvi":0,"clouds":100,"visibility":10000,"wind_speed":0.65,"wind_deg":253,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0.28},{"dt":1607043600,"temp":269.93,"feels_like":266.79,"pressure":1005,"humidity":96,"dew_point":269.45,"uvi":0,"clouds":56,"visibility":10000,"wind_speed":0.95,"wind_deg":275,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04n"}],"pop":0.03},{"dt":1607047200,"temp":270.23,"feels_like":267.14,"pressure":1005,"humidity":96,"dew_point":268.24,"uvi":0,"clouds":77,"visibility":10000,"wind_speed":0.93,"wind_deg":282,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04n"}],"pop":0},{"dt":1607050800,"temp":269.63,"feels_like":266.68,"pressure":1004,"humidity":95,"dew_point":267.52,"uvi":0,"clouds":63,"visibility":10000,"wind_speed":0.61,"wind_deg":273,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04n"}],"pop":0},{"dt":1607054400,"temp":269.46,"feels_like":266.56,"pressure":1004,"humidity":95,"dew_point":267.3,"uvi":0,"clouds":48,"visibility":10000,"wind_speed":0.51,"wind_deg":249,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03n"}],"pop":0},{"dt":1607058000,"temp":269.72,"feels_like":266.6,"pressure":1005,"humidity":95,"dew_point":267.49,"uvi":0,"clouds":38,"visibility":10000,"wind_speed":0.86,"wind_deg":283,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03n"}],"pop":0},{"dt":1607061600,"temp":269.77,"feels_like":266.81,"pressure":1005,"humidity":95,"dew_point":267.52,"uvi":0,"clouds":34,"visibility":10000,"wind_speed":0.64,"wind_deg":275,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03n"}],"pop":0},{"dt":1607065200,"temp":270.2,"feels_like":267.36,"pressure":1005,"humidity":95,"dew_point":267.95,"uvi":0,"clouds":0,"visibility":10000,"wind_speed":0.55,"wind_deg":270,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"pop":0},{"dt":1607068800,"temp":272.92,"feels_like":270.58,"pressure":1005,"humidity":96,"dew_point":270.77,"uvi":0.26,"clouds":49,"visibility":10000,"wind_speed":0.35,"wind_deg":167,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"pop":0},{"dt":1607072400,"temp":274.41,"feels_like":272.19,"pressure":1005,"humidity":90,"dew_point":272.76,"uvi":0.6,"clouds":65,"visibility":10000,"wind_speed":0.29,"wind_deg":144,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0},{"dt":1607076000,"temp":275.47,"feels_like":273.46,"pressure":1005,"humidity":91,"dew_point":274.22,"uvi":0.85,"clouds":70,"visibility":10000,"wind_speed":0.25,"wind_deg":276,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0},{"dt":1607079600,"temp":276.53,"feels_like":274.59,"pressure":1005,"humidity":92,"dew_point":275.48,"uvi":0.99,"clouds":76,"visibility":1176,"wind_speed":0.43,"wind_deg":266,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0},{"dt":1607083200,"temp":277.13,"feels_like":275.22,"pressure":1005,"humidity":92,"dew_point":276.08,"uvi":0.85,"clouds":76,"visibility":144,"wind_speed":0.54,"wind_deg":207,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0},{"dt":1607086800,"temp":277.25,"feels_like":275.29,"pressure":1005,"humidity":92,"dew_point":276.21,"uvi":0.55,"clouds":88,"visibility":49,"wind_speed":0.64,"wind_deg":165,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0},{"dt":1607090400,"temp":276.81,"feels_like":274.39,"pressure":1004,"humidity":92,"dew_point":275.77,"uvi":0.24,"clouds":94,"visibility":46,"wind_speed":1.18,"wind_deg":144,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0},{"dt":1607094000,"temp":276.37,"feels_like":273.83,"pressure":1004,"humidity":92,"dew_point":275.34,"uvi":0,"clouds":96,"visibility":46,"wind_speed":1.25,"wind_deg":140,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0},{"dt":1607097600,"temp":276.11,"feels_like":273.67,"pressure":1004,"humidity":92,"dew_point":275.05,"uvi":0,"clouds":97,"visibility":53,"wind_speed":1.04,"wind_deg":135,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0},{"dt":1607101200,"temp":276.12,"feels_like":273.64,"pressure":1004,"humidity":92,"dew_point":275.05,"uvi":0,"clouds":98,"visibility":65,"wind_speed":1.11,"wind_deg":122,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0},{"dt":1607104800,"temp":275.96,"feels_like":273.42,"pressure":1004,"humidity":92,"dew_point":274.88,"uvi":0,"clouds":98,"visibility":78,"wind_speed":1.15,"wind_deg":126,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0},{"dt":1607108400,"temp":275.85,"feels_like":273.46,"pressure":1005,"humidity":92,"dew_point":274.74,"uvi":0,"clouds":100,"visibility":528,"wind_speed":0.92,"wind_deg":122,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0},{"dt":1607112000,"temp":275.69,"feels_like":273.4,"pressure":1005,"humidity":92,"dew_point":274.57,"uvi":0,"clouds":100,"visibility":532,"wind_speed":0.73,"wind_deg":124,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0},{"dt":1607115600,"temp":275.71,"feels_like":273.5,"pressure":1005,"humidity":92,"dew_point":274.6,"uvi":0,"clouds":100,"visibility":572,"wind_speed":0.62,"wind_deg":117,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0},{"dt":1607119200,"temp":275.85,"feels_like":273.69,"pressure":1005,"humidity":92,"dew_point":274.74,"uvi":0,"clouds":100,"visibility":167,"wind_speed":0.59,"wind_deg":102,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0},{"dt":1607122800,"temp":276.05,"feels_like":273.81,"pressure":1005,"humidity":92,"dew_point":274.97,"uvi":0,"clouds":100,"visibility":136,"wind_speed":0.75,"wind_deg":97,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0},{"dt":1607126400,"temp":276.29,"feels_like":274.02,"pressure":1005,"humidity":92,"dew_point":275.21,"uvi":0,"clouds":100,"visibility":107,"wind_speed":0.84,"wind_deg":110,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0},{"dt":1607130000,"temp":276.69,"feels_like":274.45,"pressure":1005,"humidity":92,"dew_point":275.62,"uvi":0,"clouds":100,"visibility":103,"wind_speed":0.9,"wind_deg":113,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0.02},{"dt":1607133600,"temp":276.89,"feels_like":274.53,"pressure":1005,"humidity":92,"dew_point":275.82,"uvi":0,"clouds":100,"visibility":78,"wind_speed":1.12,"wind_deg":114,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0.02},{"dt":1607137200,"temp":276.93,"feels_like":274.51,"pressure":1004,"humidity":92,"dew_point":275.85,"uvi":0,"clouds":100,"visibility":61,"wind_speed":1.21,"wind_deg":122,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0.02},{"dt":1607140800,"temp":277.06,"feels_like":274.62,"pressure":1004,"humidity":92,"dew_point":275.98,"uvi":0,"clouds":100,"visibility":62,"wind_speed":1.28,"wind_deg":122,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0.02},{"dt":1607144400,"temp":277.19,"feels_like":274.7,"pressure":1004,"humidity":92,"dew_point":276.11,"uvi":0,"clouds":100,"visibility":66,"wind_speed":1.38,"wind_deg":114,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0.02},{"dt":1607148000,"temp":277.12,"feels_like":274.57,"pressure":1004,"humidity":92,"dew_point":276.04,"uvi":0,"clouds":100,"visibility":58,"wind_speed":1.45,"wind_deg":105,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0.02},{"dt":1607151600,"temp":277.12,"feels_like":274.92,"pressure":1005,"humidity":92,"dew_point":276.06,"uvi":0,"clouds":100,"visibility":58,"wind_speed":0.95,"wind_deg":98,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0.03},{"dt":1607155200,"temp":277.54,"feels_like":275.27,"pressure":1005,"humidity":92,"dew_point":276.5,"uvi":0.17,"clouds":100,"visibility":53,"wind_speed":1.15,"wind_deg":101,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0.04},{"dt":1607158800,"temp":278.04,"feels_like":275.8,"pressure":1005,"humidity":92,"dew_point":277.01,"uvi":0.4,"clouds":100,"visibility":49,"wind_speed":1.23,"wind_deg":95,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0.03},{"dt":1607162400,"temp":278.63,"feels_like":276.64,"pressure":1005,"humidity":93,"dew_point":277.6,"uvi":0.44,"clouds":100,"visibility":51,"wind_speed":1.08,"wind_deg":92,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0.02},{"dt":1607166000,"temp":279.08,"feels_like":277.01,"pressure":1004,"humidity":93,"dew_point":278.04,"uvi":0.52,"clouds":100,"visibility":49,"wind_speed":1.32,"wind_deg":88,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0.02},{"dt":1607169600,"temp":279.19,"feels_like":277.03,"pressure":1004,"humidity":93,"dew_point":278.15,"uvi":0.44,"clouds":100,"visibility":44,"wind_speed":1.48,"wind_deg":87,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0.03},{"dt":1607173200,"temp":278.94,"feels_like":276.77,"pressure":1004,"humidity":93,"dew_point":277.9,"uvi":0.35,"clouds":100,"visibility":47,"wind_speed":1.42,"wind_deg":89,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0.13},{"dt":1607176800,"temp":278.63,"feels_like":276.51,"pressure":1004,"humidity":93,"dew_point":277.59,"uvi":0.15,"clouds":100,"visibility":55,"wind_speed":1.27,"wind_deg":94,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0.09},{"dt":1607180400,"temp":278.36,"feels_like":276.14,"pressure":1004,"humidity":92,"dew_point":277.3,"uvi":0,"clouds":100,"visibility":70,"wind_speed":1.29,"wind_deg":90,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0.09},{"dt":1607184000,"temp":278.33,"feels_like":276.08,"pressure":1004,"humidity":92,"dew_point":277.27,"uvi":0,"clouds":100,"visibility":86,"wind_speed":1.32,"wind_deg":92,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0.09},{"dt":1607187600,"temp":278.34,"feels_like":276.06,"pressure":1004,"humidity":92,"dew_point":277.28,"uvi":0,"clouds":100,"visibility":97,"wind_speed":1.37,"wind_deg":94,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0.09},{"dt":1607191200,"temp":278.31,"feels_like":276.04,"pressure":1004,"humidity":92,"dew_point":277.25,"uvi":0,"clouds":100,"visibility":136,"wind_speed":1.35,"wind_deg":94,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0.05},{"dt":1607194800,"temp":278.1,"feels_like":275.9,"pressure":1005,"humidity":92,"dew_point":277.05,"uvi":0,"clouds":95,"visibility":135,"wind_speed":1.2,"wind_deg":98,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0.04},{"dt":1607198400,"temp":278.17,"feels_like":276.08,"pressure":1005,"humidity":92,"dew_point":277.1,"uvi":0,"clouds":97,"visibility":748,"wind_speed":1.06,"wind_deg":103,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0.04}],"daily":[{"dt":1606989600,"sunrise":1606976516,"sunset":1607008607,"temp":{"day":272.75,"min":271.15,"max":273.41,"night":272.59,"eve":272.39,"morn":271.2},"feels_like":{"day":270.38,"night":269.76,"eve":269.92,"morn":268.42},"pressure":1011,"humidity":97,"dew_point":271.03,"wind_speed":0.39,"wind_deg":351,"weather":[{"id":601,"main":"Snow","description":"snow","icon":"13d"}],"clouds":100,"pop":1,"snow":18.51,"uvi":0.59},{"dt":1607076000,"sunrise":1607062981,"sunset":1607094989,"temp":{"day":275.47,"min":269.46,"max":277.25,"night":275.85,"eve":276.11,"morn":269.46},"feels_like":{"day":273.46,"night":273.69,"eve":273.67,"morn":266.56},"pressure":1005,"humidity":91,"dew_point":274.22,"wind_speed":0.25,"wind_deg":276,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"clouds":70,"pop":0.28,"uvi":0.99},{"dt":1607162400,"sunrise":1607149445,"sunset":1607181374,"temp":{"day":278.63,"min":276.05,"max":279.19,"night":278.2,"eve":278.33,"morn":277.06},"feels_like":{"day":276.64,"night":276.01,"eve":276.08,"morn":274.62},"pressure":1005,"humidity":93,"dew_point":277.6,"wind_speed":1.08,"wind_deg":92,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"clouds":100,"pop":0.13,"uvi":0.52},{"dt":1607248800,"sunrise":1607235908,"sunset":1607267762,"temp":{"day":278.52,"min":277.86,"max":279.07,"night":278.45,"eve":278.66,"morn":278.43},"feels_like":{"day":276.6,"night":276.4,"eve":276.55,"morn":276.41},"pressure":1000,"humidity":92,"dew_point":277.47,"wind_speed":0.91,"wind_deg":50,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"clouds":100,"pop":1,"rain":15.96,"uvi":0.27},{"dt":1607335200,"sunrise":1607322368,"sunset":1607354152,"temp":{"day":279.21,"min":276.57,"max":281.48,"night":278.63,"eve":278.95,"morn":277.6},"feels_like":{"day":277.66,"night":277.23,"eve":276.9,"morn":275.15},"pressure":1004,"humidity":88,"dew_point":277.39,"wind_speed":0.39,"wind_deg":186,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"clouds":10,"pop":1,"rain":17.35,"uvi":1.1},{"dt":1607421600,"sunrise":1607408827,"sunset":1607440545,"temp":{"day":278.84,"min":277.29,"max":279.64,"night":277.69,"eve":278.39,"morn":277.84},"feels_like":{"day":277.16,"night":275.96,"eve":276.59,"morn":276.11},"pressure":1006,"humidity":91,"dew_point":277.57,"wind_speed":0.61,"wind_deg":340,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"clouds":100,"pop":0.95,"rain":0.83,"uvi":0.65},{"dt":1607508000,"sunrise":1607495284,"sunset":1607526940,"temp":{"day":278.22,"min":276.81,"max":278.22,"night":277.26,"eve":277.74,"morn":277.4},"feels_like":{"day":276.1,"night":274.29,"eve":275.7,"morn":275.2},"pressure":1007,"humidity":87,"dew_point":276.3,"wind_speed":0.91,"wind_deg":11,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"clouds":87,"pop":0.27,"uvi":1},{"dt":1607594400,"sunrise":1607581739,"sunset":1607613338,"temp":{"day":276.3,"min":276.3,"max":276.99,"night":276.66,"eve":276.89,"morn":276.32},"feels_like":{"day":273.51,"night":274.55,"eve":274.76,"morn":272.89},"pressure":1008,"humidity":90,"dew_point":274.94,"wind_speed":1.52,"wind_deg":315,"weather":[{"id":616,"main":"Snow","description":"rain and snow","icon":"13d"}],"clouds":100,"pop":0.96,"rain":6.33,"snow":2.4,"uvi":1}],"alerts":[{"sender_name":"DHMZ Državni hidrometeorološki zavod","event":"Red snow ice warning","start":1606950000,"end":1607036399,"description":"Freezing rain. minimum temperature \u003c 0 °C"},{"sender_name":"DHMZ Državni hidrometeorološki zavod","event":"Yellow snow ice warning","start":1607036400,"end":1607068800,"description":"Black ice. minimum temperature \u003c 0 °C"}]} \ No newline at end of file +{"lat":45.5709,"lon":15.193,"timezone":"Europe/Ljubljana","timezone_offset":7200,"current":{"dt":1650196368,"sunrise":1650168621,"sunset":1650217613,"temp":10.24,"feels_like":8.31,"pressure":1021,"humidity":38,"dew_point":-3.04,"uvi":4.66,"clouds":57,"visibility":10000,"wind_speed":6.93,"wind_deg":54,"wind_gust":11.11,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}]},"minutely":[{"dt":1650196380,"precipitation":0},{"dt":1650196440,"precipitation":0},{"dt":1650196500,"precipitation":0},{"dt":1650196560,"precipitation":0},{"dt":1650196620,"precipitation":0},{"dt":1650196680,"precipitation":0},{"dt":1650196740,"precipitation":0},{"dt":1650196800,"precipitation":0},{"dt":1650196860,"precipitation":0},{"dt":1650196920,"precipitation":0},{"dt":1650196980,"precipitation":0},{"dt":1650197040,"precipitation":0},{"dt":1650197100,"precipitation":0},{"dt":1650197160,"precipitation":0},{"dt":1650197220,"precipitation":0},{"dt":1650197280,"precipitation":0},{"dt":1650197340,"precipitation":0},{"dt":1650197400,"precipitation":0},{"dt":1650197460,"precipitation":0},{"dt":1650197520,"precipitation":0},{"dt":1650197580,"precipitation":0},{"dt":1650197640,"precipitation":0},{"dt":1650197700,"precipitation":0},{"dt":1650197760,"precipitation":0},{"dt":1650197820,"precipitation":0},{"dt":1650197880,"precipitation":0},{"dt":1650197940,"precipitation":0},{"dt":1650198000,"precipitation":0},{"dt":1650198060,"precipitation":0},{"dt":1650198120,"precipitation":0},{"dt":1650198180,"precipitation":0},{"dt":1650198240,"precipitation":0},{"dt":1650198300,"precipitation":0},{"dt":1650198360,"precipitation":0},{"dt":1650198420,"precipitation":0},{"dt":1650198480,"precipitation":0},{"dt":1650198540,"precipitation":0},{"dt":1650198600,"precipitation":0},{"dt":1650198660,"precipitation":0},{"dt":1650198720,"precipitation":0},{"dt":1650198780,"precipitation":0},{"dt":1650198840,"precipitation":0},{"dt":1650198900,"precipitation":0},{"dt":1650198960,"precipitation":0},{"dt":1650199020,"precipitation":0},{"dt":1650199080,"precipitation":0},{"dt":1650199140,"precipitation":0},{"dt":1650199200,"precipitation":0},{"dt":1650199260,"precipitation":0},{"dt":1650199320,"precipitation":0},{"dt":1650199380,"precipitation":0},{"dt":1650199440,"precipitation":0},{"dt":1650199500,"precipitation":0},{"dt":1650199560,"precipitation":0},{"dt":1650199620,"precipitation":0},{"dt":1650199680,"precipitation":0},{"dt":1650199740,"precipitation":0},{"dt":1650199800,"precipitation":0},{"dt":1650199860,"precipitation":0},{"dt":1650199920,"precipitation":0},{"dt":1650199980,"precipitation":0}],"hourly":[{"dt":1650193200,"temp":10.38,"feels_like":8.49,"pressure":1021,"humidity":39,"dew_point":-2.62,"uvi":5.01,"clouds":55,"visibility":10000,"wind_speed":7.02,"wind_deg":58,"wind_gust":11.58,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0.08},{"dt":1650196800,"temp":10.24,"feels_like":8.31,"pressure":1021,"humidity":38,"dew_point":-3.04,"uvi":4.66,"clouds":57,"visibility":10000,"wind_speed":6.93,"wind_deg":54,"wind_gust":11.11,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0.05},{"dt":1650200400,"temp":10.59,"feels_like":8.67,"pressure":1021,"humidity":37,"dew_point":-3.08,"uvi":3.73,"clouds":65,"visibility":10000,"wind_speed":6.79,"wind_deg":52,"wind_gust":10.76,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0},{"dt":1650204000,"temp":10.98,"feels_like":9.13,"pressure":1021,"humidity":38,"dew_point":-2.46,"uvi":2.51,"clouds":72,"visibility":10000,"wind_speed":6.49,"wind_deg":50,"wind_gust":10.47,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0},{"dt":1650207600,"temp":11.34,"feels_like":9.6,"pressure":1020,"humidity":41,"dew_point":-1.26,"uvi":1.37,"clouds":77,"visibility":10000,"wind_speed":5.69,"wind_deg":49,"wind_gust":10.4,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0},{"dt":1650211200,"temp":11.01,"feels_like":9.34,"pressure":1019,"humidity":45,"dew_point":-0.4,"uvi":0.57,"clouds":72,"visibility":10000,"wind_speed":5.51,"wind_deg":50,"wind_gust":11.1,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0},{"dt":1650214800,"temp":9.54,"feels_like":7.03,"pressure":1019,"humidity":51,"dew_point":-1.06,"uvi":0.14,"clouds":64,"visibility":10000,"wind_speed":4.97,"wind_deg":52,"wind_gust":11.25,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0},{"dt":1650218400,"temp":7.66,"feels_like":5.24,"pressure":1020,"humidity":57,"dew_point":-1.33,"uvi":0,"clouds":56,"visibility":10000,"wind_speed":3.78,"wind_deg":50,"wind_gust":10.49,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04n"}],"pop":0},{"dt":1650222000,"temp":6.12,"feels_like":4.14,"pressure":1021,"humidity":63,"dew_point":-1.25,"uvi":0,"clouds":10,"visibility":10000,"wind_speed":2.61,"wind_deg":48,"wind_gust":8.69,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],"pop":0},{"dt":1650225600,"temp":4.85,"feels_like":3.24,"pressure":1021,"humidity":68,"dew_point":-1.43,"uvi":0,"clouds":8,"visibility":10000,"wind_speed":1.96,"wind_deg":31,"wind_gust":3.84,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],"pop":0},{"dt":1650229200,"temp":4.17,"feels_like":2.65,"pressure":1021,"humidity":71,"dew_point":-1.66,"uvi":0,"clouds":7,"visibility":10000,"wind_speed":1.78,"wind_deg":11,"wind_gust":3.34,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],"pop":0},{"dt":1650232800,"temp":3.77,"feels_like":2.13,"pressure":1021,"humidity":72,"dew_point":-1.77,"uvi":0,"clouds":9,"visibility":10000,"wind_speed":1.83,"wind_deg":355,"wind_gust":3.3,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],"pop":0},{"dt":1650236400,"temp":3.47,"feels_like":1.71,"pressure":1021,"humidity":74,"dew_point":-1.7,"uvi":0,"clouds":19,"visibility":10000,"wind_speed":1.89,"wind_deg":343,"wind_gust":2.72,"weather":[{"id":801,"main":"Clouds","description":"few clouds","icon":"02n"}],"pop":0},{"dt":1650240000,"temp":3.5,"feels_like":1.96,"pressure":1020,"humidity":72,"dew_point":-1.98,"uvi":0,"clouds":30,"visibility":10000,"wind_speed":1.71,"wind_deg":340,"wind_gust":2.8,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03n"}],"pop":0},{"dt":1650243600,"temp":3.19,"feels_like":1.69,"pressure":1020,"humidity":72,"dew_point":-2.29,"uvi":0,"clouds":78,"visibility":10000,"wind_speed":1.64,"wind_deg":355,"wind_gust":2.39,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04n"}],"pop":0},{"dt":1650247200,"temp":2.42,"feels_like":0.96,"pressure":1020,"humidity":74,"dew_point":-2.72,"uvi":0,"clouds":49,"visibility":10000,"wind_speed":1.53,"wind_deg":353,"wind_gust":1.71,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03n"}],"pop":0},{"dt":1650250800,"temp":2.01,"feels_like":0.51,"pressure":1019,"humidity":75,"dew_point":-2.94,"uvi":0,"clouds":36,"visibility":10000,"wind_speed":1.52,"wind_deg":343,"wind_gust":1.65,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03n"}],"pop":0},{"dt":1650254400,"temp":1.8,"feels_like":0.25,"pressure":1019,"humidity":76,"dew_point":-3.04,"uvi":0,"clouds":37,"visibility":10000,"wind_speed":1.53,"wind_deg":333,"wind_gust":1.61,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03n"}],"pop":0},{"dt":1650258000,"temp":3,"feels_like":1.86,"pressure":1019,"humidity":72,"dew_point":-2.59,"uvi":0.15,"clouds":48,"visibility":10000,"wind_speed":1.37,"wind_deg":329,"wind_gust":2.1,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"pop":0},{"dt":1650261600,"temp":5.38,"feels_like":4.54,"pressure":1019,"humidity":62,"dew_point":-2.23,"uvi":0.57,"clouds":54,"visibility":10000,"wind_speed":1.38,"wind_deg":23,"wind_gust":3.84,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0},{"dt":1650265200,"temp":7.09,"feels_like":5.86,"pressure":1019,"humidity":56,"dew_point":-2.11,"uvi":1.4,"clouds":70,"visibility":10000,"wind_speed":1.94,"wind_deg":58,"wind_gust":4.91,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0},{"dt":1650268800,"temp":8.49,"feels_like":6.85,"pressure":1019,"humidity":50,"dew_point":-2.19,"uvi":2.56,"clouds":85,"visibility":10000,"wind_speed":2.76,"wind_deg":64,"wind_gust":6.74,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0},{"dt":1650272400,"temp":9.97,"feels_like":8.05,"pressure":1018,"humidity":44,"dew_point":-2.58,"uvi":3.79,"clouds":74,"visibility":10000,"wind_speed":3.81,"wind_deg":60,"wind_gust":7.32,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0},{"dt":1650276000,"temp":11.08,"feels_like":9.29,"pressure":1017,"humidity":40,"dew_point":-2.82,"uvi":4.66,"clouds":68,"visibility":10000,"wind_speed":4.52,"wind_deg":50,"wind_gust":7.66,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0},{"dt":1650279600,"temp":12.12,"feels_like":10.35,"pressure":1017,"humidity":37,"dew_point":-3.05,"uvi":5.01,"clouds":73,"visibility":10000,"wind_speed":4.47,"wind_deg":50,"wind_gust":7.54,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0},{"dt":1650283200,"temp":12.7,"feels_like":10.94,"pressure":1016,"humidity":35,"dew_point":-3.12,"uvi":4.66,"clouds":78,"visibility":10000,"wind_speed":4.59,"wind_deg":58,"wind_gust":7.55,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0},{"dt":1650286800,"temp":12.87,"feels_like":11.13,"pressure":1015,"humidity":35,"dew_point":-3.25,"uvi":3.51,"clouds":100,"visibility":10000,"wind_speed":4.35,"wind_deg":64,"wind_gust":7.21,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0},{"dt":1650290400,"temp":12.65,"feels_like":10.88,"pressure":1014,"humidity":35,"dew_point":-3.15,"uvi":2.37,"clouds":100,"visibility":10000,"wind_speed":3.61,"wind_deg":65,"wind_gust":5.82,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0},{"dt":1650294000,"temp":12.16,"feels_like":10.42,"pressure":1014,"humidity":38,"dew_point":-2.63,"uvi":1.29,"clouds":100,"visibility":10000,"wind_speed":3.64,"wind_deg":64,"wind_gust":4.9,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0},{"dt":1650297600,"temp":11.5,"feels_like":9.88,"pressure":1015,"humidity":45,"dew_point":-1.01,"uvi":0.48,"clouds":100,"visibility":10000,"wind_speed":3.04,"wind_deg":55,"wind_gust":4.63,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0},{"dt":1650301200,"temp":9.94,"feels_like":8.62,"pressure":1015,"humidity":51,"dew_point":-0.64,"uvi":0.12,"clouds":100,"visibility":10000,"wind_speed":2.7,"wind_deg":62,"wind_gust":5.88,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0},{"dt":1650304800,"temp":7.13,"feels_like":7.13,"pressure":1016,"humidity":61,"dew_point":-0.74,"uvi":0,"clouds":100,"visibility":10000,"wind_speed":1.17,"wind_deg":58,"wind_gust":1.36,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0},{"dt":1650308400,"temp":6.45,"feels_like":6.45,"pressure":1017,"humidity":63,"dew_point":-0.94,"uvi":0,"clouds":100,"visibility":10000,"wind_speed":0.82,"wind_deg":335,"wind_gust":0.96,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0},{"dt":1650312000,"temp":6.26,"feels_like":6.26,"pressure":1017,"humidity":64,"dew_point":-0.95,"uvi":0,"clouds":100,"visibility":10000,"wind_speed":1.25,"wind_deg":294,"wind_gust":1.14,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0},{"dt":1650315600,"temp":5.3,"feels_like":5.3,"pressure":1017,"humidity":68,"dew_point":-1.17,"uvi":0,"clouds":99,"visibility":10000,"wind_speed":1.07,"wind_deg":292,"wind_gust":1.05,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0},{"dt":1650319200,"temp":4.62,"feels_like":4.62,"pressure":1017,"humidity":70,"dew_point":-1.28,"uvi":0,"clouds":98,"visibility":10000,"wind_speed":0.9,"wind_deg":283,"wind_gust":0.86,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0},{"dt":1650322800,"temp":3.84,"feels_like":3.84,"pressure":1017,"humidity":73,"dew_point":-1.53,"uvi":0,"clouds":94,"visibility":10000,"wind_speed":0.94,"wind_deg":268,"wind_gust":0.89,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0},{"dt":1650326400,"temp":3.67,"feels_like":3.67,"pressure":1016,"humidity":73,"dew_point":-1.62,"uvi":0,"clouds":90,"visibility":10000,"wind_speed":0.82,"wind_deg":254,"wind_gust":0.76,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0},{"dt":1650330000,"temp":4.06,"feels_like":4.06,"pressure":1016,"humidity":71,"dew_point":-1.66,"uvi":0,"clouds":100,"visibility":10000,"wind_speed":0.58,"wind_deg":269,"wind_gust":0.73,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0.08},{"dt":1650333600,"temp":4.4,"feels_like":4.4,"pressure":1016,"humidity":70,"dew_point":-1.54,"uvi":0,"clouds":100,"visibility":10000,"wind_speed":0.39,"wind_deg":306,"wind_gust":0.64,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0.14},{"dt":1650337200,"temp":4.58,"feels_like":4.58,"pressure":1016,"humidity":71,"dew_point":-1.24,"uvi":0,"clouds":100,"visibility":10000,"wind_speed":0.29,"wind_deg":36,"wind_gust":0.5,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0.13},{"dt":1650340800,"temp":4.54,"feels_like":4.54,"pressure":1016,"humidity":73,"dew_point":-0.9,"uvi":0,"clouds":100,"visibility":10000,"wind_speed":0.47,"wind_deg":105,"wind_gust":0.46,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0.08},{"dt":1650344400,"temp":4.98,"feels_like":4.98,"pressure":1016,"humidity":75,"dew_point":-0.11,"uvi":0.09,"clouds":100,"visibility":10000,"wind_speed":0.88,"wind_deg":103,"wind_gust":1.41,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0.08},{"dt":1650348000,"temp":5.84,"feels_like":5.1,"pressure":1016,"humidity":76,"dew_point":0.99,"uvi":0.36,"clouds":100,"visibility":10000,"wind_speed":1.35,"wind_deg":94,"wind_gust":1.85,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0.08},{"dt":1650351600,"temp":6.56,"feels_like":5.68,"pressure":1016,"humidity":77,"dew_point":1.84,"uvi":0.62,"clouds":99,"visibility":10000,"wind_speed":1.54,"wind_deg":98,"wind_gust":1.51,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0.27},{"dt":1650355200,"temp":6.74,"feels_like":5.67,"pressure":1016,"humidity":81,"dew_point":2.62,"uvi":1.13,"clouds":100,"visibility":10000,"wind_speed":1.73,"wind_deg":89,"wind_gust":1.46,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0.27},{"dt":1650358800,"temp":6.64,"feels_like":5.43,"pressure":1016,"humidity":84,"dew_point":3.1,"uvi":1.67,"clouds":100,"visibility":10000,"wind_speed":1.85,"wind_deg":82,"wind_gust":1.64,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"pop":0.35,"rain":{"1h":0.13}},{"dt":1650362400,"temp":6.45,"feels_like":5.21,"pressure":1016,"humidity":87,"dew_point":3.38,"uvi":2.64,"clouds":100,"visibility":6594,"wind_speed":1.85,"wind_deg":86,"wind_gust":2.01,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"pop":0.35,"rain":{"1h":0.19}}],"daily":[{"dt":1650189600,"sunrise":1650168621,"sunset":1650217613,"moonrise":1650221520,"moonset":1650170220,"moon_phase":0.52,"temp":{"day":10.17,"min":4.14,"max":11.34,"night":4.17,"eve":11.01,"morn":4.14},"feels_like":{"day":8.29,"night":2.65,"eve":9.34,"morn":0.12},"pressure":1022,"humidity":40,"dew_point":-2.49,"wind_speed":7.42,"wind_deg":56,"wind_gust":14.94,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"clouds":49,"pop":0.11,"uvi":5.01},{"dt":1650276000,"sunrise":1650254916,"sunset":1650304091,"moonrise":1650312840,"moonset":1650258120,"moon_phase":0.56,"temp":{"day":11.08,"min":1.8,"max":12.87,"night":5.3,"eve":11.5,"morn":1.8},"feels_like":{"day":9.29,"night":5.3,"eve":9.88,"morn":0.25},"pressure":1017,"humidity":40,"dew_point":-2.82,"wind_speed":4.59,"wind_deg":58,"wind_gust":7.66,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"clouds":68,"pop":0,"uvi":5.01},{"dt":1650362400,"sunrise":1650341212,"sunset":1650390569,"moonrise":1650404160,"moonset":1650346440,"moon_phase":0.6,"temp":{"day":6.45,"min":3.67,"max":7.77,"night":6.08,"eve":7.32,"morn":4.54},"feels_like":{"day":5.21,"night":6.08,"eve":7.32,"morn":4.54},"pressure":1016,"humidity":87,"dew_point":3.38,"wind_speed":1.85,"wind_deg":82,"wind_gust":2.17,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"clouds":100,"pop":0.48,"rain":0.61,"uvi":2.83},{"dt":1650448800,"sunrise":1650427509,"sunset":1650477047,"moonrise":0,"moonset":1650435240,"moon_phase":0.64,"temp":{"day":5.75,"min":5.03,"max":6.23,"night":5.03,"eve":6.23,"morn":5.93},"feels_like":{"day":5.75,"night":5.03,"eve":5.39,"morn":5.93},"pressure":1018,"humidity":96,"dew_point":4.2,"wind_speed":1.61,"wind_deg":26,"wind_gust":2.91,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"clouds":100,"pop":1,"rain":5.99,"uvi":3.29},{"dt":1650535200,"sunrise":1650513807,"sunset":1650563525,"moonrise":1650495120,"moonset":1650524760,"moon_phase":0.67,"temp":{"day":9.15,"min":1.79,"max":15.09,"night":10.3,"eve":14.5,"morn":1.79},"feels_like":{"day":9.15,"night":9.63,"eve":13.7,"morn":1.79},"pressure":1014,"humidity":77,"dew_point":4.23,"wind_speed":1.8,"wind_deg":150,"wind_gust":1.73,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"clouds":100,"pop":0.14,"uvi":5.04},{"dt":1650621600,"sunrise":1650600106,"sunset":1650650003,"moonrise":1650585540,"moonset":1650614940,"moon_phase":0.71,"temp":{"day":10.96,"min":9,"max":11.43,"night":10.23,"eve":10.68,"morn":9},"feels_like":{"day":10.62,"night":9.87,"eve":10.34,"morn":9},"pressure":1004,"humidity":96,"dew_point":9.29,"wind_speed":2.38,"wind_deg":335,"wind_gust":6,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"clouds":100,"pop":1,"rain":10.46,"uvi":6},{"dt":1650708000,"sunrise":1650686407,"sunset":1650736481,"moonrise":1650675060,"moonset":1650705780,"moon_phase":0.75,"temp":{"day":12.85,"min":9.45,"max":17.64,"night":9.45,"eve":17.64,"morn":10.13},"feels_like":{"day":12.51,"night":9.45,"eve":17.05,"morn":9.76},"pressure":1006,"humidity":89,"dew_point":10.04,"wind_speed":2.49,"wind_deg":230,"wind_gust":5.15,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"clouds":100,"pop":1,"rain":4.12,"uvi":6},{"dt":1650794400,"sunrise":1650772708,"sunset":1650822959,"moonrise":1650763920,"moonset":1650796740,"moon_phase":0.78,"temp":{"day":15.35,"min":10.38,"max":16.38,"night":10.38,"eve":16.38,"morn":11.69},"feels_like":{"day":14.9,"night":9.51,"eve":15.64,"morn":11.26},"pressure":1007,"humidity":75,"dew_point":9.96,"wind_speed":4.97,"wind_deg":234,"wind_gust":12.15,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"clouds":78,"pop":1,"rain":2.26,"uvi":6}],"alerts":[{"sender_name":"Agencija Republike Slovenije za okolje (ARSO vreme)","event":"Moderate Wind Warning","start":1650114000,"end":1650221940,"description":"Maximum wind speed : from 50 to 70 km/h. Wind sways trees and may break smaller branches.","tags":["Wind"]},{"sender_name":"Agencija Republike Slovenije za okolje (ARSO vreme)","event":"Moderate Low temperature Warning","start":1650229200,"end":1650265140,"description":"Low temperatures may affect the health of sensitive members of the population.","tags":["Extreme temperature value"]}]} \ No newline at end of file From c0aa11bc78b10d1d47d4cfff93186a2aee6f4888 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sun, 17 Apr 2022 14:10:40 +0200 Subject: [PATCH 43/77] Put stack to union - gain few bytes for RAM --- dev/main.c | 162 ++++++++++++++--------------- lwjson/src/include/lwjson/lwjson.h | 7 +- lwjson/src/lwjson/lwjson_stream.c | 16 +-- 3 files changed, 93 insertions(+), 92 deletions(-) diff --git a/dev/main.c b/dev/main.c index 9cee551..bc513a0 100644 --- a/dev/main.c +++ b/dev/main.c @@ -106,9 +106,9 @@ jsp_stream_callback(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY && jsp->stack[2].type == LWJSON_STREAM_TYPE_ARRAY && jsp->stack[3].type == LWJSON_STREAM_TYPE_ARRAY - && strcmp(jsp->stack[1].name, "array_in_array") == 0) { + && strcmp(jsp->stack[1].meta.name, "array_in_array") == 0) { printf("Index: %u, Index: %u, value: %s\r\n", - (int)jsp->stack[2].index, (int)jsp->stack[3].index, + (int)jsp->stack[2].meta.index, (int)jsp->stack[3].meta.index, jsp->data.str.buff ); } @@ -124,13 +124,13 @@ jsp_stream_callback(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY) { - if (strcmp(jsp->stack[1].name, "lat") == 0) { + if (strcmp(jsp->stack[1].meta.name, "lat") == 0) { printf("Latitude weather: %f\r\n", strtod(jsp->data.str.buff, NULL)); - } else if (strcmp(jsp->stack[1].name, "lon") == 0) { + } else if (strcmp(jsp->stack[1].meta.name, "lon") == 0) { printf("Longitude weather: %f\r\n", strtod(jsp->data.str.buff, NULL)); - } else if (strcmp(jsp->stack[1].name, "timezone_offset") == 0) { + } else if (strcmp(jsp->stack[1].meta.name, "timezone_offset") == 0) { printf("Timezone offset: %u\r\n", (unsigned)atoll(jsp->data.str.buff)); - } else if (strcmp(jsp->stack[1].name, "timezone") == 0) { + } else if (strcmp(jsp->stack[1].meta.name, "timezone") == 0) { printf("Timezone: %s\r\n", jsp->data.str.buff); } } @@ -144,7 +144,7 @@ jsp_stream_callback(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { && jsp->stack[2].type == LWJSON_STREAM_TYPE_OBJECT && jsp->stack[3].type == LWJSON_STREAM_TYPE_KEY) { /* Check for current weather */ - if (strcmp(jsp->stack[1].name, "current") == 0) { + if (strcmp(jsp->stack[1].meta.name, "current") == 0) { /* * Process the "weather part" */ @@ -152,40 +152,40 @@ jsp_stream_callback(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { && jsp->stack[4].type == LWJSON_STREAM_TYPE_ARRAY && jsp->stack[5].type == LWJSON_STREAM_TYPE_OBJECT && jsp->stack[6].type == LWJSON_STREAM_TYPE_KEY - && strcmp(jsp->stack[3].name, "weather") == 0) { + && strcmp(jsp->stack[3].meta.name, "weather") == 0) { - if (strcmp(jsp->stack[6].name, "id") == 0) { - printf("Current weather %d id: %u\r\n", (int)jsp->stack[4].index, (unsigned)atoll(jsp->data.str.buff)); - } else if (strcmp(jsp->stack[6].name, "main") == 0) { - printf("Current weather %d main: %s\r\n", (int)jsp->stack[4].index, jsp->data.str.buff); - } else if (strcmp(jsp->stack[6].name, "description") == 0) { - printf("Current weather %d description: %s\r\n", (int)jsp->stack[4].index, jsp->data.str.buff); - } else if (strcmp(jsp->stack[6].name, "icon") == 0) { - printf("Current weather %d icon: %s\r\n", (int)jsp->stack[4].index, jsp->data.str.buff); + if (strcmp(jsp->stack[6].meta.name, "id") == 0) { + printf("Current weather %d id: %u\r\n", (int)jsp->stack[4].meta.index, (unsigned)atoll(jsp->data.str.buff)); + } else if (strcmp(jsp->stack[6].meta.name, "main") == 0) { + printf("Current weather %d main: %s\r\n", (int)jsp->stack[4].meta.index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[6].meta.name, "description") == 0) { + printf("Current weather %d description: %s\r\n", (int)jsp->stack[4].meta.index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[6].meta.name, "icon") == 0) { + printf("Current weather %d icon: %s\r\n", (int)jsp->stack[4].meta.index, jsp->data.str.buff); } - } else if (strcmp(jsp->stack[3].name, "dt") == 0) { + } else if (strcmp(jsp->stack[3].meta.name, "dt") == 0) { printf("Current weather dt: %u\r\n", (unsigned)atoll(jsp->data.str.buff)); - } else if (strcmp(jsp->stack[3].name, "sunrise") == 0) { + } else if (strcmp(jsp->stack[3].meta.name, "sunrise") == 0) { printf("Current weather sunrise: %u\r\n", (unsigned)atoll(jsp->data.str.buff)); - } else if (strcmp(jsp->stack[3].name, "sunset") == 0) { + } else if (strcmp(jsp->stack[3].meta.name, "sunset") == 0) { printf("Current weather sunset: %u\r\n", (unsigned)atoll(jsp->data.str.buff)); - } else if (strcmp(jsp->stack[3].name, "temp") == 0) { + } else if (strcmp(jsp->stack[3].meta.name, "temp") == 0) { printf("Current weather temp: %f\r\n", strtod(jsp->data.str.buff, NULL)); - } else if (strcmp(jsp->stack[3].name, "feels_like") == 0) { + } else if (strcmp(jsp->stack[3].meta.name, "feels_like") == 0) { printf("Current weather feels_like: %f\r\n", strtod(jsp->data.str.buff, NULL)); - } else if (strcmp(jsp->stack[3].name, "pressure") == 0) { + } else if (strcmp(jsp->stack[3].meta.name, "pressure") == 0) { printf("Current weather pressure: %u\r\n", (unsigned)atoll(jsp->data.str.buff)); - } else if (strcmp(jsp->stack[3].name, "humidity") == 0) { + } else if (strcmp(jsp->stack[3].meta.name, "humidity") == 0) { printf("Current weather humidity: %u\r\n", (unsigned)atoll(jsp->data.str.buff)); - } else if (strcmp(jsp->stack[3].name, "uvi") == 0) { + } else if (strcmp(jsp->stack[3].meta.name, "uvi") == 0) { printf("Current weather uvi: %u\r\n", (unsigned)atoll(jsp->data.str.buff)); - } else if (strcmp(jsp->stack[3].name, "clouds") == 0) { + } else if (strcmp(jsp->stack[3].meta.name, "clouds") == 0) { printf("Current weather clouds: %u\r\n", (unsigned)atoll(jsp->data.str.buff)); - } else if (strcmp(jsp->stack[3].name, "visibility") == 0) { + } else if (strcmp(jsp->stack[3].meta.name, "visibility") == 0) { printf("Current weather visibility: %u\r\n", (unsigned)atoll(jsp->data.str.buff)); - } else if (strcmp(jsp->stack[3].name, "wind_speed") == 0) { + } else if (strcmp(jsp->stack[3].meta.name, "wind_speed") == 0) { printf("Current weather wind_speed: %f\r\n", strtod(jsp->data.str.buff, NULL)); - } else if (strcmp(jsp->stack[3].name, "wind_deg") == 0) { + } else if (strcmp(jsp->stack[3].meta.name, "wind_deg") == 0) { printf("Current weather wind_deg: %u\r\n", (unsigned)atoll(jsp->data.str.buff)); } } @@ -205,70 +205,70 @@ jsp_stream_callback(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { /* * Handle daily forecast objects */ - if (strcmp(jsp->stack[1].name, "daily") == 0) { + if (strcmp(jsp->stack[1].meta.name, "daily") == 0) { /* Analyze objects for temp and feels like object */ if (jsp->stack_pos >= 7 && jsp->stack[5].type == LWJSON_STREAM_TYPE_OBJECT && jsp->stack[6].type == LWJSON_STREAM_TYPE_KEY) { - if (strcmp(jsp->stack[4].name, "temp") == 0) { + if (strcmp(jsp->stack[4].meta.name, "temp") == 0) { /* Parsing of temp object */ - if (strcmp(jsp->stack[6].name, "min") == 0) { - printf("Day %2d temp min: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); - } else if (strcmp(jsp->stack[6].name, "max") == 0) { - printf("Day %2d temp max: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); - } else if (strcmp(jsp->stack[6].name, "day") == 0) { - printf("Day %2d temp day: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); - } else if (strcmp(jsp->stack[6].name, "night") == 0) { - printf("Day %2d temp night: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); - } else if (strcmp(jsp->stack[6].name, "eve") == 0) { - printf("Day %2d temp eve: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); - } else if (strcmp(jsp->stack[6].name, "morn") == 0) { - printf("Day %2d temp morn: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); + if (strcmp(jsp->stack[6].meta.name, "min") == 0) { + printf("Day %2d temp min: %s\r\n", (int)jsp->stack[2].meta.index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[6].meta.name, "max") == 0) { + printf("Day %2d temp max: %s\r\n", (int)jsp->stack[2].meta.index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[6].meta.name, "day") == 0) { + printf("Day %2d temp day: %s\r\n", (int)jsp->stack[2].meta.index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[6].meta.name, "night") == 0) { + printf("Day %2d temp night: %s\r\n", (int)jsp->stack[2].meta.index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[6].meta.name, "eve") == 0) { + printf("Day %2d temp eve: %s\r\n", (int)jsp->stack[2].meta.index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[6].meta.name, "morn") == 0) { + printf("Day %2d temp morn: %s\r\n", (int)jsp->stack[2].meta.index, jsp->data.str.buff); } - } else if (strcmp(jsp->stack[4].name, "feels_like") == 0) { + } else if (strcmp(jsp->stack[4].meta.name, "feels_like") == 0) { /* Parsing of feels-like objects */ - if (strcmp(jsp->stack[6].name, "day") == 0) { - printf("Day %2d temp_feels_like day: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); - } else if (strcmp(jsp->stack[6].name, "night") == 0) { - printf("Day %2d temp_feels_like night: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); - } else if (strcmp(jsp->stack[6].name, "eve") == 0) { - printf("Day %2d temp_feels_like eve: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); - } else if (strcmp(jsp->stack[6].name, "morn") == 0) { - printf("Day %2d temp_feels_like morn: %s\r\n", (int)jsp->stack[2].index, jsp->data.str.buff); + if (strcmp(jsp->stack[6].meta.name, "day") == 0) { + printf("Day %2d temp_feels_like day: %s\r\n", (int)jsp->stack[2].meta.index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[6].meta.name, "night") == 0) { + printf("Day %2d temp_feels_like night: %s\r\n", (int)jsp->stack[2].meta.index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[6].meta.name, "eve") == 0) { + printf("Day %2d temp_feels_like eve: %s\r\n", (int)jsp->stack[2].meta.index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[6].meta.name, "morn") == 0) { + printf("Day %2d temp_feels_like morn: %s\r\n", (int)jsp->stack[2].meta.index, jsp->data.str.buff); } } - } else if (strcmp(jsp->stack[jsp->stack_pos - 1].name, "dt") == 0) { - printf("Day %2d dt: %u\r\n", (int)jsp->stack[2].index, (unsigned)atoll(jsp->data.str.buff)); - } else if (strcmp(jsp->stack[jsp->stack_pos - 1].name, "pressure") == 0) { - printf("Day %2d pressure: %u\r\n", (int)jsp->stack[2].index, (unsigned)atoll(jsp->data.str.buff)); - } else if (strcmp(jsp->stack[jsp->stack_pos - 1].name, "humidity") == 0) { - printf("Day %2d humidity: %u\r\n", (int)jsp->stack[2].index, (unsigned)atoll(jsp->data.str.buff)); - } else if (strcmp(jsp->stack[jsp->stack_pos - 1].name, "clouds") == 0) { - printf("Day %2d clouds: %u\r\n", (int)jsp->stack[2].index, (unsigned)atoll(jsp->data.str.buff)); - } else if (strcmp(jsp->stack[jsp->stack_pos - 1].name, "snow") == 0) { - printf("Day %2d snow: %f\r\n", (int)jsp->stack[2].index, strtod(jsp->data.str.buff, NULL)); - } else if (strcmp(jsp->stack[jsp->stack_pos - 1].name, "rain") == 0) { - printf("Day %2d rain: %f\r\n", (int)jsp->stack[2].index, strtod(jsp->data.str.buff, NULL)); - } else if (strcmp(jsp->stack[jsp->stack_pos - 1].name, "uvi") == 0) { - printf("Day %2d uvi: %f\r\n", (int)jsp->stack[2].index, strtod(jsp->data.str.buff, NULL)); + } else if (strcmp(jsp->stack[jsp->stack_pos - 1].meta.name, "dt") == 0) { + printf("Day %2d dt: %u\r\n", (int)jsp->stack[2].meta.index, (unsigned)atoll(jsp->data.str.buff)); + } else if (strcmp(jsp->stack[jsp->stack_pos - 1].meta.name, "pressure") == 0) { + printf("Day %2d pressure: %u\r\n", (int)jsp->stack[2].meta.index, (unsigned)atoll(jsp->data.str.buff)); + } else if (strcmp(jsp->stack[jsp->stack_pos - 1].meta.name, "humidity") == 0) { + printf("Day %2d humidity: %u\r\n", (int)jsp->stack[2].meta.index, (unsigned)atoll(jsp->data.str.buff)); + } else if (strcmp(jsp->stack[jsp->stack_pos - 1].meta.name, "clouds") == 0) { + printf("Day %2d clouds: %u\r\n", (int)jsp->stack[2].meta.index, (unsigned)atoll(jsp->data.str.buff)); + } else if (strcmp(jsp->stack[jsp->stack_pos - 1].meta.name, "snow") == 0) { + printf("Day %2d snow: %f\r\n", (int)jsp->stack[2].meta.index, strtod(jsp->data.str.buff, NULL)); + } else if (strcmp(jsp->stack[jsp->stack_pos - 1].meta.name, "rain") == 0) { + printf("Day %2d rain: %f\r\n", (int)jsp->stack[2].meta.index, strtod(jsp->data.str.buff, NULL)); + } else if (strcmp(jsp->stack[jsp->stack_pos - 1].meta.name, "uvi") == 0) { + printf("Day %2d uvi: %f\r\n", (int)jsp->stack[2].meta.index, strtod(jsp->data.str.buff, NULL)); } - } else if (strcmp(jsp->stack[1].name, "hourly") == 0) { - if (strcmp(jsp->stack[4].name, "dt") == 0) { - printf("Hour %2d forecast for dt: %u\r\n", (int)jsp->stack[jsp->stack_pos - 3].index, (unsigned)atoll(jsp->data.str.buff)); - } else if (strcmp(jsp->stack[4].name, "temp") == 0) { - printf("Hour %2d forecast for temp: %f\r\n", (int)jsp->stack[jsp->stack_pos - 3].index, strtod(jsp->data.str.buff, NULL)); - } else if (strcmp(jsp->stack[4].name, "feels_like") == 0) { - printf("Hour %2d forecast for feels_like: %f\r\n", (int)jsp->stack[jsp->stack_pos - 3].index, strtod(jsp->data.str.buff, NULL)); - } else if (strcmp(jsp->stack[4].name, "pressure") == 0) { - printf("Hour %2d forecast for pressure: %d\r\n", (int)jsp->stack[jsp->stack_pos - 3].index, (int)atoll(jsp->data.str.buff)); - } else if (strcmp(jsp->stack[4].name, "humidity") == 0) { - printf("Hour %2d forecast for humidity: %d\r\n", (int)jsp->stack[jsp->stack_pos - 3].index, (int)atoll(jsp->data.str.buff)); + } else if (strcmp(jsp->stack[1].meta.name, "hourly") == 0) { + if (strcmp(jsp->stack[4].meta.name, "dt") == 0) { + printf("Hour %2d forecast for dt: %u\r\n", (int)jsp->stack[jsp->stack_pos - 3].meta.index, (unsigned)atoll(jsp->data.str.buff)); + } else if (strcmp(jsp->stack[4].meta.name, "temp") == 0) { + printf("Hour %2d forecast for temp: %f\r\n", (int)jsp->stack[jsp->stack_pos - 3].meta.index, strtod(jsp->data.str.buff, NULL)); + } else if (strcmp(jsp->stack[4].meta.name, "feels_like") == 0) { + printf("Hour %2d forecast for feels_like: %f\r\n", (int)jsp->stack[jsp->stack_pos - 3].meta.index, strtod(jsp->data.str.buff, NULL)); + } else if (strcmp(jsp->stack[4].meta.name, "pressure") == 0) { + printf("Hour %2d forecast for pressure: %d\r\n", (int)jsp->stack[jsp->stack_pos - 3].meta.index, (int)atoll(jsp->data.str.buff)); + } else if (strcmp(jsp->stack[4].meta.name, "humidity") == 0) { + printf("Hour %2d forecast for humidity: %d\r\n", (int)jsp->stack[jsp->stack_pos - 3].meta.index, (int)atoll(jsp->data.str.buff)); } - } else if (strcmp(jsp->stack[1].name, "minutely") == 0) { - if (strcmp(jsp->stack[4].name, "dt") == 0) { - printf("Minute %2d forecast for dt: %u\r\n", (int)jsp->stack[jsp->stack_pos - 3].index, (unsigned)atoll(jsp->data.str.buff)); - } else if (strcmp(jsp->stack[4].name, "precipitation") == 0) { - printf("Minute %2d forecast for precipitation: %u\r\n", (int)jsp->stack[jsp->stack_pos - 3].index, (unsigned)atoll(jsp->data.str.buff)); + } else if (strcmp(jsp->stack[1].meta.name, "minutely") == 0) { + if (strcmp(jsp->stack[4].meta.name, "dt") == 0) { + printf("Minute %2d forecast for dt: %u\r\n", (int)jsp->stack[jsp->stack_pos - 3].meta.index, (unsigned)atoll(jsp->data.str.buff)); + } else if (strcmp(jsp->stack[4].meta.name, "precipitation") == 0) { + printf("Minute %2d forecast for precipitation: %u\r\n", (int)jsp->stack[jsp->stack_pos - 3].meta.index, (unsigned)atoll(jsp->data.str.buff)); } } } diff --git a/lwjson/src/include/lwjson/lwjson.h b/lwjson/src/include/lwjson/lwjson.h index 6883d7a..bbe5e6a 100644 --- a/lwjson/src/include/lwjson/lwjson.h +++ b/lwjson/src/include/lwjson/lwjson.h @@ -158,9 +158,10 @@ typedef enum { */ typedef struct { lwjson_stream_type_t type; /*!< Streaming type - current value */ - char name[32]; /*!< Last known dictionary name. Not used for array types - TODO: Add conditional compilation to decrease memory size even more */ - uint16_t index; /*!< Current index when type is an array */ + union { + char name[32]; /*!< Last known key name, used only for \ref LWJSON_STREAM_TYPE_KEY type */ + uint16_t index; /*!< Current index when type is an array */ + } meta; /*!< Meta information */ } lwjson_stream_stack_t; typedef enum { diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index 32ddb49..2c2a754 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -81,7 +81,7 @@ uint8_t prv_stack_push(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { if (jsp->stack_pos < LWJSON_ARRAYSIZE(jsp->stack)) { jsp->stack[jsp->stack_pos].type = type; - jsp->stack[jsp->stack_pos].index = 0; + jsp->stack[jsp->stack_pos].meta.index = 0; jsp->stack_pos++; LWJSON_DEBUG(jsp, "Pushed to stack: %s\r\n", type_strings[type]); return 1; @@ -103,7 +103,7 @@ prv_stack_pop(lwjson_stream_parser_t* jsp) { /* Take care of array to indicate number of entries */ if (jsp->stack_pos > 0 && jsp->stack[jsp->stack_pos - 1].type == LWJSON_STREAM_TYPE_ARRAY) { - jsp->stack[jsp->stack_pos - 1].index++; + jsp->stack[jsp->stack_pos - 1].meta.index++; } return t; } @@ -276,11 +276,11 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { SEND_EVT(jsp, LWJSON_STREAM_TYPE_KEY); if (prv_stack_push(jsp, LWJSON_STREAM_TYPE_KEY)) { size_t len = jsp->data.str.buff_pos; - if (len > (sizeof(jsp->stack->name) - 1)) { - len = sizeof(jsp->stack->name) - 1; + if (len > (sizeof(jsp->stack[0].meta.name) - 1)) { + len = sizeof(jsp->stack[0].meta.name) - 1; } - memcpy(jsp->stack[jsp->stack_pos - 1].name, jsp->data.str.buff, len); - jsp->stack[jsp->stack_pos - 1].name[len] = '\0'; + memcpy(jsp->stack[jsp->stack_pos - 1].meta.name, jsp->data.str.buff, len); + jsp->stack[jsp->stack_pos - 1].meta.name[len] = '\0'; } else { LWJSON_DEBUG(jsp, "Cannot push key to stack\r\n"); return lwjsonERRMEM; @@ -292,7 +292,7 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { /* Next character to wait for is either space or comma or end of object */ } else if (t == LWJSON_STREAM_TYPE_ARRAY) { SEND_EVT(jsp, LWJSON_STREAM_TYPE_STRING); - jsp->stack[jsp->stack_pos - 1].index++; + jsp->stack[jsp->stack_pos - 1].meta.index++; } jsp->parse_state = LWJSON_STREAM_STATE_PARSING; } else { @@ -349,7 +349,7 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { if (t == LWJSON_STREAM_TYPE_KEY) { prv_stack_pop(jsp); } else if (t == LWJSON_STREAM_TYPE_ARRAY) { - jsp->stack[jsp->stack_pos - 1].index++; + jsp->stack[jsp->stack_pos - 1].meta.index++; } /* From 915217b84fa7737f8aad9a70aae89616a0ddfc86 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sun, 17 Apr 2022 15:42:15 +0200 Subject: [PATCH 44/77] Set back to define _DEV macro --- lwjson/src/lwjson/lwjson_stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index 2c2a754..e6fbc36 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -34,7 +34,7 @@ #include #include "lwjson/lwjson.h" -#if defined(LWJSON_DEVV) +#if defined(LWJSON_DEV) #include #define DEBUG_STRING_PREFIX_SPACES " " #define LWJSON_DEBUG(jsp, ...) do { \ From 1dc1356aef91ec7b41024ce3e629f4bb6c9d5093 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sun, 17 Apr 2022 17:04:35 +0200 Subject: [PATCH 45/77] Add values to macros --- lwjson/src/include/lwjson/lwjson.h | 6 ++-- lwjson/src/include/lwjson/lwjson_opt.h | 49 ++++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/lwjson/src/include/lwjson/lwjson.h b/lwjson/src/include/lwjson/lwjson.h index bbe5e6a..f09b278 100644 --- a/lwjson/src/include/lwjson/lwjson.h +++ b/lwjson/src/include/lwjson/lwjson.h @@ -159,7 +159,7 @@ typedef enum { typedef struct { lwjson_stream_type_t type; /*!< Streaming type - current value */ union { - char name[32]; /*!< Last known key name, used only for \ref LWJSON_STREAM_TYPE_KEY type */ + char name[LWJSON_CFG_STREAM_KEY_MAX_LEN + 1]; /*!< Last known key name, used only for \ref LWJSON_STREAM_TYPE_KEY type */ uint16_t index; /*!< Current index when type is an array */ } meta; /*!< Meta information */ } lwjson_stream_stack_t; @@ -194,11 +194,11 @@ typedef struct lwjson_stream_parser { /* State */ union { struct { - char buff[512]; /*!< Buffer to write temporary data. TODO: Size to be variable with define */ + char buff[LWJSON_CFG_STREAM_STRING_MAX_LEN + 1]; /*!< Buffer to write temporary data. TODO: Size to be variable with define */ size_t buff_pos; /*!< Buffer position for next write (length of bytes in buffer) */ } str; /*!< String structure */ struct { - char buff[32]; /*!< Temporary write buffer */ + char buff[LWJSON_CFG_STREAM_PRIMITIVE_MAX_LEN + 1]; /*!< Temporary write buffer */ size_t buff_pos; /*!< Buffer position for next write */ } prim; /*!< Primitive object */ /* Todo: Add other types */ diff --git a/lwjson/src/include/lwjson/lwjson_opt.h b/lwjson/src/include/lwjson/lwjson_opt.h index b5a04b8..6a901b3 100644 --- a/lwjson/src/include/lwjson/lwjson_opt.h +++ b/lwjson/src/include/lwjson/lwjson_opt.h @@ -59,7 +59,7 @@ extern "C" { * This is used for numbers in \ref LWJSON_TYPE_NUM_REAL token data type. */ #ifndef LWJSON_CFG_REAL_TYPE -#define LWJSON_CFG_REAL_TYPE float +#define LWJSON_CFG_REAL_TYPE float #endif /** @@ -69,7 +69,7 @@ extern "C" { * This is used for numbers in \ref LWJSON_TYPE_NUM_INT token data type. */ #ifndef LWJSON_CFG_INT_TYPE -#define LWJSON_CFG_INT_TYPE long long +#define LWJSON_CFG_INT_TYPE long long #endif /** @@ -78,9 +78,52 @@ extern "C" { * Default set to `0` to be JSON compliant */ #ifndef LWJSON_CFG_COMMENTS -#define LWJSON_CFG_COMMENTS 0 +#define LWJSON_CFG_COMMENTS 0 #endif +/** + * \defgroup LWJSON_OPT_STREAM JSON stream + * \brief JSON streaming confiuration + * \{ + */ + +/** + * \brief Max length of token key (object key name) to be available for stack storage + * + */ +#ifndef LWJSON_CFG_STREAM_KEY_MAX_LEN +#define LWJSON_CFG_STREAM_KEY_MAX_LEN 32 +#endif + +/** + * \brief Max stack size (depth) in units of \ref lwjson_stream_stack_t structure + * + */ +#ifndef LWJSON_CFG_STREAM_STACK_SIZE +#define LWJSON_CFG_STREAM_STACK_SIZE 16 +#endif + +/** + * \brief Max size of string for single parsing in units of bytes + * + */ +#ifndef LWJSON_CFG_STREAM_STRING_MAX_LEN +#define LWJSON_CFG_STREAM_STRING_MAX_LEN 256 +#endif + +/** + * \brief Max number of bytes used to parse primitive. + * + * Primitives are all numbers and logical values (null, true, false) + */ +#ifndef LWJSON_CFG_STREAM_PRIMITIVE_MAX_LEN +#define LWJSON_CFG_STREAM_PRIMITIVE_MAX_LEN 32 +#endif + +/** + * \} + */ + /** * \} */ From bd11123b7cdb48d1456df5574d4a82722282cc3f Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sun, 17 Apr 2022 17:05:05 +0200 Subject: [PATCH 46/77] Add more code to parse full string --- dev/main.c | 32 +- test/json/weather_onecall.json | 1818 +++++++++++++++++++++++++++++++- 2 files changed, 1848 insertions(+), 2 deletions(-) diff --git a/dev/main.c b/dev/main.c index bc513a0..647cfe3 100644 --- a/dev/main.c +++ b/dev/main.c @@ -237,6 +237,21 @@ jsp_stream_callback(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { printf("Day %2d temp_feels_like morn: %s\r\n", (int)jsp->stack[2].meta.index, jsp->data.str.buff); } } + } else if (jsp->stack_pos >= 8 + && jsp->stack[5].type == LWJSON_STREAM_TYPE_ARRAY + && jsp->stack[6].type == LWJSON_STREAM_TYPE_OBJECT + && jsp->stack[7].type == LWJSON_STREAM_TYPE_KEY + && strcmp(jsp->stack[4].meta.name, "weather") == 0) { + + if (strcmp(jsp->stack[7].meta.name, "id") == 0) { + printf("Day %2d weather %2d id: %s\r\n", (int)jsp->stack[2].meta.index, (int)jsp->stack[5].meta.index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[7].meta.name, "main") == 0) { + printf("Day %2d weather %2d main: %s\r\n", (int)jsp->stack[2].meta.index, (int)jsp->stack[5].meta.index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[7].meta.name, "description") == 0) { + printf("Day %2d weather %2d description: %s\r\n", (int)jsp->stack[2].meta.index, (int)jsp->stack[5].meta.index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[7].meta.name, "icon") == 0) { + printf("Day %2d weather %2d icon: %s\r\n", (int)jsp->stack[2].meta.index, (int)jsp->stack[5].meta.index, jsp->data.str.buff); + } } else if (strcmp(jsp->stack[jsp->stack_pos - 1].meta.name, "dt") == 0) { printf("Day %2d dt: %u\r\n", (int)jsp->stack[2].meta.index, (unsigned)atoll(jsp->data.str.buff)); } else if (strcmp(jsp->stack[jsp->stack_pos - 1].meta.name, "pressure") == 0) { @@ -253,7 +268,22 @@ jsp_stream_callback(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { printf("Day %2d uvi: %f\r\n", (int)jsp->stack[2].meta.index, strtod(jsp->data.str.buff, NULL)); } } else if (strcmp(jsp->stack[1].meta.name, "hourly") == 0) { - if (strcmp(jsp->stack[4].meta.name, "dt") == 0) { + if (jsp->stack_pos >= 8 + && jsp->stack[5].type == LWJSON_STREAM_TYPE_ARRAY + && jsp->stack[6].type == LWJSON_STREAM_TYPE_OBJECT + && jsp->stack[7].type == LWJSON_STREAM_TYPE_KEY + && strcmp(jsp->stack[4].meta.name, "weather") == 0) { + + if (strcmp(jsp->stack[7].meta.name, "id") == 0) { + printf("Hour %2d weather %2d id: %s\r\n", (int)jsp->stack[2].meta.index, (int)jsp->stack[5].meta.index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[7].meta.name, "main") == 0) { + printf("Hour %2d weather %2d main: %s\r\n", (int)jsp->stack[2].meta.index, (int)jsp->stack[5].meta.index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[7].meta.name, "description") == 0) { + printf("Hour %2d weather %2d description: %s\r\n", (int)jsp->stack[2].meta.index, (int)jsp->stack[5].meta.index, jsp->data.str.buff); + } else if (strcmp(jsp->stack[7].meta.name, "icon") == 0) { + printf("Hour %2d weather %2d icon: %s\r\n", (int)jsp->stack[2].meta.index, (int)jsp->stack[5].meta.index, jsp->data.str.buff); + } + }else if (strcmp(jsp->stack[4].meta.name, "dt") == 0) { printf("Hour %2d forecast for dt: %u\r\n", (int)jsp->stack[jsp->stack_pos - 3].meta.index, (unsigned)atoll(jsp->data.str.buff)); } else if (strcmp(jsp->stack[4].meta.name, "temp") == 0) { printf("Hour %2d forecast for temp: %f\r\n", (int)jsp->stack[jsp->stack_pos - 3].meta.index, strtod(jsp->data.str.buff, NULL)); diff --git a/test/json/weather_onecall.json b/test/json/weather_onecall.json index 0eaa186..516aed8 100644 --- a/test/json/weather_onecall.json +++ b/test/json/weather_onecall.json @@ -1 +1,1817 @@ -{"lat":45.5709,"lon":15.193,"timezone":"Europe/Ljubljana","timezone_offset":7200,"current":{"dt":1650196368,"sunrise":1650168621,"sunset":1650217613,"temp":10.24,"feels_like":8.31,"pressure":1021,"humidity":38,"dew_point":-3.04,"uvi":4.66,"clouds":57,"visibility":10000,"wind_speed":6.93,"wind_deg":54,"wind_gust":11.11,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}]},"minutely":[{"dt":1650196380,"precipitation":0},{"dt":1650196440,"precipitation":0},{"dt":1650196500,"precipitation":0},{"dt":1650196560,"precipitation":0},{"dt":1650196620,"precipitation":0},{"dt":1650196680,"precipitation":0},{"dt":1650196740,"precipitation":0},{"dt":1650196800,"precipitation":0},{"dt":1650196860,"precipitation":0},{"dt":1650196920,"precipitation":0},{"dt":1650196980,"precipitation":0},{"dt":1650197040,"precipitation":0},{"dt":1650197100,"precipitation":0},{"dt":1650197160,"precipitation":0},{"dt":1650197220,"precipitation":0},{"dt":1650197280,"precipitation":0},{"dt":1650197340,"precipitation":0},{"dt":1650197400,"precipitation":0},{"dt":1650197460,"precipitation":0},{"dt":1650197520,"precipitation":0},{"dt":1650197580,"precipitation":0},{"dt":1650197640,"precipitation":0},{"dt":1650197700,"precipitation":0},{"dt":1650197760,"precipitation":0},{"dt":1650197820,"precipitation":0},{"dt":1650197880,"precipitation":0},{"dt":1650197940,"precipitation":0},{"dt":1650198000,"precipitation":0},{"dt":1650198060,"precipitation":0},{"dt":1650198120,"precipitation":0},{"dt":1650198180,"precipitation":0},{"dt":1650198240,"precipitation":0},{"dt":1650198300,"precipitation":0},{"dt":1650198360,"precipitation":0},{"dt":1650198420,"precipitation":0},{"dt":1650198480,"precipitation":0},{"dt":1650198540,"precipitation":0},{"dt":1650198600,"precipitation":0},{"dt":1650198660,"precipitation":0},{"dt":1650198720,"precipitation":0},{"dt":1650198780,"precipitation":0},{"dt":1650198840,"precipitation":0},{"dt":1650198900,"precipitation":0},{"dt":1650198960,"precipitation":0},{"dt":1650199020,"precipitation":0},{"dt":1650199080,"precipitation":0},{"dt":1650199140,"precipitation":0},{"dt":1650199200,"precipitation":0},{"dt":1650199260,"precipitation":0},{"dt":1650199320,"precipitation":0},{"dt":1650199380,"precipitation":0},{"dt":1650199440,"precipitation":0},{"dt":1650199500,"precipitation":0},{"dt":1650199560,"precipitation":0},{"dt":1650199620,"precipitation":0},{"dt":1650199680,"precipitation":0},{"dt":1650199740,"precipitation":0},{"dt":1650199800,"precipitation":0},{"dt":1650199860,"precipitation":0},{"dt":1650199920,"precipitation":0},{"dt":1650199980,"precipitation":0}],"hourly":[{"dt":1650193200,"temp":10.38,"feels_like":8.49,"pressure":1021,"humidity":39,"dew_point":-2.62,"uvi":5.01,"clouds":55,"visibility":10000,"wind_speed":7.02,"wind_deg":58,"wind_gust":11.58,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0.08},{"dt":1650196800,"temp":10.24,"feels_like":8.31,"pressure":1021,"humidity":38,"dew_point":-3.04,"uvi":4.66,"clouds":57,"visibility":10000,"wind_speed":6.93,"wind_deg":54,"wind_gust":11.11,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0.05},{"dt":1650200400,"temp":10.59,"feels_like":8.67,"pressure":1021,"humidity":37,"dew_point":-3.08,"uvi":3.73,"clouds":65,"visibility":10000,"wind_speed":6.79,"wind_deg":52,"wind_gust":10.76,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0},{"dt":1650204000,"temp":10.98,"feels_like":9.13,"pressure":1021,"humidity":38,"dew_point":-2.46,"uvi":2.51,"clouds":72,"visibility":10000,"wind_speed":6.49,"wind_deg":50,"wind_gust":10.47,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0},{"dt":1650207600,"temp":11.34,"feels_like":9.6,"pressure":1020,"humidity":41,"dew_point":-1.26,"uvi":1.37,"clouds":77,"visibility":10000,"wind_speed":5.69,"wind_deg":49,"wind_gust":10.4,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0},{"dt":1650211200,"temp":11.01,"feels_like":9.34,"pressure":1019,"humidity":45,"dew_point":-0.4,"uvi":0.57,"clouds":72,"visibility":10000,"wind_speed":5.51,"wind_deg":50,"wind_gust":11.1,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0},{"dt":1650214800,"temp":9.54,"feels_like":7.03,"pressure":1019,"humidity":51,"dew_point":-1.06,"uvi":0.14,"clouds":64,"visibility":10000,"wind_speed":4.97,"wind_deg":52,"wind_gust":11.25,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0},{"dt":1650218400,"temp":7.66,"feels_like":5.24,"pressure":1020,"humidity":57,"dew_point":-1.33,"uvi":0,"clouds":56,"visibility":10000,"wind_speed":3.78,"wind_deg":50,"wind_gust":10.49,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04n"}],"pop":0},{"dt":1650222000,"temp":6.12,"feels_like":4.14,"pressure":1021,"humidity":63,"dew_point":-1.25,"uvi":0,"clouds":10,"visibility":10000,"wind_speed":2.61,"wind_deg":48,"wind_gust":8.69,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],"pop":0},{"dt":1650225600,"temp":4.85,"feels_like":3.24,"pressure":1021,"humidity":68,"dew_point":-1.43,"uvi":0,"clouds":8,"visibility":10000,"wind_speed":1.96,"wind_deg":31,"wind_gust":3.84,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],"pop":0},{"dt":1650229200,"temp":4.17,"feels_like":2.65,"pressure":1021,"humidity":71,"dew_point":-1.66,"uvi":0,"clouds":7,"visibility":10000,"wind_speed":1.78,"wind_deg":11,"wind_gust":3.34,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],"pop":0},{"dt":1650232800,"temp":3.77,"feels_like":2.13,"pressure":1021,"humidity":72,"dew_point":-1.77,"uvi":0,"clouds":9,"visibility":10000,"wind_speed":1.83,"wind_deg":355,"wind_gust":3.3,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],"pop":0},{"dt":1650236400,"temp":3.47,"feels_like":1.71,"pressure":1021,"humidity":74,"dew_point":-1.7,"uvi":0,"clouds":19,"visibility":10000,"wind_speed":1.89,"wind_deg":343,"wind_gust":2.72,"weather":[{"id":801,"main":"Clouds","description":"few clouds","icon":"02n"}],"pop":0},{"dt":1650240000,"temp":3.5,"feels_like":1.96,"pressure":1020,"humidity":72,"dew_point":-1.98,"uvi":0,"clouds":30,"visibility":10000,"wind_speed":1.71,"wind_deg":340,"wind_gust":2.8,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03n"}],"pop":0},{"dt":1650243600,"temp":3.19,"feels_like":1.69,"pressure":1020,"humidity":72,"dew_point":-2.29,"uvi":0,"clouds":78,"visibility":10000,"wind_speed":1.64,"wind_deg":355,"wind_gust":2.39,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04n"}],"pop":0},{"dt":1650247200,"temp":2.42,"feels_like":0.96,"pressure":1020,"humidity":74,"dew_point":-2.72,"uvi":0,"clouds":49,"visibility":10000,"wind_speed":1.53,"wind_deg":353,"wind_gust":1.71,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03n"}],"pop":0},{"dt":1650250800,"temp":2.01,"feels_like":0.51,"pressure":1019,"humidity":75,"dew_point":-2.94,"uvi":0,"clouds":36,"visibility":10000,"wind_speed":1.52,"wind_deg":343,"wind_gust":1.65,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03n"}],"pop":0},{"dt":1650254400,"temp":1.8,"feels_like":0.25,"pressure":1019,"humidity":76,"dew_point":-3.04,"uvi":0,"clouds":37,"visibility":10000,"wind_speed":1.53,"wind_deg":333,"wind_gust":1.61,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03n"}],"pop":0},{"dt":1650258000,"temp":3,"feels_like":1.86,"pressure":1019,"humidity":72,"dew_point":-2.59,"uvi":0.15,"clouds":48,"visibility":10000,"wind_speed":1.37,"wind_deg":329,"wind_gust":2.1,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"pop":0},{"dt":1650261600,"temp":5.38,"feels_like":4.54,"pressure":1019,"humidity":62,"dew_point":-2.23,"uvi":0.57,"clouds":54,"visibility":10000,"wind_speed":1.38,"wind_deg":23,"wind_gust":3.84,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0},{"dt":1650265200,"temp":7.09,"feels_like":5.86,"pressure":1019,"humidity":56,"dew_point":-2.11,"uvi":1.4,"clouds":70,"visibility":10000,"wind_speed":1.94,"wind_deg":58,"wind_gust":4.91,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0},{"dt":1650268800,"temp":8.49,"feels_like":6.85,"pressure":1019,"humidity":50,"dew_point":-2.19,"uvi":2.56,"clouds":85,"visibility":10000,"wind_speed":2.76,"wind_deg":64,"wind_gust":6.74,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0},{"dt":1650272400,"temp":9.97,"feels_like":8.05,"pressure":1018,"humidity":44,"dew_point":-2.58,"uvi":3.79,"clouds":74,"visibility":10000,"wind_speed":3.81,"wind_deg":60,"wind_gust":7.32,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0},{"dt":1650276000,"temp":11.08,"feels_like":9.29,"pressure":1017,"humidity":40,"dew_point":-2.82,"uvi":4.66,"clouds":68,"visibility":10000,"wind_speed":4.52,"wind_deg":50,"wind_gust":7.66,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0},{"dt":1650279600,"temp":12.12,"feels_like":10.35,"pressure":1017,"humidity":37,"dew_point":-3.05,"uvi":5.01,"clouds":73,"visibility":10000,"wind_speed":4.47,"wind_deg":50,"wind_gust":7.54,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0},{"dt":1650283200,"temp":12.7,"feels_like":10.94,"pressure":1016,"humidity":35,"dew_point":-3.12,"uvi":4.66,"clouds":78,"visibility":10000,"wind_speed":4.59,"wind_deg":58,"wind_gust":7.55,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"pop":0},{"dt":1650286800,"temp":12.87,"feels_like":11.13,"pressure":1015,"humidity":35,"dew_point":-3.25,"uvi":3.51,"clouds":100,"visibility":10000,"wind_speed":4.35,"wind_deg":64,"wind_gust":7.21,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0},{"dt":1650290400,"temp":12.65,"feels_like":10.88,"pressure":1014,"humidity":35,"dew_point":-3.15,"uvi":2.37,"clouds":100,"visibility":10000,"wind_speed":3.61,"wind_deg":65,"wind_gust":5.82,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0},{"dt":1650294000,"temp":12.16,"feels_like":10.42,"pressure":1014,"humidity":38,"dew_point":-2.63,"uvi":1.29,"clouds":100,"visibility":10000,"wind_speed":3.64,"wind_deg":64,"wind_gust":4.9,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0},{"dt":1650297600,"temp":11.5,"feels_like":9.88,"pressure":1015,"humidity":45,"dew_point":-1.01,"uvi":0.48,"clouds":100,"visibility":10000,"wind_speed":3.04,"wind_deg":55,"wind_gust":4.63,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0},{"dt":1650301200,"temp":9.94,"feels_like":8.62,"pressure":1015,"humidity":51,"dew_point":-0.64,"uvi":0.12,"clouds":100,"visibility":10000,"wind_speed":2.7,"wind_deg":62,"wind_gust":5.88,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0},{"dt":1650304800,"temp":7.13,"feels_like":7.13,"pressure":1016,"humidity":61,"dew_point":-0.74,"uvi":0,"clouds":100,"visibility":10000,"wind_speed":1.17,"wind_deg":58,"wind_gust":1.36,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0},{"dt":1650308400,"temp":6.45,"feels_like":6.45,"pressure":1017,"humidity":63,"dew_point":-0.94,"uvi":0,"clouds":100,"visibility":10000,"wind_speed":0.82,"wind_deg":335,"wind_gust":0.96,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0},{"dt":1650312000,"temp":6.26,"feels_like":6.26,"pressure":1017,"humidity":64,"dew_point":-0.95,"uvi":0,"clouds":100,"visibility":10000,"wind_speed":1.25,"wind_deg":294,"wind_gust":1.14,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0},{"dt":1650315600,"temp":5.3,"feels_like":5.3,"pressure":1017,"humidity":68,"dew_point":-1.17,"uvi":0,"clouds":99,"visibility":10000,"wind_speed":1.07,"wind_deg":292,"wind_gust":1.05,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0},{"dt":1650319200,"temp":4.62,"feels_like":4.62,"pressure":1017,"humidity":70,"dew_point":-1.28,"uvi":0,"clouds":98,"visibility":10000,"wind_speed":0.9,"wind_deg":283,"wind_gust":0.86,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0},{"dt":1650322800,"temp":3.84,"feels_like":3.84,"pressure":1017,"humidity":73,"dew_point":-1.53,"uvi":0,"clouds":94,"visibility":10000,"wind_speed":0.94,"wind_deg":268,"wind_gust":0.89,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0},{"dt":1650326400,"temp":3.67,"feels_like":3.67,"pressure":1016,"humidity":73,"dew_point":-1.62,"uvi":0,"clouds":90,"visibility":10000,"wind_speed":0.82,"wind_deg":254,"wind_gust":0.76,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0},{"dt":1650330000,"temp":4.06,"feels_like":4.06,"pressure":1016,"humidity":71,"dew_point":-1.66,"uvi":0,"clouds":100,"visibility":10000,"wind_speed":0.58,"wind_deg":269,"wind_gust":0.73,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0.08},{"dt":1650333600,"temp":4.4,"feels_like":4.4,"pressure":1016,"humidity":70,"dew_point":-1.54,"uvi":0,"clouds":100,"visibility":10000,"wind_speed":0.39,"wind_deg":306,"wind_gust":0.64,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0.14},{"dt":1650337200,"temp":4.58,"feels_like":4.58,"pressure":1016,"humidity":71,"dew_point":-1.24,"uvi":0,"clouds":100,"visibility":10000,"wind_speed":0.29,"wind_deg":36,"wind_gust":0.5,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0.13},{"dt":1650340800,"temp":4.54,"feels_like":4.54,"pressure":1016,"humidity":73,"dew_point":-0.9,"uvi":0,"clouds":100,"visibility":10000,"wind_speed":0.47,"wind_deg":105,"wind_gust":0.46,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"pop":0.08},{"dt":1650344400,"temp":4.98,"feels_like":4.98,"pressure":1016,"humidity":75,"dew_point":-0.11,"uvi":0.09,"clouds":100,"visibility":10000,"wind_speed":0.88,"wind_deg":103,"wind_gust":1.41,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0.08},{"dt":1650348000,"temp":5.84,"feels_like":5.1,"pressure":1016,"humidity":76,"dew_point":0.99,"uvi":0.36,"clouds":100,"visibility":10000,"wind_speed":1.35,"wind_deg":94,"wind_gust":1.85,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0.08},{"dt":1650351600,"temp":6.56,"feels_like":5.68,"pressure":1016,"humidity":77,"dew_point":1.84,"uvi":0.62,"clouds":99,"visibility":10000,"wind_speed":1.54,"wind_deg":98,"wind_gust":1.51,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0.27},{"dt":1650355200,"temp":6.74,"feels_like":5.67,"pressure":1016,"humidity":81,"dew_point":2.62,"uvi":1.13,"clouds":100,"visibility":10000,"wind_speed":1.73,"wind_deg":89,"wind_gust":1.46,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"pop":0.27},{"dt":1650358800,"temp":6.64,"feels_like":5.43,"pressure":1016,"humidity":84,"dew_point":3.1,"uvi":1.67,"clouds":100,"visibility":10000,"wind_speed":1.85,"wind_deg":82,"wind_gust":1.64,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"pop":0.35,"rain":{"1h":0.13}},{"dt":1650362400,"temp":6.45,"feels_like":5.21,"pressure":1016,"humidity":87,"dew_point":3.38,"uvi":2.64,"clouds":100,"visibility":6594,"wind_speed":1.85,"wind_deg":86,"wind_gust":2.01,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"pop":0.35,"rain":{"1h":0.19}}],"daily":[{"dt":1650189600,"sunrise":1650168621,"sunset":1650217613,"moonrise":1650221520,"moonset":1650170220,"moon_phase":0.52,"temp":{"day":10.17,"min":4.14,"max":11.34,"night":4.17,"eve":11.01,"morn":4.14},"feels_like":{"day":8.29,"night":2.65,"eve":9.34,"morn":0.12},"pressure":1022,"humidity":40,"dew_point":-2.49,"wind_speed":7.42,"wind_deg":56,"wind_gust":14.94,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"clouds":49,"pop":0.11,"uvi":5.01},{"dt":1650276000,"sunrise":1650254916,"sunset":1650304091,"moonrise":1650312840,"moonset":1650258120,"moon_phase":0.56,"temp":{"day":11.08,"min":1.8,"max":12.87,"night":5.3,"eve":11.5,"morn":1.8},"feels_like":{"day":9.29,"night":5.3,"eve":9.88,"morn":0.25},"pressure":1017,"humidity":40,"dew_point":-2.82,"wind_speed":4.59,"wind_deg":58,"wind_gust":7.66,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"clouds":68,"pop":0,"uvi":5.01},{"dt":1650362400,"sunrise":1650341212,"sunset":1650390569,"moonrise":1650404160,"moonset":1650346440,"moon_phase":0.6,"temp":{"day":6.45,"min":3.67,"max":7.77,"night":6.08,"eve":7.32,"morn":4.54},"feels_like":{"day":5.21,"night":6.08,"eve":7.32,"morn":4.54},"pressure":1016,"humidity":87,"dew_point":3.38,"wind_speed":1.85,"wind_deg":82,"wind_gust":2.17,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"clouds":100,"pop":0.48,"rain":0.61,"uvi":2.83},{"dt":1650448800,"sunrise":1650427509,"sunset":1650477047,"moonrise":0,"moonset":1650435240,"moon_phase":0.64,"temp":{"day":5.75,"min":5.03,"max":6.23,"night":5.03,"eve":6.23,"morn":5.93},"feels_like":{"day":5.75,"night":5.03,"eve":5.39,"morn":5.93},"pressure":1018,"humidity":96,"dew_point":4.2,"wind_speed":1.61,"wind_deg":26,"wind_gust":2.91,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"clouds":100,"pop":1,"rain":5.99,"uvi":3.29},{"dt":1650535200,"sunrise":1650513807,"sunset":1650563525,"moonrise":1650495120,"moonset":1650524760,"moon_phase":0.67,"temp":{"day":9.15,"min":1.79,"max":15.09,"night":10.3,"eve":14.5,"morn":1.79},"feels_like":{"day":9.15,"night":9.63,"eve":13.7,"morn":1.79},"pressure":1014,"humidity":77,"dew_point":4.23,"wind_speed":1.8,"wind_deg":150,"wind_gust":1.73,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"clouds":100,"pop":0.14,"uvi":5.04},{"dt":1650621600,"sunrise":1650600106,"sunset":1650650003,"moonrise":1650585540,"moonset":1650614940,"moon_phase":0.71,"temp":{"day":10.96,"min":9,"max":11.43,"night":10.23,"eve":10.68,"morn":9},"feels_like":{"day":10.62,"night":9.87,"eve":10.34,"morn":9},"pressure":1004,"humidity":96,"dew_point":9.29,"wind_speed":2.38,"wind_deg":335,"wind_gust":6,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"clouds":100,"pop":1,"rain":10.46,"uvi":6},{"dt":1650708000,"sunrise":1650686407,"sunset":1650736481,"moonrise":1650675060,"moonset":1650705780,"moon_phase":0.75,"temp":{"day":12.85,"min":9.45,"max":17.64,"night":9.45,"eve":17.64,"morn":10.13},"feels_like":{"day":12.51,"night":9.45,"eve":17.05,"morn":9.76},"pressure":1006,"humidity":89,"dew_point":10.04,"wind_speed":2.49,"wind_deg":230,"wind_gust":5.15,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"clouds":100,"pop":1,"rain":4.12,"uvi":6},{"dt":1650794400,"sunrise":1650772708,"sunset":1650822959,"moonrise":1650763920,"moonset":1650796740,"moon_phase":0.78,"temp":{"day":15.35,"min":10.38,"max":16.38,"night":10.38,"eve":16.38,"morn":11.69},"feels_like":{"day":14.9,"night":9.51,"eve":15.64,"morn":11.26},"pressure":1007,"humidity":75,"dew_point":9.96,"wind_speed":4.97,"wind_deg":234,"wind_gust":12.15,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"clouds":78,"pop":1,"rain":2.26,"uvi":6}],"alerts":[{"sender_name":"Agencija Republike Slovenije za okolje (ARSO vreme)","event":"Moderate Wind Warning","start":1650114000,"end":1650221940,"description":"Maximum wind speed : from 50 to 70 km/h. Wind sways trees and may break smaller branches.","tags":["Wind"]},{"sender_name":"Agencija Republike Slovenije za okolje (ARSO vreme)","event":"Moderate Low temperature Warning","start":1650229200,"end":1650265140,"description":"Low temperatures may affect the health of sensitive members of the population.","tags":["Extreme temperature value"]}]} \ No newline at end of file +{ + "lat": 45.5709, + "lon": 15.193, + "timezone": "Europe/Ljubljana", + "timezone_offset": 7200, + "current": { + "dt": 1650206004, + "sunrise": 1650168621, + "sunset": 1650217613, + "temp": 10.79, + "feels_like": 9, + "pressure": 1019, + "humidity": 41, + "dew_point": -1.7, + "uvi": 1.37, + "clouds": 90, + "visibility": 10000, + "wind_speed": 5.89, + "wind_deg": 48, + "wind_gust": 10.64, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04d" + } + ] + }, + "minutely": [ + { + "dt": 1650206040, + "precipitation": 0 + }, + { + "dt": 1650206100, + "precipitation": 0 + }, + { + "dt": 1650206160, + "precipitation": 0 + }, + { + "dt": 1650206220, + "precipitation": 0 + }, + { + "dt": 1650206280, + "precipitation": 0 + }, + { + "dt": 1650206340, + "precipitation": 0 + }, + { + "dt": 1650206400, + "precipitation": 0 + }, + { + "dt": 1650206460, + "precipitation": 0 + }, + { + "dt": 1650206520, + "precipitation": 0 + }, + { + "dt": 1650206580, + "precipitation": 0 + }, + { + "dt": 1650206640, + "precipitation": 0 + }, + { + "dt": 1650206700, + "precipitation": 0 + }, + { + "dt": 1650206760, + "precipitation": 0 + }, + { + "dt": 1650206820, + "precipitation": 0 + }, + { + "dt": 1650206880, + "precipitation": 0 + }, + { + "dt": 1650206940, + "precipitation": 0 + }, + { + "dt": 1650207000, + "precipitation": 0 + }, + { + "dt": 1650207060, + "precipitation": 0 + }, + { + "dt": 1650207120, + "precipitation": 0 + }, + { + "dt": 1650207180, + "precipitation": 0 + }, + { + "dt": 1650207240, + "precipitation": 0 + }, + { + "dt": 1650207300, + "precipitation": 0 + }, + { + "dt": 1650207360, + "precipitation": 0 + }, + { + "dt": 1650207420, + "precipitation": 0 + }, + { + "dt": 1650207480, + "precipitation": 0 + }, + { + "dt": 1650207540, + "precipitation": 0 + }, + { + "dt": 1650207600, + "precipitation": 0 + }, + { + "dt": 1650207660, + "precipitation": 0 + }, + { + "dt": 1650207720, + "precipitation": 0 + }, + { + "dt": 1650207780, + "precipitation": 0 + }, + { + "dt": 1650207840, + "precipitation": 0 + }, + { + "dt": 1650207900, + "precipitation": 0 + }, + { + "dt": 1650207960, + "precipitation": 0 + }, + { + "dt": 1650208020, + "precipitation": 0 + }, + { + "dt": 1650208080, + "precipitation": 0 + }, + { + "dt": 1650208140, + "precipitation": 0 + }, + { + "dt": 1650208200, + "precipitation": 0 + }, + { + "dt": 1650208260, + "precipitation": 0 + }, + { + "dt": 1650208320, + "precipitation": 0 + }, + { + "dt": 1650208380, + "precipitation": 0 + }, + { + "dt": 1650208440, + "precipitation": 0 + }, + { + "dt": 1650208500, + "precipitation": 0 + }, + { + "dt": 1650208560, + "precipitation": 0 + }, + { + "dt": 1650208620, + "precipitation": 0 + }, + { + "dt": 1650208680, + "precipitation": 0 + }, + { + "dt": 1650208740, + "precipitation": 0 + }, + { + "dt": 1650208800, + "precipitation": 0 + }, + { + "dt": 1650208860, + "precipitation": 0 + }, + { + "dt": 1650208920, + "precipitation": 0 + }, + { + "dt": 1650208980, + "precipitation": 0 + }, + { + "dt": 1650209040, + "precipitation": 0 + }, + { + "dt": 1650209100, + "precipitation": 0 + }, + { + "dt": 1650209160, + "precipitation": 0 + }, + { + "dt": 1650209220, + "precipitation": 0 + }, + { + "dt": 1650209280, + "precipitation": 0 + }, + { + "dt": 1650209340, + "precipitation": 0 + }, + { + "dt": 1650209400, + "precipitation": 0 + }, + { + "dt": 1650209460, + "precipitation": 0 + }, + { + "dt": 1650209520, + "precipitation": 0 + }, + { + "dt": 1650209580, + "precipitation": 0 + }, + { + "dt": 1650209640, + "precipitation": 0 + } + ], + "hourly": [ + { + "dt": 1650204000, + "temp": 11.14, + "feels_like": 9.35, + "pressure": 1019, + "humidity": 40, + "dew_point": -1.72, + "uvi": 2.51, + "clouds": 91, + "visibility": 10000, + "wind_speed": 6.47, + "wind_deg": 48, + "wind_gust": 10.48, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04d" + },{ + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04n" + } + ], + "pop": 0 + }, + { + "dt": 1650207600, + "temp": 10.79, + "feels_like": 9, + "pressure": 1019, + "humidity": 41, + "dew_point": -1.7, + "uvi": 1.37, + "clouds": 90, + "visibility": 10000, + "wind_speed": 5.89, + "wind_deg": 48, + "wind_gust": 10.64, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04d" + } + ], + "pop": 0 + }, + { + "dt": 1650211200, + "temp": 10.91, + "feels_like": 9.15, + "pressure": 1019, + "humidity": 42, + "dew_point": -1.31, + "uvi": 0.57, + "clouds": 88, + "visibility": 10000, + "wind_speed": 5.55, + "wind_deg": 49, + "wind_gust": 11.15, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04d" + } + ], + "pop": 0 + }, + { + "dt": 1650214800, + "temp": 10.37, + "feels_like": 8.64, + "pressure": 1019, + "humidity": 45, + "dew_point": -0.92, + "uvi": 0.14, + "clouds": 81, + "visibility": 10000, + "wind_speed": 5.09, + "wind_deg": 53, + "wind_gust": 11.56, + "weather": [ + { + "id": 803, + "main": "Clouds", + "description": "broken clouds", + "icon": "04d" + } + ], + "pop": 0 + }, + { + "dt": 1650218400, + "temp": 9.02, + "feels_like": 6.91, + "pressure": 1020, + "humidity": 50, + "dew_point": -0.74, + "uvi": 0, + "clouds": 71, + "visibility": 10000, + "wind_speed": 3.77, + "wind_deg": 53, + "wind_gust": 10.47, + "weather": [ + { + "id": 803, + "main": "Clouds", + "description": "broken clouds", + "icon": "04n" + } + ], + "pop": 0 + }, + { + "dt": 1650222000, + "temp": 7.03, + "feels_like": 5.38, + "pressure": 1021, + "humidity": 61, + "dew_point": 0.03, + "uvi": 0, + "clouds": 24, + "visibility": 10000, + "wind_speed": 2.4, + "wind_deg": 48, + "wind_gust": 7.74, + "weather": [ + { + "id": 801, + "main": "Clouds", + "description": "few clouds", + "icon": "02n" + } + ], + "pop": 0 + }, + { + "dt": 1650225600, + "temp": 4.9, + "feels_like": 3.46, + "pressure": 1021, + "humidity": 71, + "dew_point": -1, + "uvi": 0, + "clouds": 7, + "visibility": 10000, + "wind_speed": 1.81, + "wind_deg": 26, + "wind_gust": 3.92, + "weather": [ + { + "id": 800, + "main": "Clear", + "description": "clear sky", + "icon": "01n" + } + ], + "pop": 0 + }, + { + "dt": 1650229200, + "temp": 4.22, + "feels_like": 2.75, + "pressure": 1021, + "humidity": 73, + "dew_point": -1.07, + "uvi": 0, + "clouds": 7, + "visibility": 10000, + "wind_speed": 1.74, + "wind_deg": 6, + "wind_gust": 3.2, + "weather": [ + { + "id": 800, + "main": "Clear", + "description": "clear sky", + "icon": "01n" + } + ], + "pop": 0 + }, + { + "dt": 1650232800, + "temp": 3.97, + "feels_like": 2.43, + "pressure": 1021, + "humidity": 74, + "dew_point": -1.22, + "uvi": 0, + "clouds": 8, + "visibility": 10000, + "wind_speed": 1.77, + "wind_deg": 357, + "wind_gust": 3.31, + "weather": [ + { + "id": 800, + "main": "Clear", + "description": "clear sky", + "icon": "01n" + } + ], + "pop": 0 + }, + { + "dt": 1650236400, + "temp": 3.7, + "feels_like": 2.1, + "pressure": 1021, + "humidity": 74, + "dew_point": -1.51, + "uvi": 0, + "clouds": 19, + "visibility": 10000, + "wind_speed": 1.78, + "wind_deg": 343, + "wind_gust": 2.41, + "weather": [ + { + "id": 801, + "main": "Clouds", + "description": "few clouds", + "icon": "02n" + } + ], + "pop": 0 + }, + { + "dt": 1650240000, + "temp": 3.66, + "feels_like": 2.18, + "pressure": 1020, + "humidity": 72, + "dew_point": -1.79, + "uvi": 0, + "clouds": 27, + "visibility": 10000, + "wind_speed": 1.68, + "wind_deg": 335, + "wind_gust": 2.59, + "weather": [ + { + "id": 802, + "main": "Clouds", + "description": "scattered clouds", + "icon": "03n" + } + ], + "pop": 0 + }, + { + "dt": 1650243600, + "temp": 3.21, + "feels_like": 1.91, + "pressure": 1020, + "humidity": 73, + "dew_point": -2.2, + "uvi": 0, + "clouds": 65, + "visibility": 10000, + "wind_speed": 1.5, + "wind_deg": 353, + "wind_gust": 2.05, + "weather": [ + { + "id": 803, + "main": "Clouds", + "description": "broken clouds", + "icon": "04n" + } + ], + "pop": 0 + }, + { + "dt": 1650247200, + "temp": 2.4, + "feels_like": 1.07, + "pressure": 1019, + "humidity": 74, + "dew_point": -2.63, + "uvi": 0, + "clouds": 41, + "visibility": 10000, + "wind_speed": 1.44, + "wind_deg": 347, + "wind_gust": 1.63, + "weather": [ + { + "id": 802, + "main": "Clouds", + "description": "scattered clouds", + "icon": "03n" + } + ], + "pop": 0 + }, + { + "dt": 1650250800, + "temp": 2.07, + "feels_like": 0.62, + "pressure": 1019, + "humidity": 75, + "dew_point": -2.86, + "uvi": 0, + "clouds": 39, + "visibility": 10000, + "wind_speed": 1.49, + "wind_deg": 336, + "wind_gust": 1.61, + "weather": [ + { + "id": 802, + "main": "Clouds", + "description": "scattered clouds", + "icon": "03n" + } + ], + "pop": 0 + }, + { + "dt": 1650254400, + "temp": 1.85, + "feels_like": 0.51, + "pressure": 1019, + "humidity": 76, + "dew_point": -2.98, + "uvi": 0, + "clouds": 47, + "visibility": 10000, + "wind_speed": 1.4, + "wind_deg": 331, + "wind_gust": 1.47, + "weather": [ + { + "id": 802, + "main": "Clouds", + "description": "scattered clouds", + "icon": "03n" + } + ], + "pop": 0 + }, + { + "dt": 1650258000, + "temp": 3.03, + "feels_like": 3.03, + "pressure": 1019, + "humidity": 71, + "dew_point": -2.6, + "uvi": 0.15, + "clouds": 56, + "visibility": 10000, + "wind_speed": 1.26, + "wind_deg": 329, + "wind_gust": 1.83, + "weather": [ + { + "id": 803, + "main": "Clouds", + "description": "broken clouds", + "icon": "04d" + } + ], + "pop": 0 + }, + { + "dt": 1650261600, + "temp": 5.55, + "feels_like": 4.67, + "pressure": 1019, + "humidity": 61, + "dew_point": -2.31, + "uvi": 0.57, + "clouds": 59, + "visibility": 10000, + "wind_speed": 1.43, + "wind_deg": 18, + "wind_gust": 4.99, + "weather": [ + { + "id": 803, + "main": "Clouds", + "description": "broken clouds", + "icon": "04d" + } + ], + "pop": 0 + }, + { + "dt": 1650265200, + "temp": 7.58, + "feels_like": 5.8, + "pressure": 1018, + "humidity": 53, + "dew_point": -2.37, + "uvi": 1.4, + "clouds": 50, + "visibility": 10000, + "wind_speed": 2.71, + "wind_deg": 44, + "wind_gust": 7.12, + "weather": [ + { + "id": 802, + "main": "Clouds", + "description": "scattered clouds", + "icon": "03d" + } + ], + "pop": 0 + }, + { + "dt": 1650268800, + "temp": 9.16, + "feels_like": 7.06, + "pressure": 1018, + "humidity": 47, + "dew_point": -2.36, + "uvi": 2.56, + "clouds": 56, + "visibility": 10000, + "wind_speed": 3.81, + "wind_deg": 53, + "wind_gust": 8.78, + "weather": [ + { + "id": 803, + "main": "Clouds", + "description": "broken clouds", + "icon": "04d" + } + ], + "pop": 0 + }, + { + "dt": 1650272400, + "temp": 10.37, + "feels_like": 8.59, + "pressure": 1018, + "humidity": 43, + "dew_point": -2.61, + "uvi": 3.79, + "clouds": 43, + "visibility": 10000, + "wind_speed": 4.56, + "wind_deg": 54, + "wind_gust": 8.19, + "weather": [ + { + "id": 802, + "main": "Clouds", + "description": "scattered clouds", + "icon": "03d" + } + ], + "pop": 0 + }, + { + "dt": 1650276000, + "temp": 11.35, + "feels_like": 9.56, + "pressure": 1017, + "humidity": 39, + "dew_point": -2.89, + "uvi": 4.66, + "clouds": 37, + "visibility": 10000, + "wind_speed": 4.84, + "wind_deg": 50, + "wind_gust": 7.91, + "weather": [ + { + "id": 802, + "main": "Clouds", + "description": "scattered clouds", + "icon": "03d" + } + ], + "pop": 0 + }, + { + "dt": 1650279600, + "temp": 12.28, + "feels_like": 10.5, + "pressure": 1016, + "humidity": 36, + "dew_point": -3.17, + "uvi": 5.01, + "clouds": 48, + "visibility": 10000, + "wind_speed": 4.75, + "wind_deg": 49, + "wind_gust": 7.84, + "weather": [ + { + "id": 802, + "main": "Clouds", + "description": "scattered clouds", + "icon": "03d" + } + ], + "pop": 0 + }, + { + "dt": 1650283200, + "temp": 13.02, + "feels_like": 11.24, + "pressure": 1015, + "humidity": 33, + "dew_point": -3.52, + "uvi": 4.66, + "clouds": 56, + "visibility": 10000, + "wind_speed": 4.55, + "wind_deg": 55, + "wind_gust": 7.83, + "weather": [ + { + "id": 803, + "main": "Clouds", + "description": "broken clouds", + "icon": "04d" + } + ], + "pop": 0 + }, + { + "dt": 1650286800, + "temp": 13.19, + "feels_like": 11.43, + "pressure": 1015, + "humidity": 33, + "dew_point": -3.41, + "uvi": 3.51, + "clouds": 97, + "visibility": 10000, + "wind_speed": 4.61, + "wind_deg": 64, + "wind_gust": 7.28, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04d" + } + ], + "pop": 0 + }, + { + "dt": 1650290400, + "temp": 12.65, + "feels_like": 10.88, + "pressure": 1014, + "humidity": 35, + "dew_point": -3.24, + "uvi": 2.37, + "clouds": 98, + "visibility": 10000, + "wind_speed": 3.8, + "wind_deg": 65, + "wind_gust": 5.81, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04d" + } + ], + "pop": 0 + }, + { + "dt": 1650294000, + "temp": 12.21, + "feels_like": 10.48, + "pressure": 1014, + "humidity": 38, + "dew_point": -2.55, + "uvi": 1.29, + "clouds": 99, + "visibility": 10000, + "wind_speed": 3.8, + "wind_deg": 68, + "wind_gust": 5.17, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04d" + } + ], + "pop": 0 + }, + { + "dt": 1650297600, + "temp": 11.44, + "feels_like": 9.81, + "pressure": 1015, + "humidity": 45, + "dew_point": -0.95, + "uvi": 0.48, + "clouds": 99, + "visibility": 10000, + "wind_speed": 3.05, + "wind_deg": 65, + "wind_gust": 5.02, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04d" + } + ], + "pop": 0 + }, + { + "dt": 1650301200, + "temp": 9.82, + "feels_like": 8.51, + "pressure": 1015, + "humidity": 52, + "dew_point": -0.56, + "uvi": 0.12, + "clouds": 99, + "visibility": 10000, + "wind_speed": 2.65, + "wind_deg": 59, + "wind_gust": 6.31, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04d" + } + ], + "pop": 0 + }, + { + "dt": 1650304800, + "temp": 6.91, + "feels_like": 6.13, + "pressure": 1017, + "humidity": 62, + "dew_point": -0.81, + "uvi": 0, + "clouds": 99, + "visibility": 10000, + "wind_speed": 1.5, + "wind_deg": 66, + "wind_gust": 1.86, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04n" + } + ], + "pop": 0 + }, + { + "dt": 1650308400, + "temp": 6.01, + "feels_like": 6.01, + "pressure": 1017, + "humidity": 64, + "dew_point": -1.14, + "uvi": 0, + "clouds": 97, + "visibility": 10000, + "wind_speed": 0.09, + "wind_deg": 358, + "wind_gust": 0.58, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04n" + } + ], + "pop": 0 + }, + { + "dt": 1650312000, + "temp": 5.61, + "feels_like": 5.61, + "pressure": 1018, + "humidity": 66, + "dew_point": -1.28, + "uvi": 0, + "clouds": 95, + "visibility": 10000, + "wind_speed": 1.25, + "wind_deg": 277, + "wind_gust": 1.09, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04n" + },{ + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04n" + } + ], + "pop": 0 + }, + { + "dt": 1650315600, + "temp": 5.17, + "feels_like": 5.17, + "pressure": 1018, + "humidity": 67, + "dew_point": -1.35, + "uvi": 0, + "clouds": 96, + "visibility": 10000, + "wind_speed": 1.24, + "wind_deg": 277, + "wind_gust": 1.14, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04n" + },{ + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04n" + } + ], + "pop": 0 + }, + { + "dt": 1650319200, + "temp": 4.41, + "feels_like": 4.41, + "pressure": 1017, + "humidity": 70, + "dew_point": -1.55, + "uvi": 0, + "clouds": 94, + "visibility": 10000, + "wind_speed": 0.91, + "wind_deg": 288, + "wind_gust": 0.95, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04n" + },{ + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04n" + } + ], + "pop": 0 + }, + { + "dt": 1650322800, + "temp": 4.03, + "feels_like": 4.03, + "pressure": 1017, + "humidity": 71, + "dew_point": -1.69, + "uvi": 0, + "clouds": 92, + "visibility": 10000, + "wind_speed": 1.03, + "wind_deg": 277, + "wind_gust": 1.01, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04n" + },{ + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04n" + } + ], + "pop": 0 + }, + { + "dt": 1650326400, + "temp": 3.82, + "feels_like": 3.82, + "pressure": 1017, + "humidity": 71, + "dew_point": -1.86, + "uvi": 0, + "clouds": 91, + "visibility": 10000, + "wind_speed": 1.3, + "wind_deg": 270, + "wind_gust": 1.13, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04n" + },{ + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04n" + } + ], + "pop": 0 + }, + { + "dt": 1650330000, + "temp": 3.39, + "feels_like": 2.33, + "pressure": 1016, + "humidity": 72, + "dew_point": -2.13, + "uvi": 0, + "clouds": 70, + "visibility": 10000, + "wind_speed": 1.35, + "wind_deg": 272, + "wind_gust": 1.21, + "weather": [ + { + "id": 803, + "main": "Clouds", + "description": "broken clouds", + "icon": "04n" + },{ + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04n" + } + ], + "pop": 0 + }, + { + "dt": 1650333600, + "temp": 3.31, + "feels_like": 3.31, + "pressure": 1016, + "humidity": 71, + "dew_point": -2.34, + "uvi": 0, + "clouds": 80, + "visibility": 10000, + "wind_speed": 1.15, + "wind_deg": 280, + "wind_gust": 1.07, + "weather": [ + { + "id": 803, + "main": "Clouds", + "description": "broken clouds", + "icon": "04n" + },{ + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04n" + } + ], + "pop": 0 + }, + { + "dt": 1650337200, + "temp": 3.46, + "feels_like": 3.46, + "pressure": 1016, + "humidity": 70, + "dew_point": -2.4, + "uvi": 0, + "clouds": 87, + "visibility": 10000, + "wind_speed": 1, + "wind_deg": 281, + "wind_gust": 0.93, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04n" + },{ + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04n" + } + ], + "pop": 0 + }, + { + "dt": 1650340800, + "temp": 3.45, + "feels_like": 3.45, + "pressure": 1016, + "humidity": 70, + "dew_point": -2.52, + "uvi": 0, + "clouds": 90, + "visibility": 10000, + "wind_speed": 0.91, + "wind_deg": 279, + "wind_gust": 0.86, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04n" + },{ + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04n" + } + ], + "pop": 0 + }, + { + "dt": 1650344400, + "temp": 4.72, + "feels_like": 4.72, + "pressure": 1016, + "humidity": 66, + "dew_point": -1.94, + "uvi": 0.09, + "clouds": 92, + "visibility": 10000, + "wind_speed": 0.41, + "wind_deg": 270, + "wind_gust": 0.49, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04d" + },{ + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04n" + } + ], + "pop": 0 + }, + { + "dt": 1650348000, + "temp": 6.49, + "feels_like": 6.49, + "pressure": 1016, + "humidity": 60, + "dew_point": -1.71, + "uvi": 0.36, + "clouds": 93, + "visibility": 10000, + "wind_speed": 0.47, + "wind_deg": 107, + "wind_gust": 0.59, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04d" + } + ], + "pop": 0 + }, + { + "dt": 1650351600, + "temp": 7.94, + "feels_like": 7.94, + "pressure": 1015, + "humidity": 55, + "dew_point": -1.42, + "uvi": 0.62, + "clouds": 100, + "visibility": 10000, + "wind_speed": 1.26, + "wind_deg": 108, + "wind_gust": 1.57, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04d" + } + ], + "pop": 0 + }, + { + "dt": 1650355200, + "temp": 8.92, + "feels_like": 8.42, + "pressure": 1015, + "humidity": 53, + "dew_point": -1.03, + "uvi": 1.13, + "clouds": 100, + "visibility": 10000, + "wind_speed": 1.5, + "wind_deg": 119, + "wind_gust": 1.81, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04d" + } + ], + "pop": 0 + }, + { + "dt": 1650358800, + "temp": 9.52, + "feels_like": 9.11, + "pressure": 1015, + "humidity": 52, + "dew_point": -0.66, + "uvi": 1.67, + "clouds": 100, + "visibility": 10000, + "wind_speed": 1.49, + "wind_deg": 126, + "wind_gust": 1.82, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04d" + } + ], + "pop": 0.04 + }, + { + "dt": 1650362400, + "temp": 10.25, + "feels_like": 8.69, + "pressure": 1014, + "humidity": 52, + "dew_point": -0.14, + "uvi": 2.64, + "clouds": 100, + "visibility": 10000, + "wind_speed": 1.44, + "wind_deg": 128, + "wind_gust": 1.92, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04d" + } + ], + "pop": 0.04 + }, + { + "dt": 1650366000, + "temp": 10.95, + "feels_like": 9.43, + "pressure": 1013, + "humidity": 51, + "dew_point": 0.33, + "uvi": 2.83, + "clouds": 100, + "visibility": 10000, + "wind_speed": 1.47, + "wind_deg": 124, + "wind_gust": 2.11, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04d" + } + ], + "pop": 0.04 + }, + { + "dt": 1650369600, + "temp": 11.83, + "feels_like": 10.37, + "pressure": 1013, + "humidity": 50, + "dew_point": 0.91, + "uvi": 2.64, + "clouds": 100, + "visibility": 10000, + "wind_speed": 1.54, + "wind_deg": 132, + "wind_gust": 2.6, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04d" + } + ], + "pop": 0.04 + }, + { + "dt": 1650373200, + "temp": 12.56, + "feels_like": 11.18, + "pressure": 1012, + "humidity": 50, + "dew_point": 1.53, + "uvi": 1.77, + "clouds": 100, + "visibility": 10000, + "wind_speed": 1.63, + "wind_deg": 155, + "wind_gust": 2.87, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04d" + } + ], + "pop": 0 + } + ], + "daily": [ + { + "dt": 1650189600, + "sunrise": 1650168621, + "sunset": 1650217613, + "moonrise": 1650221520, + "moonset": 1650170220, + "moon_phase": 0.52, + "temp": { + "day": 10.24, + "min": 4.14, + "max": 11.52, + "night": 4.22, + "eve": 10.91, + "morn": 4.14 + }, + "feels_like": { + "day": 8.39, + "night": 2.75, + "eve": 9.15, + "morn": 0.12 + }, + "pressure": 1023, + "humidity": 41, + "dew_point": -3.48, + "wind_speed": 7.1, + "wind_deg": 58, + "wind_gust": 14.94, + "weather": [ + { + "id": 802, + "main": "Clouds", + "description": "scattered clouds", + "icon": "03d" + },{ + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04n" + } + ], + "clouds": 33, + "pop": 0.11, + "uvi": 5.01 + }, + { + "dt": 1650276000, + "sunrise": 1650254916, + "sunset": 1650304091, + "moonrise": 1650312840, + "moonset": 1650258120, + "moon_phase": 0.56, + "temp": { + "day": 11.35, + "min": 1.85, + "max": 13.19, + "night": 5.17, + "eve": 11.44, + "morn": 1.85 + }, + "feels_like": { + "day": 9.56, + "night": 5.17, + "eve": 9.81, + "morn": 0.51 + }, + "pressure": 1017, + "humidity": 39, + "dew_point": -2.89, + "wind_speed": 4.84, + "wind_deg": 50, + "wind_gust": 8.78, + "weather": [ + { + "id": 802, + "main": "Clouds", + "description": "scattered clouds", + "icon": "03d" + },{ + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04n" + } + ], + "clouds": 37, + "pop": 0, + "uvi": 5.01 + }, + { + "dt": 1650362400, + "sunrise": 1650341212, + "sunset": 1650390569, + "moonrise": 1650404160, + "moonset": 1650346440, + "moon_phase": 0.6, + "temp": { + "day": 10.25, + "min": 3.31, + "max": 12.56, + "night": 5.52, + "eve": 11.97, + "morn": 3.45 + }, + "feels_like": { + "day": 8.69, + "night": 5.52, + "eve": 10.68, + "morn": 3.45 + }, + "pressure": 1014, + "humidity": 52, + "dew_point": -0.14, + "wind_speed": 1.63, + "wind_deg": 155, + "wind_gust": 3.27, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04d" + },{ + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04n" + } + ], + "clouds": 100, + "pop": 0.05, + "uvi": 2.83 + }, + { + "dt": 1650448800, + "sunrise": 1650427509, + "sunset": 1650477047, + "moonrise": 0, + "moonset": 1650435240, + "moon_phase": 0.64, + "temp": { + "day": 4.99, + "min": 2.79, + "max": 7.64, + "night": 2.79, + "eve": 7.64, + "morn": 6.49 + }, + "feels_like": { + "day": 4.99, + "night": 2.79, + "eve": 7.64, + "morn": 6.49 + }, + "pressure": 1018, + "humidity": 97, + "dew_point": 3.52, + "wind_speed": 1.3, + "wind_deg": 32, + "wind_gust": 3.75, + "weather": [ + { + "id": 501, + "main": "Rain", + "description": "moderate rain", + "icon": "10d" + },{ + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04n" + } + ], + "clouds": 100, + "pop": 1, + "rain": 6.92, + "uvi": 3.29 + }, + { + "dt": 1650535200, + "sunrise": 1650513807, + "sunset": 1650563525, + "moonrise": 1650495120, + "moonset": 1650524760, + "moon_phase": 0.67, + "temp": { + "day": 10.26, + "min": 1.2, + "max": 14.14, + "night": 9.05, + "eve": 14.14, + "morn": 1.2 + }, + "feels_like": { + "day": 9.01, + "night": 9.05, + "eve": 13.18, + "morn": 1.2 + }, + "pressure": 1014, + "humidity": 64, + "dew_point": 2.65, + "wind_speed": 1.59, + "wind_deg": 136, + "wind_gust": 2.04, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04d" + },{ + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04n" + } + ], + "clouds": 100, + "pop": 0, + "uvi": 5.04 + }, + { + "dt": 1650621600, + "sunrise": 1650600106, + "sunset": 1650650003, + "moonrise": 1650585540, + "moonset": 1650614940, + "moon_phase": 0.71, + "temp": { + "day": 8.81, + "min": 8.55, + "max": 9.77, + "night": 8.6, + "eve": 9.77, + "morn": 8.55 + }, + "feels_like": { + "day": 8.81, + "night": 8.17, + "eve": 9.3, + "morn": 8.55 + }, + "pressure": 1005, + "humidity": 96, + "dew_point": 7.11, + "wind_speed": 1.58, + "wind_deg": 318, + "wind_gust": 3.65, + "weather": [ + { + "id": 501, + "main": "Rain", + "description": "moderate rain", + "icon": "10d" + },{ + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04n" + } + ], + "clouds": 100, + "pop": 0.96, + "rain": 7.26, + "uvi": 6 + }, + { + "dt": 1650708000, + "sunrise": 1650686407, + "sunset": 1650736481, + "moonrise": 1650675060, + "moonset": 1650705780, + "moon_phase": 0.75, + "temp": { + "day": 16.34, + "min": 8.68, + "max": 18.34, + "night": 9.74, + "eve": 17.68, + "morn": 8.68 + }, + "feels_like": { + "day": 15.81, + "night": 9.23, + "eve": 17.04, + "morn": 8.68 + }, + "pressure": 1007, + "humidity": 68, + "dew_point": 9.46, + "wind_speed": 4.27, + "wind_deg": 244, + "wind_gust": 6.63, + "weather": [ + { + "id": 500, + "main": "Rain", + "description": "light rain", + "icon": "10d" + },{ + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04n" + } + ], + "clouds": 52, + "pop": 0.59, + "rain": 0.97, + "uvi": 6 + }, + { + "dt": 1650794400, + "sunrise": 1650772708, + "sunset": 1650822959, + "moonrise": 1650763920, + "moonset": 1650796740, + "moon_phase": 0.78, + "temp": { + "day": 16.25, + "min": 10.15, + "max": 17.18, + "night": 11.38, + "eve": 15.32, + "morn": 10.15 + }, + "feels_like": { + "day": 15.76, + "night": 10.66, + "eve": 14.92, + "morn": 9.6 + }, + "pressure": 1008, + "humidity": 70, + "dew_point": 9.72, + "wind_speed": 4.86, + "wind_deg": 230, + "wind_gust": 12.59, + "weather": [ + { + "id": 500, + "main": "Rain", + "description": "light rain", + "icon": "10d" + },{ + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04n" + } + ], + "clouds": 87, + "pop": 0.56, + "rain": 0.52, + "uvi": 6 + } + ], + "alerts": [ + { + "sender_name": "Agencija Republike Slovenije za okolje (ARSO vreme)", + "event": "Moderate Wind Warning", + "start": 1650114000, + "end": 1650221940, + "description": "Maximum wind speed : from 50 to 70 km/h. Wind sways trees and may break smaller branches.", + "tags": [ + "Wind" + ] + }, + { + "sender_name": "Agencija Republike Slovenije za okolje (ARSO vreme)", + "event": "Moderate Low temperature Warning", + "start": 1650229200, + "end": 1650265140, + "description": "Low temperatures may affect the health of sensitive members of the population.", + "tags": [ + "Extreme temperature value" + ] + } + ] +} \ No newline at end of file From 9bc62f56efdf1c27f0cf173b879b13b3a34a0a1f Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sun, 17 Apr 2022 17:36:33 +0200 Subject: [PATCH 47/77] Add docs and polish some of the points --- CMakeLists.txt | 3 ++ README.md | 1 + dev/main.c | 10 +++--- docs/index.rst | 1 + docs/user-manual/how-it-works.rst | 17 +++++++--- docs/user-manual/index.rst | 3 +- docs/user-manual/stream.rst | 54 +++++++++++++++++++++++++++++++ examples/example_stream.c | 40 +++++++++++++++++++++++ lwjson/src/lwjson/lwjson_stream.c | 2 +- 9 files changed, 120 insertions(+), 11 deletions(-) create mode 100644 docs/user-manual/stream.rst create mode 100644 examples/example_stream.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 7593ea7..a45545c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,9 @@ add_executable(${PROJECT_NAME}) target_sources(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_LIST_DIR}/dev/main.c ${CMAKE_CURRENT_LIST_DIR}/test/test.c + ${CMAKE_CURRENT_LIST_DIR}/examples/example_minimal.c + ${CMAKE_CURRENT_LIST_DIR}/examples/example_traverse.c + ${CMAKE_CURRENT_LIST_DIR}/examples/example_stream.c ) # Add key include paths diff --git a/README.md b/README.md index e968961..8c26290 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Library provides generic JSON text parser. * No recursion during parse operation * Re-entrant functions * Zero-copy, no ``malloc`` or ``free`` functions used +* Supports streaming parsing as secondary option * Optional support for inline comments with `/* comment... */` syntax between any *blank* region of input string * Advanced find algorithm for tokens * Test coverage is available diff --git a/dev/main.c b/dev/main.c index 647cfe3..b6e4088 100644 --- a/dev/main.c +++ b/dev/main.c @@ -14,6 +14,7 @@ static lwjson_stream_parser_t stream_parser; extern void test_run(void); extern void example_minimal_run(void); extern void example_traverse_run(void); +extern void example_stream_run(void); static void jsp_stream_callback(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type); @@ -25,10 +26,11 @@ main() { char* json_text = NULL; const lwjson_token_t* tkn; - //test_run(); - //example_minimal_run(); - //example_traverse_run(); - //return 0; + test_run(); + example_minimal_run(); + example_traverse_run(); + example_stream_run(); + return 0; printf("\n---\n"); /* Init JSON */ diff --git a/docs/index.rst b/docs/index.rst index 27b8f08..ec9a101 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -22,6 +22,7 @@ Features * No recursion during parse operation * Re-entrant functions * Zero-copy, no ``malloc`` or ``free`` functions used +* Supports streaming parsing as secondary option * Optional support for inline comments with `/* comment... */` syntax between any *blank* region of input string * Advanced find algorithm for tokens * Tests coverage is available diff --git a/docs/user-manual/how-it-works.rst b/docs/user-manual/how-it-works.rst index caa7b45..3c5bf4a 100644 --- a/docs/user-manual/how-it-works.rst +++ b/docs/user-manual/how-it-works.rst @@ -3,11 +3,14 @@ How it works ============ -LwJSON fully complies with *RFC 4627* memo. +LwJSON fully complies with *RFC 4627* memo and supports ``2`` types of parsing: -LwJSON accepts input string formatted as JSON as per *RFC 4627*. -Library parses each character and creates list of tokens that are -understood by C language for easier further processing. +* Parsing with full data available as single linear memory (primary option) +* Stream parsing with partial available bytes at any given point of time - advanced state machine + +When full data are available, standard parsing is used with tokens, +that contain references to start/stop indexes of the strings and other primitives and provide +full device tree - sort of custom hash-map value. When JSON is successfully parsed, there are several tokens used, one for each JSON data type. Each token consists of: @@ -19,11 +22,15 @@ Each token consists of: As an example, JSON text ``{"mykey":"myvalue"}`` will be parsed into ``2`` tokens: * First token is the opening bracket and has type *object* as it holds children tokens -* Second token has name ``mykey``, its type is *string* and value is ``myvalue`` +* Second token has name ``mykey``, its type is *string* with value set as ``myvalue`` .. warning:: When JSON input string is parsed, create tokens use input string as a reference. This means that until JSON parsed tokens are being used, original text must stay as-is. + Any modification of source JSON input may destroy references from the token tree and hence generate wrong output for the user + +.. tip:: + See :ref:`stream` for implementation of streaming parser where full data do not need to be available at any given time. .. toctree:: :maxdepth: 2 \ No newline at end of file diff --git a/docs/user-manual/index.rst b/docs/user-manual/index.rst index d9e14b1..d0e7eb4 100644 --- a/docs/user-manual/index.rst +++ b/docs/user-manual/index.rst @@ -8,4 +8,5 @@ User manual how-it-works token-design - data-access \ No newline at end of file + data-access + stream \ No newline at end of file diff --git a/docs/user-manual/stream.rst b/docs/user-manual/stream.rst new file mode 100644 index 0000000..c08f421 --- /dev/null +++ b/docs/user-manual/stream.rst @@ -0,0 +1,54 @@ +.. _stream: + +Stream parser +============= + +Streaming parser implementation is alternative option versus standard tokenized one, in the sense that: + +* There is no need to have full JSON available at one time to have successful parsing +* It can be utilized to parse very large JSON strings on very small systems with limited memory +* It allows users to *take* from the stream only necessary parts and store them to local more system-friendly variable + +This type of parser does not utilize use of tokens, rather focuses on the callback function, +where user is in charge to manually understand token structure and get useful data from it. + +Stream parser introduces *stack* mechanism instead - to keep the track of depthness during parsing the process. +``3`` different element types are stored on *local stack*: + +* Start of object, with ``{`` character +* Start of array, with ``[`` character +* Key from the *object* entry + +.. note:: + Stack is nested as long as JSON input stream is nested in the same way + +Consider this input string: ``{"k1":"v1","k2":[true, false]}``. +During parsing procedure, at some point of time, these events will occur: + +#. Start of *object* detected - *object* pushed to stack + + #. *key* element with name ``k1`` detected and pushed to stack + + #. *string* ``v1`` parsed as *string-value* + #. *key* element with name ``k1`` popped from stack + #. *key* element with name ``k2`` detected and pushed to stack + + #. Start of *array* detected - *array* pushed to stack + + #. ``true`` primitive detected + #. ``false`` primitive detected + #. End of *array* detected - *array* popped from stack + #. *key* element with name ``k2`` popped from stack +#. End of *object* detected - *object* popped from stack + +Each of these events is reported to user in the callback function. + +An example of the stream parsing: + +.. literalinclude:: ../../examples/example_stream.c + :language: c + :linenos: + :caption: Parse JSON data as a stream object + +.. toctree:: + :maxdepth: 2 \ No newline at end of file diff --git a/examples/example_stream.c b/examples/example_stream.c new file mode 100644 index 0000000..39b4f0a --- /dev/null +++ b/examples/example_stream.c @@ -0,0 +1,40 @@ +#include +#include "lwjson/lwjson.h" + +/* Test string to parser */ +static const char* json_str = "{\"k1\":\"v1\",\"k2\":[true, false]}"; + +/* LwJSON stream parser */ +static lwjson_stream_parser_t stream_parser; + +/** + * \brief Callback function for various events + * \param jsp: JSON stream parser object + * \param type: Event type + */ +void +prv_example_callback_func(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { + /* Get a value corresponsing to "k1" key */ + if (jsp->stack_pos >= 2 /* Number of stack entries must be high */ + && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT /* First must be object */ + && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY /* We need key to be before */ + && strcmp(jsp->stack[1].meta.name, "k1") == 0) { + printf("Got key '%s' with value '%s'\r\n", jsp->stack[1].meta.name, jsp->data.str.buff); + } +} + +/* Parse JSON */ +void +example_stream_run(void) { + printf("\r\n\r\nParsing stream\r\n"); + lwjson_stream_init(&stream_parser, prv_example_callback_func); + + /* Demonstrate as stream inputs */ + for (const char *c = json_str; *c != '\0'; ++c) { + if (lwjson_stream_parse(&stream_parser, *c) != lwjsonOK) { + printf("ERROR\r\n"); + return; + } + } + printf("Parsing completed\r\n"); +} diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index e6fbc36..63daca7 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -82,8 +82,8 @@ prv_stack_push(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { if (jsp->stack_pos < LWJSON_ARRAYSIZE(jsp->stack)) { jsp->stack[jsp->stack_pos].type = type; jsp->stack[jsp->stack_pos].meta.index = 0; - jsp->stack_pos++; LWJSON_DEBUG(jsp, "Pushed to stack: %s\r\n", type_strings[type]); + jsp->stack_pos++; return 1; } return 0; From 195ccb58abc779e38611e43910b90023d93762b0 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sun, 17 Apr 2022 17:37:57 +0200 Subject: [PATCH 48/77] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd3ad09..60282fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Change license year to 2022 - Fix GCC warning for incompatible comparison types - Update code style with astyle +- Add support for stream parsing ## 1.5.0 From cf1edae326e90a8156128d164b94063183bc16b6 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Mon, 18 Apr 2022 10:34:43 +0200 Subject: [PATCH 49/77] Update README.md --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8c26290..7fe1c98 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # Lightweight JSON text parser -Library provides generic JSON text parser. +Library provides generic JSON text parser, that is optimized for embedded systems. +Supports `streaming` parsing or classic parsing with full JSON data available in one big linear memory. +First one being optimized for ultra small microcontrollers, second one being ready for PC applications - or simply when several kB of RAM memory is available at any given point of time

Read first: Documentation

@@ -29,4 +31,4 @@ Fresh contributions are always welcome. Simple instructions to proceed:: Alternatively you may: 1. Report a bug -2. Ask for a feature request \ No newline at end of file +2. Ask for a feature request From 644edba6e1378772fb81d7dd22a77cc4e87cce38 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Mon, 18 Apr 2022 12:46:46 +0200 Subject: [PATCH 50/77] Add todo list and update changelog --- CHANGELOG.md | 2 +- TODO.md | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 TODO.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 60282fc..e3b0eac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ - Change license year to 2022 - Fix GCC warning for incompatible comparison types - Update code style with astyle -- Add support for stream parsing +- Add support for stream parsing - first version ## 1.5.0 diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..404fec6 --- /dev/null +++ b/TODO.md @@ -0,0 +1,4 @@ +# TODO + +- Stream parser: split large strings to multiple calbacks +- Stream parser: ignore comments (optional) \ No newline at end of file From 7fb079c1c13693ccf6ff47c464efee00761516e4 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Tue, 3 May 2022 22:14:33 +0200 Subject: [PATCH 51/77] Fix typo --- lwjson/src/lwjson/lwjson_stream.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index 63daca7..d2cb130 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -162,7 +162,7 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { */ case LWJSON_STREAM_STATE_WAITINGFIRSTCHAR: case LWJSON_STREAM_STATE_PARSING: { - /* Determine start or object or an array */ + /* Determine start of object or an array */ if (c == '{' || c == '[') { if (!prv_stack_push(jsp, c == '{' ? LWJSON_STREAM_TYPE_OBJECT : LWJSON_STREAM_TYPE_ARRAY)) { LWJSON_DEBUG(jsp, "Cannot push object/array to stack\r\n"); @@ -171,7 +171,7 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { jsp->parse_state = LWJSON_STREAM_STATE_PARSING; SEND_EVT(jsp, c == '{' ? LWJSON_STREAM_TYPE_OBJECT : LWJSON_STREAM_TYPE_ARRAY); - /* Determine end or object or an array */ + /* Determine end of object or an array */ } else if (c == '}' || c == ']') { lwjson_stream_type_t t = prv_stack_get_top(jsp); From 1cf6e845a5467383ed4bc220fc2198e3381bae34 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Fri, 6 May 2022 11:22:48 +0200 Subject: [PATCH 52/77] Add x-check for parsing --- lwjson/src/lwjson/lwjson_stream.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index d2cb130..012bc2b 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -184,6 +184,16 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { return lwjsonERRJSON; } + /* + * Check if closing character matches stack value + * Avoid cases like: {"key":"value"] or ["v1", "v2", "v3"} + */ + if ((c == '}' && t != LWJSON_STREAM_TYPE_OBJECT) + || (c == ']' && t != LWJSON_STREAM_TYPE_ARRAY)) { + LWJSON_DEBUG(jsp, "ERROR - closing character '%c' does not match stack element \"%s\"\r\n", c, type_strings[t]); + return lwjsonERRJSON; + } + /* Now remove the array or object from stack */ if (!prv_stack_pop(jsp)) { return lwjsonERRMEM; @@ -254,9 +264,6 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { */ if (c == '"' && jsp->prev_c != '\\') { lwjson_stream_type_t t = prv_stack_get_top(jsp); - - /* TODO: Send callback to user */ - #if defined(LWJSON_DEV) if (t == LWJSON_STREAM_TYPE_OBJECT) { LWJSON_DEBUG(jsp, "End of string parsing - object key name: \"%s\"\r\n", jsp->data.str.buff); From ed903cd695b68ff903bf74b378981045424a99b3 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sat, 7 May 2022 01:21:48 +0200 Subject: [PATCH 53/77] Reset stack if state waits for first character --- lwjson/src/lwjson/lwjson_stream.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index 012bc2b..55bfce8 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -164,6 +164,10 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { case LWJSON_STREAM_STATE_PARSING: { /* Determine start of object or an array */ if (c == '{' || c == '[') { + /* Reset stack pointer if this character came from waiting for first character */ + if (jsp->parse_state == LWJSON_STREAM_STATE_WAITINGFIRSTCHAR) { + jsp->stack_pos = 0; + } if (!prv_stack_push(jsp, c == '{' ? LWJSON_STREAM_TYPE_OBJECT : LWJSON_STREAM_TYPE_ARRAY)) { LWJSON_DEBUG(jsp, "Cannot push object/array to stack\r\n"); return lwjsonERRMEM; From 5386a020db0e872a51fdf24647a5de13f6b21571 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Tue, 10 May 2022 20:38:02 +0200 Subject: [PATCH 54/77] docs: Update index rst --- docs/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.rst b/docs/index.rst index ec9a101..12df122 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -80,6 +80,7 @@ Table of contents LwDTC - DateTimeCron LwESP - ESP-AT library + LwEVT - Event manager LwGPS - GPS NMEA parser LwGSM - GSM-AT library LwJSON - JSON parser From 34f2af3e6f34a5b3ccf610bd0069cf07f6099c65 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Wed, 18 May 2022 20:37:13 +0200 Subject: [PATCH 55/77] Update vscode & CMake related points --- .vscode/c_cpp_properties.json | 4 ++-- .vscode/launch.json | 7 +++---- .vscode/settings.json | 8 ++++++-- .vscode/tasks.json | 11 ++++------- cmake/i686-w64-mingw32-gcc.cmake | 7 +++++++ cmake/x86_64-w64-mingw32-gcc.cmake | 7 +++++++ 6 files changed, 29 insertions(+), 15 deletions(-) create mode 100644 cmake/i686-w64-mingw32-gcc.cmake create mode 100644 cmake/x86_64-w64-mingw32-gcc.cmake diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 69fa685..9768bc2 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -1,4 +1,5 @@ { + "version": 4, "configurations": [ { "name": "Win32", @@ -8,6 +9,5 @@ "intelliSenseMode": "windows-gcc-x86", "configurationProvider": "ms-vscode.cmake-tools" } - ], - "version": 4 + ] } \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 51aec30..bf1450f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,13 +8,12 @@ "name": "(Windows) Launch", "type": "cppdbg", "request": "launch", - "program": "${workspaceFolder}\\build\\LwLibPROJECT.exe", + "program": "${command:cmake.buildDirectory}\\LwLibPROJECT.exe", "miDebuggerPath": "c:\\msys64\\mingw64\\bin\\gdb.exe", "args": [], "stopAtEntry": false, - "cwd": "${workspaceFolder}", - "environment": [], - "console": "integratedTerminal" + "cwd": "${fileDirname}", + "environment": [] } ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 80d947b..3b59a00 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,10 @@ { "files.associations": { + "lwevt_types.h": "c", + "lwevt_type.h": "c", + "lwevt.h": "c", "string.h": "c", - "lwjson.h": "c" - } + "lwevt_opt.h": "c" + }, + "esbonio.sphinx.confDir": "" } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 7b3751b..103e43a 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -5,7 +5,7 @@ "type": "cppbuild", "label": "Build project", "command": "cmake", - "args": ["--build", "\"build\"", "-j", "8"], + "args": ["--build", "${command:cmake.buildDirectory}", "-j", "8"], "options": { "cwd": "${workspaceFolder}" }, @@ -19,7 +19,7 @@ "type": "shell", "label": "Re-build project", "command": "cmake", - "args": ["--build", "\"build\"", "--clean-first", "-v", "-j", "8"], + "args": ["--build", "${command:cmake.buildDirectory}", "--clean-first", "-v", "-j", "8"], "options": { "cwd": "${workspaceFolder}" }, @@ -29,7 +29,7 @@ "type": "shell", "label": "Clean project", "command": "cmake", - "args": ["--build", "\"build\"", "--target", "clean"], + "args": ["--build", "${command:cmake.buildDirectory}", "--target", "clean"], "options": { "cwd": "${workspaceFolder}" }, @@ -38,11 +38,8 @@ { "type": "shell", "label": "Run application", - "command": "${workspaceFolder}\\build\\LwLibPROJECT.exe", + "command": "${command:cmake.buildDirectory}\\LwLibPROJECT.exe", "args": [], - "options": { - "cwd": "${workspaceFolder}" - }, "problemMatcher": [], }, { diff --git a/cmake/i686-w64-mingw32-gcc.cmake b/cmake/i686-w64-mingw32-gcc.cmake new file mode 100644 index 0000000..334d580 --- /dev/null +++ b/cmake/i686-w64-mingw32-gcc.cmake @@ -0,0 +1,7 @@ +set(CMAKE_SYSTEM_NAME Windows) + +# Some default GCC settings +set(CMAKE_C_COMPILER i686-w64-mingw32-gcc) +set(CMAKE_CXX_COMPILER i686-w64-mingw32-g++) + +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) diff --git a/cmake/x86_64-w64-mingw32-gcc.cmake b/cmake/x86_64-w64-mingw32-gcc.cmake new file mode 100644 index 0000000..1d82433 --- /dev/null +++ b/cmake/x86_64-w64-mingw32-gcc.cmake @@ -0,0 +1,7 @@ +set(CMAKE_SYSTEM_NAME Windows) + +# Some default GCC settings +set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc) +set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++) + +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) From be355afb6be00b64616d0cfcba2661942857b553 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Wed, 18 May 2022 23:54:43 +0200 Subject: [PATCH 56/77] Update vscode & CMake related points --- .vscode/launch.json | 2 +- .vscode/tasks.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index bf1450f..a53089a 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,7 +8,7 @@ "name": "(Windows) Launch", "type": "cppdbg", "request": "launch", - "program": "${command:cmake.buildDirectory}\\LwLibPROJECT.exe", + "program": "${command:cmake.launchTargetPath}", "miDebuggerPath": "c:\\msys64\\mingw64\\bin\\gdb.exe", "args": [], "stopAtEntry": false, diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 103e43a..b15064b 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -38,7 +38,7 @@ { "type": "shell", "label": "Run application", - "command": "${command:cmake.buildDirectory}\\LwLibPROJECT.exe", + "command": "${command:cmake.launchTargetPath}", "args": [], "problemMatcher": [], }, From 162fc2794ea700fcd9edc67c08d33d86d2157d20 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Tue, 24 May 2022 10:10:15 +0200 Subject: [PATCH 57/77] Add CMake Presets --- CMakePresets.json | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 CMakePresets.json diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..7d277c4 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,30 @@ +{ + "version": 3, + "configurePresets": [ + { + "name": "default", + "hidden": true, + "generator": "Ninja", + "binaryDir": "${sourceDir}/build/${presetName}", + "cacheVariables": { + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "Win32-Debug", + "inherits": "default", + "toolchainFile": "${sourceDir}/cmake/i686-w64-mingw32-gcc.cmake", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "Win64-Debug", + "inherits": "default", + "toolchainFile": "${sourceDir}/cmake/x86_64-w64-mingw32-gcc.cmake", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + } + ] +} \ No newline at end of file From a137bead33b021376e5dcde1533cbe7aa36d7809 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Wed, 27 Jul 2022 11:56:22 +0200 Subject: [PATCH 58/77] Reduce compiler warnings for -Wall -Wpedantic -Wextra --- CMakeLists.txt | 13 ++++++++++--- dev/main.c | 3 +++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a45545c..7647a65 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,12 +23,12 @@ target_sources(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_LIST_DIR}/examples/example_minimal.c ${CMAKE_CURRENT_LIST_DIR}/examples/example_traverse.c ${CMAKE_CURRENT_LIST_DIR}/examples/example_stream.c - ) +) # Add key include paths target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_LIST_DIR}/dev - ) +) # Compilation definition information target_compile_definitions(${PROJECT_NAME} PUBLIC @@ -36,7 +36,14 @@ target_compile_definitions(${PROJECT_NAME} PUBLIC _DEBUG CONSOLE LWJSON_DEV - ) +) + +# Compiler options +target_compile_options(${PROJECT_NAME} PRIVATE + -Wall + -Wextra + -Wpedantic +) # Add subdir with lwjson and link to project add_subdirectory("lwjson" lwjson) diff --git a/dev/main.c b/dev/main.c index b6e4088..c41b024 100644 --- a/dev/main.c +++ b/dev/main.c @@ -26,6 +26,8 @@ main() { char* json_text = NULL; const lwjson_token_t* tkn; + (void)token_cnt; + test_run(); example_minimal_run(); example_traverse_run(); @@ -101,6 +103,7 @@ main() { static void jsp_stream_callback(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { + (void)type; #if 0 if (jsp->stack_pos >= 4 && (type == LWJSON_STREAM_TYPE_STRING || type == LWJSON_STREAM_TYPE_NUMBER) From b91a35a53006bb68e061b19c98508af67d785379 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Tue, 16 Aug 2022 21:39:43 +0200 Subject: [PATCH 59/77] Update lwjson with macro --- lwjson/src/include/lwjson/lwjson.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lwjson/src/include/lwjson/lwjson.h b/lwjson/src/include/lwjson/lwjson.h index f09b278..2eb4fd9 100644 --- a/lwjson/src/include/lwjson/lwjson.h +++ b/lwjson/src/include/lwjson/lwjson.h @@ -140,17 +140,17 @@ void lwjson_print_json(const lwjson_t* lw); * \brief Object type for streaming parser */ typedef enum { - LWJSON_STREAM_TYPE_NONE, - LWJSON_STREAM_TYPE_OBJECT, - LWJSON_STREAM_TYPE_OBJECT_END, - LWJSON_STREAM_TYPE_ARRAY, - LWJSON_STREAM_TYPE_ARRAY_END, - LWJSON_STREAM_TYPE_KEY, - LWJSON_STREAM_TYPE_STRING, - LWJSON_STREAM_TYPE_TRUE, - LWJSON_STREAM_TYPE_FALSE, - LWJSON_STREAM_TYPE_NULL, - LWJSON_STREAM_TYPE_NUMBER, + LWJSON_STREAM_TYPE_NONE, /*!< No entry - not used */ + LWJSON_STREAM_TYPE_OBJECT, /*!< Object indication */ + LWJSON_STREAM_TYPE_OBJECT_END, /*!< Object end indication */ + LWJSON_STREAM_TYPE_ARRAY, /*!< Array indication */ + LWJSON_STREAM_TYPE_ARRAY_END, /*!< Array end indication */ + LWJSON_STREAM_TYPE_KEY, /*!< Key string */ + LWJSON_STREAM_TYPE_STRING, /*!< Strin type */ + LWJSON_STREAM_TYPE_TRUE, /*!< True primitive */ + LWJSON_STREAM_TYPE_FALSE, /*!< False primitive */ + LWJSON_STREAM_TYPE_NULL, /*!< Null primitive */ + LWJSON_STREAM_TYPE_NUMBER, /*!< Generic number */ } lwjson_stream_type_t; /** @@ -184,7 +184,7 @@ typedef void (*lwjson_stream_parser_callback_fn)(struct lwjson_stream_parser* js * \brief LwJSON streaming structure */ typedef struct lwjson_stream_parser { - lwjson_stream_stack_t stack[16]; /*!< Stack used for parsing. TODO: Add conditional compilation flag */ + lwjson_stream_stack_t stack[LWJSON_CFG_STREAM_STACK_SIZE]; /*!< Stack used for parsing */ size_t stack_pos; /*!< Current stack position */ lwjson_stream_state_t parse_state; /*!< Parser state */ From c09c185139b74612748fa1c3b0586185a23b962e Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Tue, 16 Aug 2022 21:55:57 +0200 Subject: [PATCH 60/77] correct return message --- lwjson/src/lwjson/lwjson_stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index 55bfce8..3fad8ba 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -200,7 +200,7 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { /* Now remove the array or object from stack */ if (!prv_stack_pop(jsp)) { - return lwjsonERRMEM; + return lwjsonERRJSON; } /* From c37e20df9ceef46a16161383f3d2d61ad06cecf8 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Wed, 17 Aug 2022 21:13:15 +0200 Subject: [PATCH 61/77] Fix potential future bug --- lwjson/src/lwjson/lwjson_stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index 3fad8ba..16f97b1 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -199,7 +199,7 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { } /* Now remove the array or object from stack */ - if (!prv_stack_pop(jsp)) { + if (prv_stack_pop(jsp) == LWJSON_STREAM_TYPE_NONE) { return lwjsonERRJSON; } From 6f6c62bec3172fb0fec11f02b4af96538f08063f Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Wed, 17 Aug 2022 21:24:58 +0200 Subject: [PATCH 62/77] Update C/C++ properties --- .vscode/c_cpp_properties.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 9768bc2..4f457d1 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -2,11 +2,12 @@ "version": 4, "configurations": [ { + /* + * Full configuration is provided by CMake plugin for vscode, + * that shall be installed by user + */ "name": "Win32", - "compilerPath": "c:\\msys64\\mingw64\\bin\\gcc.exe", - "cStandard": "gnu17", - "cppStandard": "gnu++14", - "intelliSenseMode": "windows-gcc-x86", + "intelliSenseMode": "${default}", "configurationProvider": "ms-vscode.cmake-tools" } ] From d691c7c7b09585229c3fac1dec73e8dba13a1ba5 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sun, 21 Aug 2022 16:50:07 +0200 Subject: [PATCH 63/77] Update docs --- docs/changelog/index.rst | 6 ++++++ docs/index.rst | 1 + 2 files changed, 7 insertions(+) create mode 100644 docs/changelog/index.rst diff --git a/docs/changelog/index.rst b/docs/changelog/index.rst new file mode 100644 index 0000000..51b89b4 --- /dev/null +++ b/docs/changelog/index.rst @@ -0,0 +1,6 @@ +.. _changelof: + +Changelog +========= + +.. literalinclude:: ../../CHANGELOG.md diff --git a/docs/index.rst b/docs/index.rst index 12df122..91c286f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -72,6 +72,7 @@ Table of contents get-started/index user-manual/index api-reference/index + changelog/index .. toctree:: :maxdepth: 2 From 804e29147d8f6d7189eb8c8026fed3b2ee190a3a Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Tue, 30 Aug 2022 22:19:38 +0200 Subject: [PATCH 64/77] Add .clang-format first file --- .clang-format | 199 ++++++++++++++++++++++++++++++++++++++++++++++++++ CHANGELOG.md | 1 + 2 files changed, 200 insertions(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..7f92b4a --- /dev/null +++ b/.clang-format @@ -0,0 +1,199 @@ +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveMacros: + Enabled: true + AcrossEmptyLines: true + AcrossComments: true +AlignConsecutiveAssignments: None +AlignConsecutiveBitFields: + Enabled: true + AcrossEmptyLines: true + AcrossComments: true +AlignConsecutiveDeclarations: None +AlignEscapedNewlines: Right +AlignOperands: Align +InsertBraces: true # Control statements must have curly brackets +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortEnumsOnASingleLine: true +AllowShortBlocksOnASingleLine: Empty +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: AllDefinitions +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeConceptDeclarations: true +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 120 +CommentPragmas: '^ IWYU pragma:' +QualifierAlignment: Leave +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +PackConstructorInitializers: BinPack +BasedOnStyle: '' +ConstructorInitializerAllOnOneLineOrOnePerLine: false +AllowAllConstructorInitializersOnNextLine: true +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseLabels: true +IndentCaseBlocks: false +IndentGotoLabels: true +IndentPPDirectives: None +IndentExternBlock: AfterExternBlock +IndentRequires: true +IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertTrailingCommas: None +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +LambdaBodyIndentation: Signature +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PenaltyIndentedWhitespace: 0 +PointerAlignment: Left +PPIndentWidth: -1 +ReferenceAlignment: Pointer +ReflowComments: false +RemoveBracesLLVM: false +SeparateDefinitionBlocks: Always +ShortNamespaceLines: 1 +SortIncludes: CaseSensitive +SortJavaStaticImport: Before +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: true + AfterOverloadedOperator: false + BeforeNonEmptyParentheses: false +SpaceAroundPointerQualifiers: Default +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +BitFieldColonSpacing: Both +Standard: Latest +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseCRLF: false +UseTab: Never +WhitespaceSensitiveMacros: + - STRINGIZE + - PP_STRINGIZE + - BOOST_PP_STRINGIZE + - NS_SWIFT_NAME + - CF_SWIFT_NAME +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +... + diff --git a/CHANGELOG.md b/CHANGELOG.md index e3b0eac..ce99c92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Fix GCC warning for incompatible comparison types - Update code style with astyle - Add support for stream parsing - first version +- Add `.clang-format` draft ## 1.5.0 From 708fa284c1e577145306c1659bf5e88db1854961 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Thu, 1 Sep 2022 19:05:40 +0200 Subject: [PATCH 65/77] Apply clang-format --- .clang-format | 18 +-- lwjson/src/include/lwjson/lwjson.h | 180 ++++++++++++++----------- lwjson/src/include/lwjson/lwjson_opt.h | 14 +- lwjson/src/lwjson/lwjson.c | 51 ++++--- lwjson/src/lwjson/lwjson_debug.c | 10 +- lwjson/src/lwjson/lwjson_stream.c | 91 +++++++------ 6 files changed, 189 insertions(+), 175 deletions(-) diff --git a/.clang-format b/.clang-format index 7f92b4a..490ba55 100644 --- a/.clang-format +++ b/.clang-format @@ -30,7 +30,7 @@ AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: AllDefinitions AlwaysBreakBeforeMultilineStrings: false -AlwaysBreakTemplateDeclarations: MultiLine +AlwaysBreakTemplateDeclarations: Yes AttributeMacros: - __capability BinPackArguments: true @@ -90,18 +90,12 @@ IfMacros: - KJ_IF_MAYBE IncludeBlocks: Preserve IncludeCategories: - - Regex: '^"(llvm|llvm-c|clang|clang-c)/' - Priority: 2 - SortPriority: 0 - CaseSensitive: false - - Regex: '^(<|"(gtest|gmock|isl|json)/)' - Priority: 3 - SortPriority: 0 - CaseSensitive: false - - Regex: '.*' + - Regex: '^<(.*)>' + Priority: 0 + - Regex: '^"(.*)"' Priority: 1 - SortPriority: 0 - CaseSensitive: false + - Regex: '(.*)' + Priority: 2 IncludeIsMainRegex: '(Test)?$' IncludeIsMainSourceRegex: '' IndentAccessModifiers: false diff --git a/lwjson/src/include/lwjson/lwjson.h b/lwjson/src/include/lwjson/lwjson.h index 2eb4fd9..c371be4 100644 --- a/lwjson/src/include/lwjson/lwjson.h +++ b/lwjson/src/include/lwjson/lwjson.h @@ -34,8 +34,8 @@ #ifndef LWJSON_HDR_H #define LWJSON_HDR_H -#include #include +#include #include "lwjson/lwjson_opt.h" #ifdef __cplusplus @@ -53,20 +53,20 @@ extern "C" { * \param[in] x: Object to get array size of * \return Number of elements in array */ -#define LWJSON_ARRAYSIZE(x) (sizeof(x) / sizeof((x)[0])) +#define LWJSON_ARRAYSIZE(x) (sizeof(x) / sizeof((x)[0])) /** * \brief List of supported JSON types */ typedef enum { - LWJSON_TYPE_STRING, /*!< String/Text format. Everything that has beginning and ending quote character */ - LWJSON_TYPE_NUM_INT, /*!< Number type for integer */ - LWJSON_TYPE_NUM_REAL, /*!< Number type for real number */ - LWJSON_TYPE_OBJECT, /*!< Object data type */ - LWJSON_TYPE_ARRAY, /*!< Array data type */ - LWJSON_TYPE_TRUE, /*!< True boolean value */ - LWJSON_TYPE_FALSE, /*!< False boolean value */ - LWJSON_TYPE_NULL, /*!< Null value */ + LWJSON_TYPE_STRING, /*!< String/Text format. Everything that has beginning and ending quote character */ + LWJSON_TYPE_NUM_INT, /*!< Number type for integer */ + LWJSON_TYPE_NUM_REAL, /*!< Number type for real number */ + LWJSON_TYPE_OBJECT, /*!< Object data type */ + LWJSON_TYPE_ARRAY, /*!< Array data type */ + LWJSON_TYPE_TRUE, /*!< True boolean value */ + LWJSON_TYPE_FALSE, /*!< False boolean value */ + LWJSON_TYPE_NULL, /*!< Null value */ } lwjson_type_t; /** @@ -83,92 +83,98 @@ typedef LWJSON_CFG_INT_TYPE lwjson_int_t; * \brief JSON token */ typedef struct lwjson_token { - struct lwjson_token* next; /*!< Next token on a list */ - lwjson_type_t type; /*!< Token type */ - const char* token_name; /*!< Token name (if exists) */ - size_t token_name_len; /*!< Length of token name (this is needed to support const input strings to parse) */ + struct lwjson_token* next; /*!< Next token on a list */ + lwjson_type_t type; /*!< Token type */ + const char* token_name; /*!< Token name (if exists) */ + size_t token_name_len; /*!< Length of token name (this is needed to support const input strings to parse) */ + union { struct { - const char* token_value; /*!< Pointer to the beginning of the string */ - size_t token_value_len; /*!< Length of token value (this is needed to support const input strings to parse) */ - } str; /*!< String data */ - lwjson_real_t num_real; /*!< Real number format */ - lwjson_int_t num_int; /*!< Int number format */ - struct lwjson_token* first_child; /*!< First children object for object or array type */ - } u; /*!< Union with different data types */ + const char* token_value; /*!< Pointer to the beginning of the string */ + size_t + token_value_len; /*!< Length of token value (this is needed to support const input strings to parse) */ + } str; /*!< String data */ + + lwjson_real_t num_real; /*!< Real number format */ + lwjson_int_t num_int; /*!< Int number format */ + struct lwjson_token* first_child; /*!< First children object for object or array type */ + } u; /*!< Union with different data types */ } lwjson_token_t; /** * \brief JSON result enumeration */ typedef enum { - lwjsonOK = 0x00, /*!< Function returns successfully */ - lwjsonERR, /*!< Generic error message */ - lwjsonERRJSON, /*!< Error JSON format */ - lwjsonERRMEM, /*!< Memory error */ - lwjsonERRPAR, /*!< Parameter error */ - - lwjsonSTREAMNONE, /*!< No new info to process - parsing in progress */ - lwjsonSTREAMINPROGRESS, /*!< Stream token parsing is in progress */ - lwjsonSTREAMDONE, /*!< Streaming parser is done */ + lwjsonOK = 0x00, /*!< Function returns successfully */ + lwjsonERR, /*!< Generic error message */ + lwjsonERRJSON, /*!< Error JSON format */ + lwjsonERRMEM, /*!< Memory error */ + lwjsonERRPAR, /*!< Parameter error */ + + lwjsonSTREAMNONE, /*!< No new info to process - parsing in progress */ + lwjsonSTREAMINPROGRESS, /*!< Stream token parsing is in progress */ + lwjsonSTREAMDONE, /*!< Streaming parser is done */ } lwjsonr_t; /** * \brief LwJSON instance */ typedef struct { - lwjson_token_t* tokens; /*!< Pointer to array of tokens */ - size_t tokens_len; /*!< Size of all tokens */ - size_t next_free_token_pos; /*!< Position of next free token instance */ - lwjson_token_t first_token; /*!< First token on a list */ + lwjson_token_t* tokens; /*!< Pointer to array of tokens */ + size_t tokens_len; /*!< Size of all tokens */ + size_t next_free_token_pos; /*!< Position of next free token instance */ + lwjson_token_t first_token; /*!< First token on a list */ + struct { - uint8_t parsed : 1; /*!< Flag indicating JSON parsing has finished successfully */ - } flags; /*!< List of flags */ + uint8_t parsed : 1; /*!< Flag indicating JSON parsing has finished successfully */ + } flags; /*!< List of flags */ } lwjson_t; -lwjsonr_t lwjson_init(lwjson_t* lw, lwjson_token_t* tokens, size_t tokens_len); -lwjsonr_t lwjson_parse_ex(lwjson_t* lw, const void* json_data, size_t len); -lwjsonr_t lwjson_parse(lwjson_t* lw, const char* json_str); -const lwjson_token_t* lwjson_find(lwjson_t* lw, const char* path); -const lwjson_token_t* lwjson_find_ex(lwjson_t* lw, const lwjson_token_t* token, const char* path); -lwjsonr_t lwjson_free(lwjson_t* lw); +lwjsonr_t lwjson_init(lwjson_t* lw, lwjson_token_t* tokens, size_t tokens_len); +lwjsonr_t lwjson_parse_ex(lwjson_t* lw, const void* json_data, size_t len); +lwjsonr_t lwjson_parse(lwjson_t* lw, const char* json_str); +const lwjson_token_t* lwjson_find(lwjson_t* lw, const char* path); +const lwjson_token_t* lwjson_find_ex(lwjson_t* lw, const lwjson_token_t* token, const char* path); +lwjsonr_t lwjson_free(lwjson_t* lw); -void lwjson_print_token(const lwjson_token_t* token); -void lwjson_print_json(const lwjson_t* lw); +void lwjson_print_token(const lwjson_token_t* token); +void lwjson_print_json(const lwjson_t* lw); /** * \brief Object type for streaming parser */ typedef enum { - LWJSON_STREAM_TYPE_NONE, /*!< No entry - not used */ - LWJSON_STREAM_TYPE_OBJECT, /*!< Object indication */ - LWJSON_STREAM_TYPE_OBJECT_END, /*!< Object end indication */ - LWJSON_STREAM_TYPE_ARRAY, /*!< Array indication */ - LWJSON_STREAM_TYPE_ARRAY_END, /*!< Array end indication */ - LWJSON_STREAM_TYPE_KEY, /*!< Key string */ - LWJSON_STREAM_TYPE_STRING, /*!< Strin type */ - LWJSON_STREAM_TYPE_TRUE, /*!< True primitive */ - LWJSON_STREAM_TYPE_FALSE, /*!< False primitive */ - LWJSON_STREAM_TYPE_NULL, /*!< Null primitive */ - LWJSON_STREAM_TYPE_NUMBER, /*!< Generic number */ + LWJSON_STREAM_TYPE_NONE, /*!< No entry - not used */ + LWJSON_STREAM_TYPE_OBJECT, /*!< Object indication */ + LWJSON_STREAM_TYPE_OBJECT_END, /*!< Object end indication */ + LWJSON_STREAM_TYPE_ARRAY, /*!< Array indication */ + LWJSON_STREAM_TYPE_ARRAY_END, /*!< Array end indication */ + LWJSON_STREAM_TYPE_KEY, /*!< Key string */ + LWJSON_STREAM_TYPE_STRING, /*!< Strin type */ + LWJSON_STREAM_TYPE_TRUE, /*!< True primitive */ + LWJSON_STREAM_TYPE_FALSE, /*!< False primitive */ + LWJSON_STREAM_TYPE_NULL, /*!< Null primitive */ + LWJSON_STREAM_TYPE_NUMBER, /*!< Generic number */ } lwjson_stream_type_t; /** * \brief Stream parsing stack object */ typedef struct { - lwjson_stream_type_t type; /*!< Streaming type - current value */ - union { - char name[LWJSON_CFG_STREAM_KEY_MAX_LEN + 1]; /*!< Last known key name, used only for \ref LWJSON_STREAM_TYPE_KEY type */ - uint16_t index; /*!< Current index when type is an array */ - } meta; /*!< Meta information */ + lwjson_stream_type_t type; /*!< Streaming type - current value */ + + union { + char name[LWJSON_CFG_STREAM_KEY_MAX_LEN + + 1]; /*!< Last known key name, used only for \ref LWJSON_STREAM_TYPE_KEY type */ + uint16_t index; /*!< Current index when type is an array */ + } meta; /*!< Meta information */ } lwjson_stream_stack_t; typedef enum { - LWJSON_STREAM_STATE_WAITINGFIRSTCHAR = 0x00,/*!< State to wait for very first opening character */ - LWJSON_STREAM_STATE_PARSING, /*!< In parsing of the first char state - detecting next character state */ - LWJSON_STREAM_STATE_PARSING_STRING, /*!< Parse string primitive */ - LWJSON_STREAM_STATE_PARSING_PRIMITIVE, /*!< Parse any primitive that is non-string, either "true", "false", "null" or a number */ + LWJSON_STREAM_STATE_WAITINGFIRSTCHAR = 0x00, /*!< State to wait for very first opening character */ + LWJSON_STREAM_STATE_PARSING, /*!< In parsing of the first char state - detecting next character state */ + LWJSON_STREAM_STATE_PARSING_STRING, /*!< Parse string primitive */ + LWJSON_STREAM_STATE_PARSING_PRIMITIVE, /*!< Parse any primitive that is non-string, either "true", "false", "null" or a number */ } lwjson_stream_state_t; /* Forward declaration */ @@ -184,26 +190,30 @@ typedef void (*lwjson_stream_parser_callback_fn)(struct lwjson_stream_parser* js * \brief LwJSON streaming structure */ typedef struct lwjson_stream_parser { - lwjson_stream_stack_t stack[LWJSON_CFG_STREAM_STACK_SIZE]; /*!< Stack used for parsing */ - size_t stack_pos; /*!< Current stack position */ - - lwjson_stream_state_t parse_state; /*!< Parser state */ + lwjson_stream_stack_t stack[LWJSON_CFG_STREAM_STACK_SIZE]; /*!< Stack used for parsing */ + size_t stack_pos; /*!< Current stack position */ + + lwjson_stream_state_t parse_state; /*!< Parser state */ - lwjson_stream_parser_callback_fn evt_fn; /*!< Event function for user */ + lwjson_stream_parser_callback_fn evt_fn; /*!< Event function for user */ /* State */ union { struct { - char buff[LWJSON_CFG_STREAM_STRING_MAX_LEN + 1]; /*!< Buffer to write temporary data. TODO: Size to be variable with define */ - size_t buff_pos; /*!< Buffer position for next write (length of bytes in buffer) */ - } str; /*!< String structure */ + char buff[LWJSON_CFG_STREAM_STRING_MAX_LEN + + 1]; /*!< Buffer to write temporary data. TODO: Size to be variable with define */ + size_t buff_pos; /*!< Buffer position for next write (length of bytes in buffer) */ + } str; /*!< String structure */ + struct { char buff[LWJSON_CFG_STREAM_PRIMITIVE_MAX_LEN + 1]; /*!< Temporary write buffer */ - size_t buff_pos; /*!< Buffer position for next write */ - } prim; /*!< Primitive object */ + size_t buff_pos; /*!< Buffer position for next write */ + } prim; /*!< Primitive object */ + /* Todo: Add other types */ - } data; /*!< Data union used to parse various */ - char prev_c; /*!< History of characters */ + } data; /*!< Data union used to parse various */ + + char prev_c; /*!< History of characters */ } lwjson_stream_parser_t; lwjsonr_t lwjson_stream_init(lwjson_stream_parser_t* jsp, lwjson_stream_parser_callback_fn evt_fn); @@ -214,35 +224,40 @@ lwjsonr_t lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c); * \param[in] lw: Pointer to LwJSON instance * \return Number of tokens used to parse JSON */ -#define lwjson_get_tokens_used(lw) (((lw) != NULL) ? ((lw)->next_free_token_pos + 1) : 0) +#define lwjson_get_tokens_used(lw) (((lw) != NULL) ? ((lw)->next_free_token_pos + 1) : 0) /** * \brief Get very first token of LwJSON instance * \param[in] lw: Pointer to LwJSON instance * \return Pointer to first token */ -#define lwjson_get_first_token(lw) (((lw) != NULL) ? (&(lw)->first_token) : NULL) +#define lwjson_get_first_token(lw) (((lw) != NULL) ? (&(lw)->first_token) : NULL) /** * \brief Get token value for \ref LWJSON_TYPE_NUM_INT type * \param[in] token: token with integer type * \return Int number if type is integer, `0` otherwise */ -#define lwjson_get_val_int(token) ((lwjson_int_t)(((token) != NULL && (token)->type == LWJSON_TYPE_NUM_INT) ? (token)->u.num_int : 0)) +#define lwjson_get_val_int(token) \ + ((lwjson_int_t)(((token) != NULL && (token)->type == LWJSON_TYPE_NUM_INT) ? (token)->u.num_int : 0)) /** * \brief Get token value for \ref LWJSON_TYPE_NUM_REAL type * \param[in] token: token with real type * \return Real numbeer if type is real, `0` otherwise */ -#define lwjson_get_val_real(token) ((lwjson_real_t)(((token) != NULL && (token)->type == LWJSON_TYPE_NUM_REAL) ? (token)->u.num_real : 0)) +#define lwjson_get_val_real(token) \ + ((lwjson_real_t)(((token) != NULL && (token)->type == LWJSON_TYPE_NUM_REAL) ? (token)->u.num_real : 0)) /** * \brief Get first child token for \ref LWJSON_TYPE_OBJECT or \ref LWJSON_TYPE_ARRAY types * \param[in] token: token with integer type * \return Pointer to first child or `NULL` if parent token is not object or array */ -#define lwjson_get_first_child(token) (const void *)(((token) != NULL && ((token)->type == LWJSON_TYPE_OBJECT || (token)->type == LWJSON_TYPE_ARRAY)) ? (token)->u.first_child : NULL) +#define lwjson_get_first_child(token) \ + (const void*)(((token) != NULL && ((token)->type == LWJSON_TYPE_OBJECT || (token)->type == LWJSON_TYPE_ARRAY)) \ + ? (token)->u.first_child \ + : NULL) /** * \brief Get string value from JSON token @@ -267,7 +282,8 @@ lwjson_get_val_string(const lwjson_token_t* token, size_t* str_len) { * \param[in] token: token with string type * \return Length of string in units of bytes */ -#define lwjson_get_val_string_length(token) ((size_t)(((token) != NULL && (token)->type == LWJSON_TYPE_STRING) ? (token)->u.str.token_value_len : 0)) +#define lwjson_get_val_string_length(token) \ + ((size_t)(((token) != NULL && (token)->type == LWJSON_TYPE_STRING) ? (token)->u.str.token_value_len : 0)) /** * \brief Compare string token with user input string for a case-sensitive match diff --git a/lwjson/src/include/lwjson/lwjson_opt.h b/lwjson/src/include/lwjson/lwjson_opt.h index 6a901b3..ebb8e6c 100644 --- a/lwjson/src/include/lwjson/lwjson_opt.h +++ b/lwjson/src/include/lwjson/lwjson_opt.h @@ -59,7 +59,7 @@ extern "C" { * This is used for numbers in \ref LWJSON_TYPE_NUM_REAL token data type. */ #ifndef LWJSON_CFG_REAL_TYPE -#define LWJSON_CFG_REAL_TYPE float +#define LWJSON_CFG_REAL_TYPE float #endif /** @@ -69,7 +69,7 @@ extern "C" { * This is used for numbers in \ref LWJSON_TYPE_NUM_INT token data type. */ #ifndef LWJSON_CFG_INT_TYPE -#define LWJSON_CFG_INT_TYPE long long +#define LWJSON_CFG_INT_TYPE long long #endif /** @@ -78,7 +78,7 @@ extern "C" { * Default set to `0` to be JSON compliant */ #ifndef LWJSON_CFG_COMMENTS -#define LWJSON_CFG_COMMENTS 0 +#define LWJSON_CFG_COMMENTS 0 #endif /** @@ -92,7 +92,7 @@ extern "C" { * */ #ifndef LWJSON_CFG_STREAM_KEY_MAX_LEN -#define LWJSON_CFG_STREAM_KEY_MAX_LEN 32 +#define LWJSON_CFG_STREAM_KEY_MAX_LEN 32 #endif /** @@ -100,7 +100,7 @@ extern "C" { * */ #ifndef LWJSON_CFG_STREAM_STACK_SIZE -#define LWJSON_CFG_STREAM_STACK_SIZE 16 +#define LWJSON_CFG_STREAM_STACK_SIZE 16 #endif /** @@ -108,7 +108,7 @@ extern "C" { * */ #ifndef LWJSON_CFG_STREAM_STRING_MAX_LEN -#define LWJSON_CFG_STREAM_STRING_MAX_LEN 256 +#define LWJSON_CFG_STREAM_STRING_MAX_LEN 256 #endif /** @@ -117,7 +117,7 @@ extern "C" { * Primitives are all numbers and logical values (null, true, false) */ #ifndef LWJSON_CFG_STREAM_PRIMITIVE_MAX_LEN -#define LWJSON_CFG_STREAM_PRIMITIVE_MAX_LEN 32 +#define LWJSON_CFG_STREAM_PRIMITIVE_MAX_LEN 32 #endif /** diff --git a/lwjson/src/lwjson/lwjson.c b/lwjson/src/lwjson/lwjson.c index 97b8b04..ffde382 100644 --- a/lwjson/src/lwjson/lwjson.c +++ b/lwjson/src/lwjson/lwjson.c @@ -38,9 +38,9 @@ * \brief Internal string object */ typedef struct { - const char* start; /*!< Original pointer to beginning of JSON object */ - size_t len; /*!< Total length of input json string */ - const char* p; /*!< Current char pointer */ + const char* start; /*!< Original pointer to beginning of JSON object */ + size_t len; /*!< Total length of input json string */ + const char* p; /*!< Current char pointer */ } lwjson_int_str_t; /** @@ -137,8 +137,7 @@ prv_parse_string(lwjson_int_str_t* pobj, const char** pout, size_t* poutlen) { case 'u': ++pobj->p; for (size_t i = 0; i < 4; ++i, ++len) { - if (!((*pobj->p >= '0' && *pobj->p <= '9') - || (*pobj->p >= 'a' && *pobj->p <= 'f') + if (!((*pobj->p >= '0' && *pobj->p <= '9') || (*pobj->p >= 'a' && *pobj->p <= 'f') || (*pobj->p >= 'A' && *pobj->p <= 'F'))) { return lwjsonERRJSON; } @@ -211,9 +210,10 @@ prv_parse_number(lwjson_int_str_t* pobj, lwjson_type_t* tout, lwjson_real_t* fou return lwjsonERRJSON; } is_minus = *pobj->p == '-' ? (++pobj->p, 1) : 0; - if (*pobj->p == '\0' /* Invalid string */ - || *pobj->p < '0' || *pobj->p > '9' /* Character outside number range */ - || (*pobj->p == '0' && (pobj->p[1] < '0' && pobj->p[1] > '9'))) { /* Number starts with 0 but not followed by dot */ + if (*pobj->p == '\0' /* Invalid string */ + || *pobj->p < '0' || *pobj->p > '9' /* Character outside number range */ + || (*pobj->p == '0' + && (pobj->p[1] < '0' && pobj->p[1] > '9'))) { /* Number starts with 0 but not followed by dot */ return lwjsonERRJSON; } @@ -221,7 +221,7 @@ prv_parse_number(lwjson_int_str_t* pobj, lwjson_type_t* tout, lwjson_real_t* fou for (num = 0; *pobj->p >= '0' && *pobj->p <= '9'; ++pobj->p) { num = num * 10 + (*pobj->p - '0'); } - if (pobj->p != NULL && *pobj->p == '.') { /* Number has exponent */ + if (pobj->p != NULL && *pobj->p == '.') { /* Number has exponent */ lwjson_real_t exp, dec_num; type = LWJSON_TYPE_NUM_REAL; /* Format is real */ @@ -233,16 +233,16 @@ prv_parse_number(lwjson_int_str_t* pobj, lwjson_type_t* tout, lwjson_real_t* fou for (exp = 1, dec_num = 0; *pobj->p >= '0' && *pobj->p <= '9'; ++pobj->p, exp *= 10) { dec_num = dec_num * 10 + (*pobj->p - '0'); } - num += dec_num / exp; /* Add decimal part to number */ + num += dec_num / exp; /* Add decimal part to number */ } - if (pobj->p != NULL && (*pobj->p == 'e' || *pobj->p == 'E')) { /* Engineering mode */ + if (pobj->p != NULL && (*pobj->p == 'e' || *pobj->p == 'E')) { /* Engineering mode */ uint8_t is_minus_exp; int exp_cnt; - type = LWJSON_TYPE_NUM_REAL; /* Format is real */ - ++pobj->p; /* Ignore enginnering sing part */ - is_minus_exp = *pobj->p == '-' ? (++pobj->p, 1) : 0;/* Check if negative */ - if (*pobj->p == '+') { /* Optional '+' is possible too */ + type = LWJSON_TYPE_NUM_REAL; /* Format is real */ + ++pobj->p; /* Ignore enginnering sing part */ + is_minus_exp = *pobj->p == '-' ? (++pobj->p, 1) : 0; /* Check if negative */ + if (*pobj->p == '+') { /* Optional '+' is possible too */ ++pobj->p; } if (*pobj->p < '0' || *pobj->p > '9') { /* Must be followed by number characters */ @@ -377,7 +377,7 @@ prv_find(const lwjson_token_t* parent, const char* path) { } /* Scan all indexes and get first match */ - for (const lwjson_token_t* tmp_t, *t = parent->u.first_child; t != NULL; t = t->next) { + for (const lwjson_token_t *tmp_t, *t = parent->u.first_child; t != NULL; t = t->next) { if ((tmp_t = prv_find(t, path)) != NULL) { return tmp_t; } @@ -416,9 +416,7 @@ prv_check_valid_char_after_open_bracket(lwjson_int_str_t* pobj, lwjson_token_t* if ((res = prv_skip_blank(pobj)) != lwjsonOK) { return res; } - if (*pobj->p == '\0' - || (t->type == LWJSON_TYPE_OBJECT - && (*pobj->p != '"' && *pobj->p != '}')) + if (*pobj->p == '\0' || (t->type == LWJSON_TYPE_OBJECT && (*pobj->p != '"' && *pobj->p != '}')) || (t->type == LWJSON_TYPE_ARRAY && (*pobj->p != '"' && *pobj->p != ']' && *pobj->p != '[' && *pobj->p != '{' && *pobj->p != '-' && (*pobj->p < '0' || *pobj->p > '9') && *pobj->p != 't' && *pobj->p != 'n' && *pobj->p != 'f'))) { @@ -455,12 +453,8 @@ lwjson_init(lwjson_t* lw, lwjson_token_t* tokens, size_t tokens_len) { lwjsonr_t lwjson_parse_ex(lwjson_t* lw, const void* json_data, size_t json_len) { lwjsonr_t res = lwjsonOK; - lwjson_token_t* t, *to; - lwjson_int_str_t pobj = { - .start = json_data, - .len = json_len, - .p = json_data - }; + lwjson_token_t *t, *to; + lwjson_int_str_t pobj = {.start = json_data, .len = json_len, .p = json_data}; /* Check input parameters */ if (lw == NULL || json_data == NULL || json_len == 0) { @@ -513,7 +507,8 @@ lwjson_parse_ex(lwjson_t* lw, const void* json_data, size_t json_len) { /* End of string if to == NULL (no parent), check if properly terminated */ if ((to = parent) == NULL) { prv_skip_blank(&pobj); - res = (pobj.p == NULL || *pobj.p == '\0' || (size_t)(pobj.p - pobj.start) == pobj.len) ? lwjsonOK : lwjsonERR; + res = (pobj.p == NULL || *pobj.p == '\0' || (size_t)(pobj.p - pobj.start) == pobj.len) ? lwjsonOK + : lwjsonERR; goto ret; } continue; @@ -554,7 +549,7 @@ lwjson_parse_ex(lwjson_t* lw, const void* json_data, size_t json_len) { if ((res = prv_check_valid_char_after_open_bracket(&pobj, t)) != lwjsonOK) { goto ret; } - t->next = to; /* Temporary saved as parent object */ + t->next = to; /* Temporary saved as parent object */ to = t; break; case '"': @@ -628,7 +623,7 @@ lwjson_parse_ex(lwjson_t* lw, const void* json_data, size_t json_len) { if (pobj.p == NULL || *pobj.p == '\0' || (*pobj.p != ',' && *pobj.p != ']' && *pobj.p != '}')) { res = lwjsonERRJSON; goto ret; - } else if (*pobj.p == ',') { /* Check to advance to next token immediatey */ + } else if (*pobj.p == ',') { /* Check to advance to next token immediatey */ ++pobj.p; } } diff --git a/lwjson/src/lwjson/lwjson_debug.c b/lwjson/src/lwjson/lwjson_debug.c index f237e92..252bd71 100644 --- a/lwjson/src/lwjson/lwjson_debug.c +++ b/lwjson/src/lwjson/lwjson_debug.c @@ -31,15 +31,15 @@ * Author: Tilen MAJERLE * Version: v1.5.0 */ -#include #include +#include #include "lwjson/lwjson.h" /** * \brief Token print instance */ typedef struct { - size_t indent; /*!< Indent level for token print */ + size_t indent; /*!< Indent level for token print */ } lwjson_token_print_t; /** @@ -49,7 +49,7 @@ typedef struct { */ static void prv_print_token(lwjson_token_print_t* p, const lwjson_token_t* token) { -#define print_indent() printf("%.*s", (int)((p->indent)), "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"); +#define print_indent() printf("%.*s", (int)((p->indent)), "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"); if (token == NULL) { return; @@ -118,7 +118,7 @@ prv_print_token(lwjson_token_print_t* p, const lwjson_token_t* token) { */ void lwjson_print_token(const lwjson_token_t* token) { - lwjson_token_print_t p = { 0 }; + lwjson_token_print_t p = {0}; prv_print_token(&p, token); } @@ -129,6 +129,6 @@ lwjson_print_token(const lwjson_token_t* token) { */ void lwjson_print_json(const lwjson_t* lw) { - lwjson_token_print_t p = { 0 }; + lwjson_token_print_t p = {0}; prv_print_token(&p, lwjson_get_first_token(lw)); } diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index 16f97b1..527886c 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -36,40 +36,45 @@ #if defined(LWJSON_DEV) #include -#define DEBUG_STRING_PREFIX_SPACES " " -#define LWJSON_DEBUG(jsp, ...) do { \ - if ((jsp) != NULL) { \ - printf("%.*s", (int)(4 * (jsp)->stack_pos), DEBUG_STRING_PREFIX_SPACES); \ - } \ - printf(__VA_ARGS__); \ -} while (0) +#define DEBUG_STRING_PREFIX_SPACES \ + " " +#define LWJSON_DEBUG(jsp, ...) \ + do { \ + if ((jsp) != NULL) { \ + printf("%.*s", (int)(4 * (jsp)->stack_pos), DEBUG_STRING_PREFIX_SPACES); \ + } \ + printf(__VA_ARGS__); \ + } while (0) /* Strings for debug */ static const char* type_strings[] = { - [LWJSON_STREAM_TYPE_NONE] = "none", - [LWJSON_STREAM_TYPE_OBJECT] = "object", + [LWJSON_STREAM_TYPE_NONE] = "none", + [LWJSON_STREAM_TYPE_OBJECT] = "object", [LWJSON_STREAM_TYPE_OBJECT_END] = "object_end", - [LWJSON_STREAM_TYPE_ARRAY] = "array", - [LWJSON_STREAM_TYPE_ARRAY_END] = "array_end", - [LWJSON_STREAM_TYPE_KEY] = "key", - [LWJSON_STREAM_TYPE_STRING] = "string", - [LWJSON_STREAM_TYPE_TRUE] = "true", - [LWJSON_STREAM_TYPE_FALSE] = "false", - [LWJSON_STREAM_TYPE_NULL] = "null", - [LWJSON_STREAM_TYPE_NUMBER] = "number", + [LWJSON_STREAM_TYPE_ARRAY] = "array", + [LWJSON_STREAM_TYPE_ARRAY_END] = "array_end", + [LWJSON_STREAM_TYPE_KEY] = "key", + [LWJSON_STREAM_TYPE_STRING] = "string", + [LWJSON_STREAM_TYPE_TRUE] = "true", + [LWJSON_STREAM_TYPE_FALSE] = "false", + [LWJSON_STREAM_TYPE_NULL] = "null", + [LWJSON_STREAM_TYPE_NUMBER] = "number", }; #else -#define LWJSON_DEBUG(jsp, ...) +#define LWJSON_DEBUG(jsp, ...) #endif /* defined(LWJSON_DEV) */ -#define SEND_EVT(jsp, type) if ((jsp) != NULL && (jsp)->evt_fn != NULL) { (jsp)->evt_fn((jsp), (type)); } +#define SEND_EVT(jsp, type) \ + if ((jsp) != NULL && (jsp)->evt_fn != NULL) { \ + (jsp)->evt_fn((jsp), (type)); \ + } /** * \brief Check if character is a space character (with extended chars) * \param[in] c: Character to check * \return `1` if considered extended space, `0` otherwise */ -#define prv_is_space_char_ext(c) ((c) == ' ' || (c) == '\t' || (c) == '\r' || (c) == '\n' || (c) == '\f') +#define prv_is_space_char_ext(c) ((c) == ' ' || (c) == '\t' || (c) == '\r' || (c) == '\n' || (c) == '\f') /** * \brief Push "parent" state to the artificial stack @@ -145,8 +150,7 @@ lwjson_stream_init(lwjson_stream_parser_t* jsp, lwjson_stream_parser_callback_fn lwjsonr_t lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { /* Get first character first */ - if (jsp->parse_state == LWJSON_STREAM_STATE_WAITINGFIRSTCHAR - && c != '{' && c != '[') { + if (jsp->parse_state == LWJSON_STREAM_STATE_WAITINGFIRSTCHAR && c != '{' && c != '[') { return lwjsonOK; } @@ -175,7 +179,7 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { jsp->parse_state = LWJSON_STREAM_STATE_PARSING; SEND_EVT(jsp, c == '{' ? LWJSON_STREAM_TYPE_OBJECT : LWJSON_STREAM_TYPE_ARRAY); - /* Determine end of object or an array */ + /* Determine end of object or an array */ } else if (c == '}' || c == ']') { lwjson_stream_type_t t = prv_stack_get_top(jsp); @@ -192,9 +196,9 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { * Check if closing character matches stack value * Avoid cases like: {"key":"value"] or ["v1", "v2", "v3"} */ - if ((c == '}' && t != LWJSON_STREAM_TYPE_OBJECT) - || (c == ']' && t != LWJSON_STREAM_TYPE_ARRAY)) { - LWJSON_DEBUG(jsp, "ERROR - closing character '%c' does not match stack element \"%s\"\r\n", c, type_strings[t]); + if ((c == '}' && t != LWJSON_STREAM_TYPE_OBJECT) || (c == ']' && t != LWJSON_STREAM_TYPE_ARRAY)) { + LWJSON_DEBUG(jsp, "ERROR - closing character '%c' does not match stack element \"%s\"\r\n", c, + type_strings[t]); return lwjsonERRJSON; } @@ -214,22 +218,23 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { } SEND_EVT(jsp, c == '}' ? LWJSON_STREAM_TYPE_OBJECT_END : LWJSON_STREAM_TYPE_ARRAY_END); - /* Determine start of string - can be key or regular string (in array or after key) */ + /* Determine start of string - can be key or regular string (in array or after key) */ } else if (c == '"') { #if defined(LWJSON_DEV) lwjson_stream_type_t t = prv_stack_get_top(jsp); if (t == LWJSON_STREAM_TYPE_OBJECT) { LWJSON_DEBUG(jsp, "Start of string parsing - expected key name in an object\r\n"); } else if (t == LWJSON_STREAM_TYPE_KEY) { - LWJSON_DEBUG(jsp, "Start of string parsing - string value associated to previous key in an object\r\n"); + LWJSON_DEBUG(jsp, + "Start of string parsing - string value associated to previous key in an object\r\n"); } else if (t == LWJSON_STREAM_TYPE_ARRAY) { LWJSON_DEBUG(jsp, "Start of string parsing - string entry in an array\r\n"); - } + } #endif /* defined(LWJSON_DEV) */ jsp->parse_state = LWJSON_STREAM_STATE_PARSING_STRING; memset(&jsp->data.str, 0x00, sizeof(jsp->data.str)); - /* Check for end of key character */ + /* Check for end of key character */ } else if (c == ':') { lwjson_stream_type_t t = prv_stack_get_top(jsp); @@ -242,11 +247,10 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { LWJSON_DEBUG(jsp, "Error - wrong ':' character\r\n"); return lwjsonERRJSON; } - /* Check if this is start of number or "true", "false" or "null" */ - } else if (c == '-' || (c >= '0' && c <= '9') - || c == 't' || c == 'f' || c == 'n') { + /* Check if this is start of number or "true", "false" or "null" */ + } else if (c == '-' || (c >= '0' && c <= '9') || c == 't' || c == 'f' || c == 'n') { LWJSON_DEBUG(jsp, "Start of primitive parsing parsing - %s, First char: %c\r\n", - (c == '-' || (c >= '0' && c <= '9')) ? "number" : "true,false,null", c); + (c == '-' || (c >= '0' && c <= '9')) ? "number" : "true,false,null", c); jsp->parse_state = LWJSON_STREAM_STATE_PARSING_PRIMITIVE; memset(&jsp->data.prim, 0x00, sizeof(jsp->data.prim)); jsp->data.prim.buff[jsp->data.prim.buff_pos++] = c; @@ -272,7 +276,9 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { if (t == LWJSON_STREAM_TYPE_OBJECT) { LWJSON_DEBUG(jsp, "End of string parsing - object key name: \"%s\"\r\n", jsp->data.str.buff); } else if (t == LWJSON_STREAM_TYPE_KEY) { - LWJSON_DEBUG(jsp, "End of string parsing - string value associated to previous key in an object: \"%s\"\r\n", jsp->data.str.buff); + LWJSON_DEBUG( + jsp, "End of string parsing - string value associated to previous key in an object: \"%s\"\r\n", + jsp->data.str.buff); } else if (t == LWJSON_STREAM_TYPE_ARRAY) { LWJSON_DEBUG(jsp, "End of string parsing - an array string entry: \"%s\"\r\n", jsp->data.str.buff); } @@ -322,8 +328,7 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { */ case LWJSON_STREAM_STATE_PARSING_PRIMITIVE: { /* Any character except space, comma, or end of array/object are valid */ - if (!prv_is_space_char_ext(c) - && c != ',' && c != ']' && c != '}') { + if (!prv_is_space_char_ext(c) && c != ',' && c != ']' && c != '}') { jsp->data.prim.buff[jsp->data.prim.buff_pos++] = c; } else { lwjson_stream_type_t t = prv_stack_get_top(jsp); @@ -332,9 +337,13 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { if (t == LWJSON_STREAM_TYPE_OBJECT) { /* TODO: Handle error - primitive cannot be just after object */ } else if (t == LWJSON_STREAM_TYPE_KEY) { - LWJSON_DEBUG(jsp, "End of primitive parsing - string value associated to previous key in an object: \"%s\"\r\n", jsp->data.str.buff); + LWJSON_DEBUG( + jsp, + "End of primitive parsing - string value associated to previous key in an object: \"%s\"\r\n", + jsp->data.str.buff); } else if (t == LWJSON_STREAM_TYPE_ARRAY) { - LWJSON_DEBUG(jsp, "End of primitive parsing - an array string entry: \"%s\"\r\n", jsp->data.str.buff); + LWJSON_DEBUG(jsp, "End of primitive parsing - an array string entry: \"%s\"\r\n", + jsp->data.str.buff); } #endif /* defined(LWJSON_DEV) */ @@ -362,7 +371,7 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { } else if (t == LWJSON_STREAM_TYPE_ARRAY) { jsp->stack[jsp->stack_pos - 1].meta.index++; } - + /* * Received character is not part of the primitive and must be processed again * @@ -378,6 +387,6 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { default: break; } - jsp->prev_c = c; /* Save current c as previous for next round */ + jsp->prev_c = c; /* Save current c as previous for next round */ return lwjsonOK; } From 85d01b32aed4c4724856bf20f9c09efbdc5e84f5 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Thu, 1 Sep 2022 23:08:51 +0200 Subject: [PATCH 66/77] Fix wrong clang formatting --- .clang-format | 84 +++++++++++++++++++++++------------------------ CMakePresets.json | 10 ++++++ 2 files changed, 52 insertions(+), 42 deletions(-) diff --git a/.clang-format b/.clang-format index 490ba55..1fad350 100644 --- a/.clang-format +++ b/.clang-format @@ -1,5 +1,5 @@ --- -Language: Cpp +Language: Cpp # BasedOnStyle: LLVM AccessModifierOffset: -2 AlignAfterOpenBracket: Align @@ -16,7 +16,8 @@ AlignConsecutiveBitFields: AlignConsecutiveDeclarations: None AlignEscapedNewlines: Right AlignOperands: Align -InsertBraces: true # Control statements must have curly brackets +SortIncludes: false +InsertBraces: true # Control statements must have curly brackets AlignTrailingComments: true AllowAllArgumentsOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: true @@ -36,21 +37,21 @@ AttributeMacros: BinPackArguments: true BinPackParameters: true BraceWrapping: - AfterCaseLabel: false - AfterClass: false + AfterCaseLabel: false + AfterClass: false AfterControlStatement: Never - AfterEnum: false - AfterFunction: false - AfterNamespace: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false AfterObjCDeclaration: false - AfterStruct: false - AfterUnion: false + AfterStruct: false + AfterUnion: false AfterExternBlock: false - BeforeCatch: false - BeforeElse: false + BeforeCatch: false + BeforeElse: false BeforeLambdaBody: false - BeforeWhile: false - IndentBraces: false + BeforeWhile: false + IndentBraces: false SplitEmptyFunction: true SplitEmptyRecord: true SplitEmptyNamespace: true @@ -64,8 +65,8 @@ BreakConstructorInitializersBeforeComma: false BreakConstructorInitializers: BeforeColon BreakAfterJavaFieldAnnotations: false BreakStringLiterals: true -ColumnLimit: 120 -CommentPragmas: '^ IWYU pragma:' +ColumnLimit: 120 +CommentPragmas: "^ IWYU pragma:" QualifierAlignment: Leave CompactNamespaces: false ConstructorInitializerIndentWidth: 4 @@ -73,12 +74,12 @@ ContinuationIndentWidth: 4 Cpp11BracedListStyle: true DeriveLineEnding: true DerivePointerAlignment: false -DisableFormat: false +DisableFormat: false EmptyLineAfterAccessModifier: Never EmptyLineBeforeAccessModifier: LogicalBlock ExperimentalAutoDetectBinPacking: false PackConstructorInitializers: BinPack -BasedOnStyle: '' +BasedOnStyle: "" ConstructorInitializerAllOnOneLineOrOnePerLine: false AllowAllConstructorInitializersOnNextLine: true FixNamespaceComments: true @@ -88,32 +89,32 @@ ForEachMacros: - BOOST_FOREACH IfMacros: - KJ_IF_MAYBE -IncludeBlocks: Preserve +IncludeBlocks: Preserve IncludeCategories: - - Regex: '^<(.*)>' - Priority: 0 - - Regex: '^"(.*)"' - Priority: 1 - - Regex: '(.*)' - Priority: 2 -IncludeIsMainRegex: '(Test)?$' -IncludeIsMainSourceRegex: '' + - Regex: "^<(.*)>" + Priority: 0 + - Regex: '^"(.*)"' + Priority: 1 + - Regex: "(.*)" + Priority: 2 +IncludeIsMainRegex: "(Test)?$" +IncludeIsMainSourceRegex: "" IndentAccessModifiers: false IndentCaseLabels: true IndentCaseBlocks: false IndentGotoLabels: true IndentPPDirectives: None IndentExternBlock: AfterExternBlock -IndentRequires: true -IndentWidth: 4 +IndentRequires: true +IndentWidth: 4 IndentWrappedFunctionNames: false InsertTrailingCommas: None JavaScriptQuotes: Leave JavaScriptWrapImports: true KeepEmptyLinesAtTheStartOfBlocks: true LambdaBodyIndentation: Signature -MacroBlockBegin: '' -MacroBlockEnd: '' +MacroBlockBegin: "" +MacroBlockEnd: "" MaxEmptyLinesToKeep: 1 NamespaceIndentation: None ObjCBinPackProtocolList: Auto @@ -132,13 +133,12 @@ PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 60 PenaltyIndentedWhitespace: 0 PointerAlignment: Left -PPIndentWidth: -1 +PPIndentWidth: -1 ReferenceAlignment: Pointer -ReflowComments: false +ReflowComments: false RemoveBracesLLVM: false SeparateDefinitionBlocks: Always ShortNamespaceLines: 1 -SortIncludes: CaseSensitive SortJavaStaticImport: Before SortUsingDeclarations: true SpaceAfterCStyleCast: false @@ -152,7 +152,7 @@ SpaceBeforeParensOptions: AfterForeachMacros: true AfterFunctionDefinitionName: false AfterFunctionDeclarationName: false - AfterIfMacros: true + AfterIfMacros: true AfterOverloadedOperator: false BeforeNonEmptyParentheses: false SpaceAroundPointerQualifiers: Default @@ -160,26 +160,26 @@ SpaceBeforeRangeBasedForLoopColon: true SpaceInEmptyBlock: false SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 -SpacesInAngles: Never +SpacesInAngles: Never SpacesInConditionalStatement: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInLineCommentPrefix: - Minimum: 1 - Maximum: -1 + Minimum: 1 + Maximum: -1 SpacesInParentheses: false SpacesInSquareBrackets: false SpaceBeforeSquareBrackets: false BitFieldColonSpacing: Both -Standard: Latest +Standard: Latest StatementAttributeLikeMacros: - Q_EMIT StatementMacros: - Q_UNUSED - QT_REQUIRE_VERSION -TabWidth: 8 -UseCRLF: false -UseTab: Never +TabWidth: 8 +UseCRLF: false +UseTab: Never WhitespaceSensitiveMacros: - STRINGIZE - PP_STRINGIZE @@ -189,5 +189,5 @@ WhitespaceSensitiveMacros: SpaceBeforeCpp11BracedList: false SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true -... +--- diff --git a/CMakePresets.json b/CMakePresets.json index 7d277c4..f99154c 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -26,5 +26,15 @@ "CMAKE_BUILD_TYPE": "Debug" } } + ], + "buildPresets": [ + { + "name": "Win32-Debug", + "configurePreset": "Win32-Debug" + }, + { + "name": "Win64-Debug", + "configurePreset": "Win64-Debug" + } ] } \ No newline at end of file From b71538f61ef9bc35118e2e4ff1737921ae9c8b4e Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Fri, 30 Sep 2022 08:11:07 +0200 Subject: [PATCH 67/77] Update CMakeLists.txt --- CMakeLists.txt | 86 +++++++++++++++++++++++--------------------------- 1 file changed, 40 insertions(+), 46 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7647a65..02fa3a3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,49 +3,43 @@ cmake_minimum_required(VERSION 3.22) # Setup project project(LwLibPROJECT) -# ------------------------------------------------- -# This CMakeLists.txt is used only if it is a top-level file. -# Purpose of it is to be able to compile project in standalone way only -# -# When library sources are to be included in another project -# user shall use /lwjson/CMakeLists.txt instead -if (NOT PROJECT_IS_TOP_LEVEL) - message(FATAL_ERROR "This CMakeLists.txt can only be used as top-level. Use /lwjson/CMakeLists.txt for library include purpose") -endif() - -# Set as executable -add_executable(${PROJECT_NAME}) - -# Add key executable block -target_sources(${PROJECT_NAME} PUBLIC - ${CMAKE_CURRENT_LIST_DIR}/dev/main.c - ${CMAKE_CURRENT_LIST_DIR}/test/test.c - ${CMAKE_CURRENT_LIST_DIR}/examples/example_minimal.c - ${CMAKE_CURRENT_LIST_DIR}/examples/example_traverse.c - ${CMAKE_CURRENT_LIST_DIR}/examples/example_stream.c -) - -# Add key include paths -target_include_directories(${PROJECT_NAME} PUBLIC - ${CMAKE_CURRENT_LIST_DIR}/dev -) - -# Compilation definition information -target_compile_definitions(${PROJECT_NAME} PUBLIC - WIN32 - _DEBUG - CONSOLE - LWJSON_DEV -) - -# Compiler options -target_compile_options(${PROJECT_NAME} PRIVATE - -Wall - -Wextra - -Wpedantic -) - -# Add subdir with lwjson and link to project -add_subdirectory("lwjson" lwjson) -target_link_libraries(${PROJECT_NAME} lwjson) -target_link_libraries(${PROJECT_NAME} lwjson_debug) \ No newline at end of file +if(NOT PROJECT_IS_TOP_LEVEL) + add_subdirectory(lwjson) +else() + # Set as executable + add_executable(${PROJECT_NAME}) + + # Add key executable block + target_sources(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/dev/main.c + ${CMAKE_CURRENT_LIST_DIR}/test/test.c + ${CMAKE_CURRENT_LIST_DIR}/examples/example_minimal.c + ${CMAKE_CURRENT_LIST_DIR}/examples/example_traverse.c + ${CMAKE_CURRENT_LIST_DIR}/examples/example_stream.c + ) + + # Add key include paths + target_include_directories(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/dev + ) + + # Compilation definition information + target_compile_definitions(${PROJECT_NAME} PUBLIC + WIN32 + _DEBUG + CONSOLE + LWJSON_DEV + ) + + # Compiler options + target_compile_options(${PROJECT_NAME} PRIVATE + -Wall + -Wextra + -Wpedantic + ) + + # Add subdir with lwjson and link to project + add_subdirectory(lwjson) + target_link_libraries(${PROJECT_NAME} lwjson) + target_link_libraries(${PROJECT_NAME} lwjson_debug) +endif() \ No newline at end of file From 21bacc10b4c07f48979bca8539cfcd0bcbc59276 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Mon, 10 Oct 2022 08:20:55 +0200 Subject: [PATCH 68/77] Update data-access.rst --- docs/user-manual/data-access.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user-manual/data-access.rst b/docs/user-manual/data-access.rst index e3997a6..42dabe5 100644 --- a/docs/user-manual/data-access.rst +++ b/docs/user-manual/data-access.rst @@ -57,7 +57,7 @@ Let's consider following JSON as input: "brand":"Range", "year":2020, "repainted":true - } + } ] } From 4a66082a88f912962a2a0f5ae5090a776b1e9fd1 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Fri, 14 Oct 2022 21:45:15 +0200 Subject: [PATCH 69/77] Stream: Support multiple & splitted blocks of long data --- .vscode/launch.json | 1 - .vscode/settings.json | 3 +- dev/main.c | 219 ++++++++++++++++------------- examples/example_stream.c | 9 +- lwjson/src/include/lwjson/lwjson.h | 18 ++- lwjson/src/lwjson/lwjson_stream.c | 35 +++-- test/json/weather_onecall.json | 58 +++++--- 7 files changed, 202 insertions(+), 141 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index a53089a..56ffea9 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,6 @@ "type": "cppdbg", "request": "launch", "program": "${command:cmake.launchTargetPath}", - "miDebuggerPath": "c:\\msys64\\mingw64\\bin\\gdb.exe", "args": [], "stopAtEntry": false, "cwd": "${fileDirname}", diff --git a/.vscode/settings.json b/.vscode/settings.json index 3b59a00..e696cf0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,7 +4,8 @@ "lwevt_type.h": "c", "lwevt.h": "c", "string.h": "c", - "lwevt_opt.h": "c" + "lwevt_opt.h": "c", + "lwjson.h": "c" }, "esbonio.sphinx.confDir": "" } \ No newline at end of file diff --git a/dev/main.c b/dev/main.c index c41b024..4301c8b 100644 --- a/dev/main.c +++ b/dev/main.c @@ -27,24 +27,25 @@ main() { const lwjson_token_t* tkn; (void)token_cnt; - +#if 0 test_run(); example_minimal_run(); example_traverse_run(); example_stream_run(); return 0; +#endif printf("\n---\n"); /* Init JSON */ lwjson_init(&lwjson, tokens, LWJSON_ARRAYSIZE(tokens)); f = CreateFile(TEXT("test\\json\\weather_onecall.json"), - GENERIC_READ, // open for reading - 0, // do not share - NULL, // no security - OPEN_EXISTING, // existing file only - FILE_ATTRIBUTE_NORMAL, // normal file - NULL); // no attr. template + GENERIC_READ, // open for reading + 0, // do not share + NULL, // no security + OPEN_EXISTING, // existing file only + FILE_ATTRIBUTE_NORMAL, // normal file + NULL); // no attr. template if (f == INVALID_HANDLE_VALUE) { printf("Could not open file..\r\n"); @@ -101,66 +102,69 @@ main() { return 0; } +/** + * \brief Stream calback demo + * \param jsp: JSON Stream parser object + * \param type: Primitive type + */ static void jsp_stream_callback(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { (void)type; -#if 0 - if (jsp->stack_pos >= 4 - && (type == LWJSON_STREAM_TYPE_STRING || type == LWJSON_STREAM_TYPE_NUMBER) - && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT - && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY - && jsp->stack[2].type == LWJSON_STREAM_TYPE_ARRAY - && jsp->stack[3].type == LWJSON_STREAM_TYPE_ARRAY - && strcmp(jsp->stack[1].meta.name, "array_in_array") == 0) { - printf("Index: %u, Index: %u, value: %s\r\n", - (int)jsp->stack[2].meta.index, (int)jsp->stack[3].meta.index, - jsp->data.str.buff - ); + + /* + * Very long string demo has been added to the weather object, + * in order to test string field that is longer than maximal allowed length. + * + * This will generate multiple calls for same key. + * + * For test, we use base64_encoded string - but it can be anything + */ + if (jsp->stack_pos == 2 && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT + && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY) { + if (strcmp(jsp->stack[1].meta.name, "base64_str") == 0) { + printf("Base64_string. Block len: %d, total len: %d, is_last: %d, data: %.*s\r\n", + (int)jsp->data.str.buff_pos, (int)jsp->data.str.buff_total_pos, (int)jsp->data.str.is_last, + (int)jsp->data.str.buff_pos, jsp->data.str.buff); + } } - return; -#endif /* * Take care of key-value pairs immediately at the start of object * * Values such as latitude and longitude are parsed here */ - if (jsp->stack_pos == 2 - && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT + if (jsp->stack_pos == 2 && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY) { if (strcmp(jsp->stack[1].meta.name, "lat") == 0) { - printf("Latitude weather: %f\r\n", strtod(jsp->data.str.buff, NULL)); + printf("Latitude weather: %f\r\n", strtod(jsp->data.prim.buff, NULL)); } else if (strcmp(jsp->stack[1].meta.name, "lon") == 0) { - printf("Longitude weather: %f\r\n", strtod(jsp->data.str.buff, NULL)); + printf("Longitude weather: %f\r\n", strtod(jsp->data.prim.buff, NULL)); } else if (strcmp(jsp->stack[1].meta.name, "timezone_offset") == 0) { - printf("Timezone offset: %u\r\n", (unsigned)atoll(jsp->data.str.buff)); + printf("Timezone offset: %u\r\n", (unsigned)atoll(jsp->data.prim.buff)); } else if (strcmp(jsp->stack[1].meta.name, "timezone") == 0) { - printf("Timezone: %s\r\n", jsp->data.str.buff); + printf("Timezone: %s\r\n", jsp->data.prim.buff); } } /* * Handle current object - single object with multiple key-value pairs */ - if (jsp->stack_pos >= 4 - && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT - && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY - && jsp->stack[2].type == LWJSON_STREAM_TYPE_OBJECT + if (jsp->stack_pos >= 4 && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT + && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY && jsp->stack[2].type == LWJSON_STREAM_TYPE_OBJECT && jsp->stack[3].type == LWJSON_STREAM_TYPE_KEY) { /* Check for current weather */ if (strcmp(jsp->stack[1].meta.name, "current") == 0) { /* * Process the "weather part" */ - if (jsp->stack_pos >= 7 - && jsp->stack[4].type == LWJSON_STREAM_TYPE_ARRAY - && jsp->stack[5].type == LWJSON_STREAM_TYPE_OBJECT - && jsp->stack[6].type == LWJSON_STREAM_TYPE_KEY + if (jsp->stack_pos >= 7 && jsp->stack[4].type == LWJSON_STREAM_TYPE_ARRAY + && jsp->stack[5].type == LWJSON_STREAM_TYPE_OBJECT && jsp->stack[6].type == LWJSON_STREAM_TYPE_KEY && strcmp(jsp->stack[3].meta.name, "weather") == 0) { - + if (strcmp(jsp->stack[6].meta.name, "id") == 0) { - printf("Current weather %d id: %u\r\n", (int)jsp->stack[4].meta.index, (unsigned)atoll(jsp->data.str.buff)); + printf("Current weather %d id: %u\r\n", (int)jsp->stack[4].meta.index, + (unsigned)atoll(jsp->data.prim.buff)); } else if (strcmp(jsp->stack[6].meta.name, "main") == 0) { printf("Current weather %d main: %s\r\n", (int)jsp->stack[4].meta.index, jsp->data.str.buff); } else if (strcmp(jsp->stack[6].meta.name, "description") == 0) { @@ -169,29 +173,29 @@ jsp_stream_callback(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { printf("Current weather %d icon: %s\r\n", (int)jsp->stack[4].meta.index, jsp->data.str.buff); } } else if (strcmp(jsp->stack[3].meta.name, "dt") == 0) { - printf("Current weather dt: %u\r\n", (unsigned)atoll(jsp->data.str.buff)); + printf("Current weather dt: %u\r\n", (unsigned)atoll(jsp->data.prim.buff)); } else if (strcmp(jsp->stack[3].meta.name, "sunrise") == 0) { - printf("Current weather sunrise: %u\r\n", (unsigned)atoll(jsp->data.str.buff)); + printf("Current weather sunrise: %u\r\n", (unsigned)atoll(jsp->data.prim.buff)); } else if (strcmp(jsp->stack[3].meta.name, "sunset") == 0) { - printf("Current weather sunset: %u\r\n", (unsigned)atoll(jsp->data.str.buff)); + printf("Current weather sunset: %u\r\n", (unsigned)atoll(jsp->data.prim.buff)); } else if (strcmp(jsp->stack[3].meta.name, "temp") == 0) { - printf("Current weather temp: %f\r\n", strtod(jsp->data.str.buff, NULL)); + printf("Current weather temp: %f\r\n", strtod(jsp->data.prim.buff, NULL)); } else if (strcmp(jsp->stack[3].meta.name, "feels_like") == 0) { - printf("Current weather feels_like: %f\r\n", strtod(jsp->data.str.buff, NULL)); + printf("Current weather feels_like: %f\r\n", strtod(jsp->data.prim.buff, NULL)); } else if (strcmp(jsp->stack[3].meta.name, "pressure") == 0) { - printf("Current weather pressure: %u\r\n", (unsigned)atoll(jsp->data.str.buff)); + printf("Current weather pressure: %u\r\n", (unsigned)atoll(jsp->data.prim.buff)); } else if (strcmp(jsp->stack[3].meta.name, "humidity") == 0) { - printf("Current weather humidity: %u\r\n", (unsigned)atoll(jsp->data.str.buff)); + printf("Current weather humidity: %u\r\n", (unsigned)atoll(jsp->data.prim.buff)); } else if (strcmp(jsp->stack[3].meta.name, "uvi") == 0) { - printf("Current weather uvi: %u\r\n", (unsigned)atoll(jsp->data.str.buff)); + printf("Current weather uvi: %u\r\n", (unsigned)atoll(jsp->data.prim.buff)); } else if (strcmp(jsp->stack[3].meta.name, "clouds") == 0) { - printf("Current weather clouds: %u\r\n", (unsigned)atoll(jsp->data.str.buff)); + printf("Current weather clouds: %u\r\n", (unsigned)atoll(jsp->data.prim.buff)); } else if (strcmp(jsp->stack[3].meta.name, "visibility") == 0) { - printf("Current weather visibility: %u\r\n", (unsigned)atoll(jsp->data.str.buff)); + printf("Current weather visibility: %u\r\n", (unsigned)atoll(jsp->data.prim.buff)); } else if (strcmp(jsp->stack[3].meta.name, "wind_speed") == 0) { - printf("Current weather wind_speed: %f\r\n", strtod(jsp->data.str.buff, NULL)); + printf("Current weather wind_speed: %f\r\n", strtod(jsp->data.prim.buff, NULL)); } else if (strcmp(jsp->stack[3].meta.name, "wind_deg") == 0) { - printf("Current weather wind_deg: %u\r\n", (unsigned)atoll(jsp->data.str.buff)); + printf("Current weather wind_deg: %u\r\n", (unsigned)atoll(jsp->data.prim.buff)); } } } @@ -201,109 +205,122 @@ jsp_stream_callback(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { */ if (jsp->stack_pos >= 5 /* First build the order... */ - && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT - && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY - && jsp->stack[2].type == LWJSON_STREAM_TYPE_ARRAY - && jsp->stack[3].type == LWJSON_STREAM_TYPE_OBJECT + && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY + && jsp->stack[2].type == LWJSON_STREAM_TYPE_ARRAY && jsp->stack[3].type == LWJSON_STREAM_TYPE_OBJECT && jsp->stack[4].type == LWJSON_STREAM_TYPE_KEY) { - + /* * Handle daily forecast objects */ if (strcmp(jsp->stack[1].meta.name, "daily") == 0) { /* Analyze objects for temp and feels like object */ - if (jsp->stack_pos >= 7 - && jsp->stack[5].type == LWJSON_STREAM_TYPE_OBJECT + if (jsp->stack_pos >= 7 && jsp->stack[5].type == LWJSON_STREAM_TYPE_OBJECT && jsp->stack[6].type == LWJSON_STREAM_TYPE_KEY) { if (strcmp(jsp->stack[4].meta.name, "temp") == 0) { - /* Parsing of temp object */ + /* Parsing of temp object */ if (strcmp(jsp->stack[6].meta.name, "min") == 0) { - printf("Day %2d temp min: %s\r\n", (int)jsp->stack[2].meta.index, jsp->data.str.buff); + printf("Day %2d temp min: %s\r\n", (int)jsp->stack[2].meta.index, jsp->data.prim.buff); } else if (strcmp(jsp->stack[6].meta.name, "max") == 0) { - printf("Day %2d temp max: %s\r\n", (int)jsp->stack[2].meta.index, jsp->data.str.buff); + printf("Day %2d temp max: %s\r\n", (int)jsp->stack[2].meta.index, jsp->data.prim.buff); } else if (strcmp(jsp->stack[6].meta.name, "day") == 0) { - printf("Day %2d temp day: %s\r\n", (int)jsp->stack[2].meta.index, jsp->data.str.buff); + printf("Day %2d temp day: %s\r\n", (int)jsp->stack[2].meta.index, jsp->data.prim.buff); } else if (strcmp(jsp->stack[6].meta.name, "night") == 0) { - printf("Day %2d temp night: %s\r\n", (int)jsp->stack[2].meta.index, jsp->data.str.buff); + printf("Day %2d temp night: %s\r\n", (int)jsp->stack[2].meta.index, jsp->data.prim.buff); } else if (strcmp(jsp->stack[6].meta.name, "eve") == 0) { - printf("Day %2d temp eve: %s\r\n", (int)jsp->stack[2].meta.index, jsp->data.str.buff); + printf("Day %2d temp eve: %s\r\n", (int)jsp->stack[2].meta.index, jsp->data.prim.buff); } else if (strcmp(jsp->stack[6].meta.name, "morn") == 0) { - printf("Day %2d temp morn: %s\r\n", (int)jsp->stack[2].meta.index, jsp->data.str.buff); + printf("Day %2d temp morn: %s\r\n", (int)jsp->stack[2].meta.index, jsp->data.prim.buff); } } else if (strcmp(jsp->stack[4].meta.name, "feels_like") == 0) { /* Parsing of feels-like objects */ if (strcmp(jsp->stack[6].meta.name, "day") == 0) { - printf("Day %2d temp_feels_like day: %s\r\n", (int)jsp->stack[2].meta.index, jsp->data.str.buff); + printf("Day %2d temp_feels_like day: %s\r\n", (int)jsp->stack[2].meta.index, + jsp->data.prim.buff); } else if (strcmp(jsp->stack[6].meta.name, "night") == 0) { - printf("Day %2d temp_feels_like night: %s\r\n", (int)jsp->stack[2].meta.index, jsp->data.str.buff); + printf("Day %2d temp_feels_like night: %s\r\n", (int)jsp->stack[2].meta.index, + jsp->data.prim.buff); } else if (strcmp(jsp->stack[6].meta.name, "eve") == 0) { - printf("Day %2d temp_feels_like eve: %s\r\n", (int)jsp->stack[2].meta.index, jsp->data.str.buff); + printf("Day %2d temp_feels_like eve: %s\r\n", (int)jsp->stack[2].meta.index, + jsp->data.prim.buff); } else if (strcmp(jsp->stack[6].meta.name, "morn") == 0) { - printf("Day %2d temp_feels_like morn: %s\r\n", (int)jsp->stack[2].meta.index, jsp->data.str.buff); + printf("Day %2d temp_feels_like morn: %s\r\n", (int)jsp->stack[2].meta.index, + jsp->data.prim.buff); } } - } else if (jsp->stack_pos >= 8 - && jsp->stack[5].type == LWJSON_STREAM_TYPE_ARRAY - && jsp->stack[6].type == LWJSON_STREAM_TYPE_OBJECT - && jsp->stack[7].type == LWJSON_STREAM_TYPE_KEY - && strcmp(jsp->stack[4].meta.name, "weather") == 0) { - + } else if (jsp->stack_pos >= 8 && jsp->stack[5].type == LWJSON_STREAM_TYPE_ARRAY + && jsp->stack[6].type == LWJSON_STREAM_TYPE_OBJECT + && jsp->stack[7].type == LWJSON_STREAM_TYPE_KEY + && strcmp(jsp->stack[4].meta.name, "weather") == 0) { + if (strcmp(jsp->stack[7].meta.name, "id") == 0) { - printf("Day %2d weather %2d id: %s\r\n", (int)jsp->stack[2].meta.index, (int)jsp->stack[5].meta.index, jsp->data.str.buff); + printf("Day %2d weather %2d id: %s\r\n", (int)jsp->stack[2].meta.index, + (int)jsp->stack[5].meta.index, jsp->data.prim.buff); } else if (strcmp(jsp->stack[7].meta.name, "main") == 0) { - printf("Day %2d weather %2d main: %s\r\n", (int)jsp->stack[2].meta.index, (int)jsp->stack[5].meta.index, jsp->data.str.buff); + printf("Day %2d weather %2d main: %s\r\n", (int)jsp->stack[2].meta.index, + (int)jsp->stack[5].meta.index, jsp->data.str.buff); } else if (strcmp(jsp->stack[7].meta.name, "description") == 0) { - printf("Day %2d weather %2d description: %s\r\n", (int)jsp->stack[2].meta.index, (int)jsp->stack[5].meta.index, jsp->data.str.buff); + printf("Day %2d weather %2d description: %s\r\n", (int)jsp->stack[2].meta.index, + (int)jsp->stack[5].meta.index, jsp->data.str.buff); } else if (strcmp(jsp->stack[7].meta.name, "icon") == 0) { - printf("Day %2d weather %2d icon: %s\r\n", (int)jsp->stack[2].meta.index, (int)jsp->stack[5].meta.index, jsp->data.str.buff); + printf("Day %2d weather %2d icon: %s\r\n", (int)jsp->stack[2].meta.index, + (int)jsp->stack[5].meta.index, jsp->data.str.buff); } } else if (strcmp(jsp->stack[jsp->stack_pos - 1].meta.name, "dt") == 0) { - printf("Day %2d dt: %u\r\n", (int)jsp->stack[2].meta.index, (unsigned)atoll(jsp->data.str.buff)); + printf("Day %2d dt: %u\r\n", (int)jsp->stack[2].meta.index, (unsigned)atoll(jsp->data.prim.buff)); } else if (strcmp(jsp->stack[jsp->stack_pos - 1].meta.name, "pressure") == 0) { - printf("Day %2d pressure: %u\r\n", (int)jsp->stack[2].meta.index, (unsigned)atoll(jsp->data.str.buff)); + printf("Day %2d pressure: %u\r\n", (int)jsp->stack[2].meta.index, (unsigned)atoll(jsp->data.prim.buff)); } else if (strcmp(jsp->stack[jsp->stack_pos - 1].meta.name, "humidity") == 0) { - printf("Day %2d humidity: %u\r\n", (int)jsp->stack[2].meta.index, (unsigned)atoll(jsp->data.str.buff)); + printf("Day %2d humidity: %u\r\n", (int)jsp->stack[2].meta.index, (unsigned)atoll(jsp->data.prim.buff)); } else if (strcmp(jsp->stack[jsp->stack_pos - 1].meta.name, "clouds") == 0) { - printf("Day %2d clouds: %u\r\n", (int)jsp->stack[2].meta.index, (unsigned)atoll(jsp->data.str.buff)); + printf("Day %2d clouds: %u\r\n", (int)jsp->stack[2].meta.index, (unsigned)atoll(jsp->data.prim.buff)); } else if (strcmp(jsp->stack[jsp->stack_pos - 1].meta.name, "snow") == 0) { - printf("Day %2d snow: %f\r\n", (int)jsp->stack[2].meta.index, strtod(jsp->data.str.buff, NULL)); + printf("Day %2d snow: %f\r\n", (int)jsp->stack[2].meta.index, strtod(jsp->data.prim.buff, NULL)); } else if (strcmp(jsp->stack[jsp->stack_pos - 1].meta.name, "rain") == 0) { - printf("Day %2d rain: %f\r\n", (int)jsp->stack[2].meta.index, strtod(jsp->data.str.buff, NULL)); + printf("Day %2d rain: %f\r\n", (int)jsp->stack[2].meta.index, strtod(jsp->data.prim.buff, NULL)); } else if (strcmp(jsp->stack[jsp->stack_pos - 1].meta.name, "uvi") == 0) { - printf("Day %2d uvi: %f\r\n", (int)jsp->stack[2].meta.index, strtod(jsp->data.str.buff, NULL)); + printf("Day %2d uvi: %f\r\n", (int)jsp->stack[2].meta.index, strtod(jsp->data.prim.buff, NULL)); } } else if (strcmp(jsp->stack[1].meta.name, "hourly") == 0) { - if (jsp->stack_pos >= 8 - && jsp->stack[5].type == LWJSON_STREAM_TYPE_ARRAY - && jsp->stack[6].type == LWJSON_STREAM_TYPE_OBJECT - && jsp->stack[7].type == LWJSON_STREAM_TYPE_KEY + if (jsp->stack_pos >= 8 && jsp->stack[5].type == LWJSON_STREAM_TYPE_ARRAY + && jsp->stack[6].type == LWJSON_STREAM_TYPE_OBJECT && jsp->stack[7].type == LWJSON_STREAM_TYPE_KEY && strcmp(jsp->stack[4].meta.name, "weather") == 0) { - + if (strcmp(jsp->stack[7].meta.name, "id") == 0) { - printf("Hour %2d weather %2d id: %s\r\n", (int)jsp->stack[2].meta.index, (int)jsp->stack[5].meta.index, jsp->data.str.buff); + printf("Hour %2d weather %2d id: %s\r\n", (int)jsp->stack[2].meta.index, + (int)jsp->stack[5].meta.index, jsp->data.prim.buff); } else if (strcmp(jsp->stack[7].meta.name, "main") == 0) { - printf("Hour %2d weather %2d main: %s\r\n", (int)jsp->stack[2].meta.index, (int)jsp->stack[5].meta.index, jsp->data.str.buff); + printf("Hour %2d weather %2d main: %s\r\n", (int)jsp->stack[2].meta.index, + (int)jsp->stack[5].meta.index, jsp->data.str.buff); } else if (strcmp(jsp->stack[7].meta.name, "description") == 0) { - printf("Hour %2d weather %2d description: %s\r\n", (int)jsp->stack[2].meta.index, (int)jsp->stack[5].meta.index, jsp->data.str.buff); + printf("Hour %2d weather %2d description: %s\r\n", (int)jsp->stack[2].meta.index, + (int)jsp->stack[5].meta.index, jsp->data.str.buff); } else if (strcmp(jsp->stack[7].meta.name, "icon") == 0) { - printf("Hour %2d weather %2d icon: %s\r\n", (int)jsp->stack[2].meta.index, (int)jsp->stack[5].meta.index, jsp->data.str.buff); + printf("Hour %2d weather %2d icon: %s\r\n", (int)jsp->stack[2].meta.index, + (int)jsp->stack[5].meta.index, jsp->data.str.buff); } - }else if (strcmp(jsp->stack[4].meta.name, "dt") == 0) { - printf("Hour %2d forecast for dt: %u\r\n", (int)jsp->stack[jsp->stack_pos - 3].meta.index, (unsigned)atoll(jsp->data.str.buff)); + } else if (strcmp(jsp->stack[4].meta.name, "dt") == 0) { + printf("Hour %2d forecast for dt: %u\r\n", (int)jsp->stack[jsp->stack_pos - 3].meta.index, + (unsigned)atoll(jsp->data.prim.buff)); } else if (strcmp(jsp->stack[4].meta.name, "temp") == 0) { - printf("Hour %2d forecast for temp: %f\r\n", (int)jsp->stack[jsp->stack_pos - 3].meta.index, strtod(jsp->data.str.buff, NULL)); + printf("Hour %2d forecast for temp: %f\r\n", (int)jsp->stack[jsp->stack_pos - 3].meta.index, + strtod(jsp->data.prim.buff, NULL)); } else if (strcmp(jsp->stack[4].meta.name, "feels_like") == 0) { - printf("Hour %2d forecast for feels_like: %f\r\n", (int)jsp->stack[jsp->stack_pos - 3].meta.index, strtod(jsp->data.str.buff, NULL)); + printf("Hour %2d forecast for feels_like: %f\r\n", (int)jsp->stack[jsp->stack_pos - 3].meta.index, + strtod(jsp->data.prim.buff, NULL)); } else if (strcmp(jsp->stack[4].meta.name, "pressure") == 0) { - printf("Hour %2d forecast for pressure: %d\r\n", (int)jsp->stack[jsp->stack_pos - 3].meta.index, (int)atoll(jsp->data.str.buff)); + printf("Hour %2d forecast for pressure: %d\r\n", (int)jsp->stack[jsp->stack_pos - 3].meta.index, + (int)atoll(jsp->data.prim.buff)); } else if (strcmp(jsp->stack[4].meta.name, "humidity") == 0) { - printf("Hour %2d forecast for humidity: %d\r\n", (int)jsp->stack[jsp->stack_pos - 3].meta.index, (int)atoll(jsp->data.str.buff)); + printf("Hour %2d forecast for humidity: %d\r\n", (int)jsp->stack[jsp->stack_pos - 3].meta.index, + (int)atoll(jsp->data.prim.buff)); } } else if (strcmp(jsp->stack[1].meta.name, "minutely") == 0) { if (strcmp(jsp->stack[4].meta.name, "dt") == 0) { - printf("Minute %2d forecast for dt: %u\r\n", (int)jsp->stack[jsp->stack_pos - 3].meta.index, (unsigned)atoll(jsp->data.str.buff)); + printf("Minute %2d forecast for dt: %u\r\n", (int)jsp->stack[jsp->stack_pos - 3].meta.index, + (unsigned)atoll(jsp->data.prim.buff)); } else if (strcmp(jsp->stack[4].meta.name, "precipitation") == 0) { - printf("Minute %2d forecast for precipitation: %u\r\n", (int)jsp->stack[jsp->stack_pos - 3].meta.index, (unsigned)atoll(jsp->data.str.buff)); + printf("Minute %2d forecast for precipitation: %u\r\n", (int)jsp->stack[jsp->stack_pos - 3].meta.index, + (unsigned)atoll(jsp->data.prim.buff)); } } } diff --git a/examples/example_stream.c b/examples/example_stream.c index 39b4f0a..a01686f 100644 --- a/examples/example_stream.c +++ b/examples/example_stream.c @@ -15,12 +15,13 @@ static lwjson_stream_parser_t stream_parser; void prv_example_callback_func(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { /* Get a value corresponsing to "k1" key */ - if (jsp->stack_pos >= 2 /* Number of stack entries must be high */ - && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT /* First must be object */ - && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY /* We need key to be before */ + if (jsp->stack_pos >= 2 /* Number of stack entries must be high */ + && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT /* First must be object */ + && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY /* We need key to be before */ && strcmp(jsp->stack[1].meta.name, "k1") == 0) { printf("Got key '%s' with value '%s'\r\n", jsp->stack[1].meta.name, jsp->data.str.buff); } + (void)type; } /* Parse JSON */ @@ -30,7 +31,7 @@ example_stream_run(void) { lwjson_stream_init(&stream_parser, prv_example_callback_func); /* Demonstrate as stream inputs */ - for (const char *c = json_str; *c != '\0'; ++c) { + for (const char* c = json_str; *c != '\0'; ++c) { if (lwjson_stream_parse(&stream_parser, *c) != lwjsonOK) { printf("ERROR\r\n"); return; diff --git a/lwjson/src/include/lwjson/lwjson.h b/lwjson/src/include/lwjson/lwjson.h index c371be4..5faea42 100644 --- a/lwjson/src/include/lwjson/lwjson.h +++ b/lwjson/src/include/lwjson/lwjson.h @@ -34,8 +34,8 @@ #ifndef LWJSON_HDR_H #define LWJSON_HDR_H -#include #include +#include #include "lwjson/lwjson_opt.h" #ifdef __cplusplus @@ -190,8 +190,9 @@ typedef void (*lwjson_stream_parser_callback_fn)(struct lwjson_stream_parser* js * \brief LwJSON streaming structure */ typedef struct lwjson_stream_parser { - lwjson_stream_stack_t stack[LWJSON_CFG_STREAM_STACK_SIZE]; /*!< Stack used for parsing */ - size_t stack_pos; /*!< Current stack position */ + lwjson_stream_stack_t + stack[LWJSON_CFG_STREAM_STACK_SIZE]; /*!< Stack used for parsing. TODO: Add conditional compilation flag */ + size_t stack_pos; /*!< Current stack position */ lwjson_stream_state_t parse_state; /*!< Parser state */ @@ -201,14 +202,17 @@ typedef struct lwjson_stream_parser { union { struct { char buff[LWJSON_CFG_STREAM_STRING_MAX_LEN - + 1]; /*!< Buffer to write temporary data. TODO: Size to be variable with define */ - size_t buff_pos; /*!< Buffer position for next write (length of bytes in buffer) */ - } str; /*!< String structure */ + + 1]; /*!< Buffer to write temporary data. TODO: Size to be variable with define */ + size_t buff_pos; /*!< Buffer position for next write (length of bytes in buffer) */ + size_t buff_total_pos; /*!< Total buffer position used up to now (in several data chunks) */ + uint8_t is_last; /*!< Status indicates if this is the last part of the string */ + } str; /*!< String structure. It is only used for keys and string objects. + Use primitive part for all other options */ struct { char buff[LWJSON_CFG_STREAM_PRIMITIVE_MAX_LEN + 1]; /*!< Temporary write buffer */ size_t buff_pos; /*!< Buffer position for next write */ - } prim; /*!< Primitive object */ + } prim; /*!< Primitive object. Used for all types, except key or string */ /* Todo: Add other types */ } data; /*!< Data union used to parse various */ diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index 527886c..a5f76af 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -264,6 +264,8 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { * It is used for key or string in an object or an array */ case LWJSON_STREAM_STATE_PARSING_STRING: { + lwjson_stream_type_t t = prv_stack_get_top(jsp); + /* * Quote character may trigger end of string, * or if backslasled before - it is part of string @@ -271,7 +273,6 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { * TODO: Handle backslash */ if (c == '"' && jsp->prev_c != '\\') { - lwjson_stream_type_t t = prv_stack_get_top(jsp); #if defined(LWJSON_DEV) if (t == LWJSON_STREAM_TYPE_OBJECT) { LWJSON_DEBUG(jsp, "End of string parsing - object key name: \"%s\"\r\n", jsp->data.str.buff); @@ -284,6 +285,9 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { } #endif /* defined(LWJSON_DEV) */ + /* Set is_last to 1 as this is the last part of this string token */ + jsp->data.str.is_last = 1; + /* * When top of stack is object - string is treated as a key * When top of stack is a key - string is a value for a key - notify user and pop the value for key @@ -302,7 +306,6 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { LWJSON_DEBUG(jsp, "Cannot push key to stack\r\n"); return lwjsonERRMEM; } - } else if (t == LWJSON_STREAM_TYPE_KEY) { SEND_EVT(jsp, LWJSON_STREAM_TYPE_STRING); prv_stack_pop(jsp); @@ -314,9 +317,22 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { jsp->parse_state = LWJSON_STREAM_STATE_PARSING; } else { /* TODO: Check other backslash elements */ - jsp->data.str.buff[jsp->data.str.buff_pos++] = c; - /* TODO: If buffer lower than needed for user - send multiple events */ + jsp->data.str.buff_total_pos++; + + /* Handle buffer "overflow" */ + if (jsp->data.str.buff_pos >= (LWJSON_CFG_STREAM_STRING_MAX_LEN - 1)) { + jsp->data.str.buff[jsp->data.str.buff_pos] = '\0'; + + /* + * - For array or key types - following one is always string + * - For object type - character is key + */ + SEND_EVT(jsp, (t == LWJSON_STREAM_TYPE_KEY || t == LWJSON_STREAM_TYPE_ARRAY) + ? LWJSON_STREAM_TYPE_STRING + : LWJSON_STREAM_TYPE_KEY); + jsp->data.str.buff_pos = 0; + } } break; } @@ -340,10 +356,10 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { LWJSON_DEBUG( jsp, "End of primitive parsing - string value associated to previous key in an object: \"%s\"\r\n", - jsp->data.str.buff); + jsp->data.prim.buff); } else if (t == LWJSON_STREAM_TYPE_ARRAY) { LWJSON_DEBUG(jsp, "End of primitive parsing - an array string entry: \"%s\"\r\n", - jsp->data.str.buff); + jsp->data.prim.buff); } #endif /* defined(LWJSON_DEV) */ @@ -362,9 +378,12 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { } else if (jsp->data.prim.buff_pos == 5 && strncmp(jsp->data.prim.buff, "false", 5) == 0) { LWJSON_DEBUG(jsp, "Primitive parsed as %s\r\n", "false"); SEND_EVT(jsp, LWJSON_STREAM_TYPE_FALSE); - } else { - LWJSON_DEBUG(jsp, "Primitive parsed - can only be a number\r\n"); + } else if (jsp->data.prim.buff[0] == '-' + || (jsp->data.prim.buff[0] >= '0' && jsp->data.prim.buff[0] <= '9')) { + LWJSON_DEBUG(jsp, "Primitive parsed - number\r\n"); SEND_EVT(jsp, LWJSON_STREAM_TYPE_NUMBER); + } else { + LWJSON_DEBUG(jsp, "Invalid primitive type. Got: %s\r\n", jsp->data.prim.buff); } if (t == LWJSON_STREAM_TYPE_KEY) { prv_stack_pop(jsp); diff --git a/test/json/weather_onecall.json b/test/json/weather_onecall.json index 516aed8..11402a5 100644 --- a/test/json/weather_onecall.json +++ b/test/json/weather_onecall.json @@ -2,6 +2,7 @@ "lat": 45.5709, "lon": 15.193, "timezone": "Europe/Ljubljana", + "base64_str": "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gRHVpcyBxdWlzIGRpYW0gbm9uIG5pc2wgZGljdHVtIGZlcm1lbnR1bS4gTWFlY2VuYXMgdnVscHV0YXRlIGVyYXQgc2VkIHNvbGxpY2l0dWRpbiBpbXBlcmRpZXQuIFN1c3BlbmRpc3NlIGV1IG1hdXJpcyBhdWd1ZS4gUGVsbGVudGVzcXVlIHZhcml1cyBkYXBpYnVzIGR1aSwgbm9uIHZlaGljdWxhIGxpYmVybyB2YXJpdXMgZXUuIFBlbGxlbnRlc3F1ZSBjb25ndWUgbmliaCBwdXJ1cywgaW4gYWxpcXVldCBsaWd1bGEgdml2ZXJyYSBhYy4gSW50ZWdlciBzZWQgbmlzbCBldSBlcm9zIHNhZ2l0dGlzIGNvbmRpbWVudHVtIHZlbCBzaXQgYW1ldCBsZW8uIE51bGxhbSBzZWQgZmluaWJ1cyBuaWJoLiBDbGFzcyBhcHRlbnQgdGFjaXRpIHNvY2lvc3F1IGFkIGxpdG9yYSB0b3JxdWVudCBwZXIgY29udWJpYSBub3N0cmEsIHBlciBpbmNlcHRvcyBoaW1lbmFlb3MuIEFsaXF1YW0gdmVsIHNvZGFsZXMganVzdG8sIGV0IHBvc3VlcmUgbWFnbmEuIEV0aWFtIGVsZW1lbnR1bSBmZWxpcyB1dCBsZWN0dXMgdGluY2lkdW50IGdyYXZpZGEuIE51bGxhbSBmYXVjaWJ1cyBhbGlxdWV0IHRlbGx1cyB2aXRhZSBzb2RhbGVzLiBBbGlxdWFtIHNjZWxlcmlzcXVlIGZlcm1lbnR1bSB2dWxwdXRhdGUuIEN1cmFiaXR1ciBzaXQgYW1ldCBlcmF0IGhlbmRyZXJpdCwgdWx0cmljZXMgbWV0dXMgc2l0IGFtZXQsIG1hbGVzdWFkYSBuaXNsLiBDdXJhYml0dXIgZXUgdG9ydG9yIG1hbGVzdWFkYSBmZWxpcyB2aXZlcnJhIHB1bHZpbmFyLiBOdWxsYSBlbGVtZW50dW0gdmVsIHVybmEgaWQgcGhhcmV0cmEuCgpBbGlxdWFtIHVsdHJpY2VzIHNpdCBhbWV0IGxhY3VzIHNlZCBiaWJlbmR1bS4gSW4gY29udmFsbGlzIGVyYXQgdmVsIGRpYW0gbGFjaW5pYSB1bHRyaWNpZXMuIFNlZCBhdCBjb25ndWUgbmlzaS4gRnVzY2UgdmFyaXVzIGxhY2luaWEgbGVvLCB2aXRhZSB0ZW1wdXMgbGVvIG1heGltdXMgZXQuIE51bGxhIG5lYyBkYXBpYnVzIGVyb3MuIEV0aWFtIGF0IGZlcm1lbnR1bSByaXN1cywgYWMgcHJldGl1bSBmZWxpcy4gRnVzY2UgcXVpcyBqdXN0byBhbGlxdWFtLCBmaW5pYnVzIG1ldHVzIGlkLCBzb2RhbGVzIG1hdXJpcy4gQ3JhcyB2ZXN0aWJ1bHVtIHB1cnVzIHF1YW0sIGlkIHZlc3RpYnVsdW0gbnVuYyBjb252YWxsaXMgZXQuIE1hZWNlbmFzIGp1c3RvIGxvcmVtLCB2YXJpdXMgYSBsYW9yZWV0IGVnZXQsIGZpbmlidXMgZXUgbGlndWxhLiBQcmFlc2VudCBmYWNpbGlzaXMsIG1pIGJsYW5kaXQgbW9sZXN0aWUgYXVjdG9yLCB0ZWxsdXMgc2VtIHVsdHJpY2llcyBuZXF1ZSwgdml0YWUgdm9sdXRwYXQgZW5pbSBsZW8gZXQgYXVndWUuIFBoYXNlbGx1cyB2ZXN0aWJ1bHVtIGNvbnNlY3RldHVyIGNvbW1vZG8uCgpEb25lYyBmZXJtZW50dW0gb2RpbyBldSBwdXJ1cyBpbnRlcmR1bSB0cmlzdGlxdWUuIER1aXMgaGVuZHJlcml0IG5lcXVlIGRvbG9yLCB1dCBjb25zZXF1YXQgc2VtIHBvcnRhIHF1aXMuIENyYXMgZGljdHVtIGNvbmd1ZSBpbnRlcmR1bS4gVXQgbmVjIGR1aSBpbiBhdWd1ZSBsYW9yZWV0IHRpbmNpZHVudCBzaXQgYW1ldCBhIGxhY3VzLiBWaXZhbXVzIGZyaW5naWxsYSBvcmNpIGF0IHZpdmVycmEgdGluY2lkdW50LiBEdWlzIGF1Y3RvciwgZmVsaXMgdm9sdXRwYXQgdGVtcHVzIGNvbW1vZG8sIGxlbyBwdXJ1cyBjb25ndWUgbGVjdHVzLCBldCBtb2xsaXMgZW5pbSBhbnRlIHF1aXMgdXJuYS4gVmVzdGlidWx1bSBxdWlzIG1hZ25hIGEgdmVsaXQgZGlnbmlzc2ltIGV1aXNtb2QgbGFjaW5pYSBpbiBlbmltLiBJbiBtYXR0aXMgc2VkIGRpYW0gbmVjIGF1Y3Rvci4gRnVzY2Ugc2l0IGFtZXQgZXN0IHR1cnBpcy4gSW4gZXUgdG9ydG9yIGVyb3MuIENyYXMgbG9ib3J0aXMgZGFwaWJ1cyBzZW0gbmVjIGxhY2luaWEuIFN1c3BlbmRpc3NlIG1hZ25hIGFyY3UsIHJ1dHJ1bSB1dCBkaWN0dW0gc2VkLCBzY2VsZXJpc3F1ZSBpbiBlbGl0LiBEdWlzIHZ1bHB1dGF0ZSBjdXJzdXMgZWZmaWNpdHVyLiBQZWxsZW50ZXNxdWUgYmxhbmRpdCBmZXJtZW50dW0gZXN0LiBDcmFzIHZhcml1cyBpcHN1bSB1cm5hLCBhIGhlbmRyZXJpdCBzYXBpZW4gcnV0cnVtIGF0LiBOYW0gZ3JhdmlkYSBxdWlzIG1pIGV0IGF1Y3Rvci4KCkRvbmVjIHBvcnRhIG1hdXJpcyBzaXQgYW1ldCB2aXZlcnJhIHVsdHJpY2VzLiBEb25lYyBsdWN0dXMgcHJldGl1bSBvcmNpIGV0IG9ybmFyZS4gTW9yYmkgYWMgbGFjdXMgYWxpcXVhbSwgY3Vyc3VzIHB1cnVzIHZpdGFlLCBncmF2aWRhIGRvbG9yLiBGdXNjZSBzZWQgdmVsaXQgbW9sbGlzLCBzZW1wZXIgbGlndWxhIGlkLCBtYXR0aXMgc2VtLiBWZXN0aWJ1bHVtIHVsdHJpY2llcyBzZW0gbm9uIG9kaW8gaGVuZHJlcml0IGRhcGlidXMuIFBoYXNlbGx1cyB2b2x1dHBhdCBhdWd1ZSB2ZWwgbmliaCB2YXJpdXMsIGluIGRpZ25pc3NpbSBtZXR1cyBzZW1wZXIuIFN1c3BlbmRpc3NlIGxvYm9ydGlzIGxlY3R1cyBldSBxdWFtIGNvbmd1ZSwgcXVpcyB2dWxwdXRhdGUgYXJjdSBiaWJlbmR1bS4gU3VzcGVuZGlzc2UgdGVtcHVzIG1hZ25hIHZlbmVuYXRpcyBwdXJ1cyB2ZXN0aWJ1bHVtIGlhY3VsaXMuIE51bGxhbSBpbnRlcmR1bSB0cmlzdGlxdWUgbmVxdWUgZWdldCBlZmZpY2l0dXIuIER1aXMgZmluaWJ1cywgdmVsaXQgdWxsYW1jb3JwZXIgc2NlbGVyaXNxdWUgaGVuZHJlcml0LCBuaXNpIG51bmMgbGFvcmVldCBkb2xvciwgdGVtcHVzIHJ1dHJ1bSBhbnRlIGxlbyBhIGxlY3R1cy4gRG9uZWMgdml0YWUgcG9ydGEgbG9yZW0sIGVnZXQgc3VzY2lwaXQgbGVjdHVzLiBNYXVyaXMgYWMgbGVvIG1pLiBNYXVyaXMgZXVpc21vZCBzZW0gZXUgbGlndWxhIGNvbnNlY3RldHVyIGNvbmRpbWVudHVtLgoKTW9yYmkgdWxsYW1jb3JwZXIgdm9sdXRwYXQgbWFzc2Egbm9uIGFsaXF1ZXQuIE1hdXJpcyB2aXRhZSBmcmluZ2lsbGEgbWF1cmlzLiBTZWQgbmliaCBtYWduYSwgdmVuZW5hdGlzIHF1aXMgbWF4aW11cyBhdCwgbWF0dGlzIG5lYyBudW5jLiBVdCByaG9uY3VzIGFudGUgYXQgZGFwaWJ1cyBmYWNpbGlzaXMuIEN1cmFiaXR1ciBhY2N1bXNhbiBpZCBhdWd1ZSBuZWMgbGFjaW5pYS4gUGhhc2VsbHVzIGNvbnNlcXVhdCBoZW5kcmVyaXQgbWFzc2EuIEFsaXF1YW0gbHVjdHVzIGVmZmljaXR1ciBjb25zZWN0ZXR1ci4gSW50ZWdlciBmZXJtZW50dW0gc2FwaWVuIGZldWdpYXQgZmVsaXMgdmVoaWN1bGEsIG5vbiBlbGVtZW50dW0gbnVsbGEgZ3JhdmlkYS4gSW4gdmVsIHVsdHJpY2llcyBleC4gUHJhZXNlbnQgcGhhcmV0cmEgYSB0dXJwaXMgdml0YWUgbGFvcmVldC4gRXRpYW0gYWNjdW1zYW4gc2VtcGVyIHR1cnBpcyBlZ2V0IHBvcnR0aXRvci4gUGhhc2VsbHVzIGFjIGZlbGlzIGV1IHR1cnBpcyBtYXhpbXVzIG1vbGxpcy4gRG9uZWMgY3Vyc3VzIGRpYW0gbmVjIGp1c3RvIGZhY2lsaXNpcywgc2VkIG1vbGxpcyBudWxsYSBoZW5kcmVyaXQuIE5hbSBydXRydW0sIG1hc3NhIG5vbiBibGFuZGl0IHZvbHV0cGF0LCBsYWN1cyB0ZWxsdXMgYWxpcXVhbSB0b3J0b3IsIHV0IGZyaW5naWxsYSBuaWJoIHNhcGllbiB2aXRhZSBzYXBpZW4uIENsYXNzIGFwdGVudCB0YWNpdGkgc29jaW9zcXUgYWQgbGl0b3JhIHRvcnF1ZW50IHBlciBjb251YmlhIG5vc3RyYSwgcGVyIGluY2VwdG9zIGhpbWVuYWVvcy4=", "timezone_offset": 7200, "current": { "dt": 1650206004, @@ -293,7 +294,8 @@ "main": "Clouds", "description": "overcast clouds", "icon": "04d" - },{ + }, + { "id": 804, "main": "Clouds", "description": "overcast clouds", @@ -988,7 +990,8 @@ "main": "Clouds", "description": "overcast clouds", "icon": "04n" - },{ + }, + { "id": 804, "main": "Clouds", "description": "overcast clouds", @@ -1016,7 +1019,8 @@ "main": "Clouds", "description": "overcast clouds", "icon": "04n" - },{ + }, + { "id": 804, "main": "Clouds", "description": "overcast clouds", @@ -1044,7 +1048,8 @@ "main": "Clouds", "description": "overcast clouds", "icon": "04n" - },{ + }, + { "id": 804, "main": "Clouds", "description": "overcast clouds", @@ -1072,7 +1077,8 @@ "main": "Clouds", "description": "overcast clouds", "icon": "04n" - },{ + }, + { "id": 804, "main": "Clouds", "description": "overcast clouds", @@ -1100,7 +1106,8 @@ "main": "Clouds", "description": "overcast clouds", "icon": "04n" - },{ + }, + { "id": 804, "main": "Clouds", "description": "overcast clouds", @@ -1128,7 +1135,8 @@ "main": "Clouds", "description": "broken clouds", "icon": "04n" - },{ + }, + { "id": 804, "main": "Clouds", "description": "overcast clouds", @@ -1156,7 +1164,8 @@ "main": "Clouds", "description": "broken clouds", "icon": "04n" - },{ + }, + { "id": 804, "main": "Clouds", "description": "overcast clouds", @@ -1184,7 +1193,8 @@ "main": "Clouds", "description": "overcast clouds", "icon": "04n" - },{ + }, + { "id": 804, "main": "Clouds", "description": "overcast clouds", @@ -1212,7 +1222,8 @@ "main": "Clouds", "description": "overcast clouds", "icon": "04n" - },{ + }, + { "id": 804, "main": "Clouds", "description": "overcast clouds", @@ -1240,7 +1251,8 @@ "main": "Clouds", "description": "overcast clouds", "icon": "04d" - },{ + }, + { "id": 804, "main": "Clouds", "description": "overcast clouds", @@ -1468,7 +1480,8 @@ "main": "Clouds", "description": "scattered clouds", "icon": "03d" - },{ + }, + { "id": 804, "main": "Clouds", "description": "overcast clouds", @@ -1512,7 +1525,8 @@ "main": "Clouds", "description": "scattered clouds", "icon": "03d" - },{ + }, + { "id": 804, "main": "Clouds", "description": "overcast clouds", @@ -1556,7 +1570,8 @@ "main": "Clouds", "description": "overcast clouds", "icon": "04d" - },{ + }, + { "id": 804, "main": "Clouds", "description": "overcast clouds", @@ -1600,7 +1615,8 @@ "main": "Rain", "description": "moderate rain", "icon": "10d" - },{ + }, + { "id": 804, "main": "Clouds", "description": "overcast clouds", @@ -1645,7 +1661,8 @@ "main": "Clouds", "description": "overcast clouds", "icon": "04d" - },{ + }, + { "id": 804, "main": "Clouds", "description": "overcast clouds", @@ -1689,7 +1706,8 @@ "main": "Rain", "description": "moderate rain", "icon": "10d" - },{ + }, + { "id": 804, "main": "Clouds", "description": "overcast clouds", @@ -1734,7 +1752,8 @@ "main": "Rain", "description": "light rain", "icon": "10d" - },{ + }, + { "id": 804, "main": "Clouds", "description": "overcast clouds", @@ -1779,7 +1798,8 @@ "main": "Rain", "description": "light rain", "icon": "10d" - },{ + }, + { "id": 804, "main": "Clouds", "description": "overcast clouds", From c61a7bd51a8cc7709d00b13014886da019d29cce Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Wed, 26 Oct 2022 22:03:50 +0200 Subject: [PATCH 70/77] Add reset and reeturn code for end of parsing --- CHANGELOG.md | 2 ++ examples/example_stream.c | 11 ++++++++--- lwjson/src/include/lwjson/lwjson.h | 16 +++++++--------- lwjson/src/lwjson/lwjson_stream.c | 21 ++++++++++++++++++++- 4 files changed, 37 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce99c92..23dd1eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ - Update code style with astyle - Add support for stream parsing - first version - Add `.clang-format` draft +- Add `lwjsonSTREAMDONE` return code when streamer well parsed some JSON and reached end of string +- Add option to reset stream state machine ## 1.5.0 diff --git a/examples/example_stream.c b/examples/example_stream.c index a01686f..66cda22 100644 --- a/examples/example_stream.c +++ b/examples/example_stream.c @@ -27,14 +27,19 @@ prv_example_callback_func(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type /* Parse JSON */ void example_stream_run(void) { + lwjsonr_t res; printf("\r\n\r\nParsing stream\r\n"); lwjson_stream_init(&stream_parser, prv_example_callback_func); /* Demonstrate as stream inputs */ for (const char* c = json_str; *c != '\0'; ++c) { - if (lwjson_stream_parse(&stream_parser, *c) != lwjsonOK) { - printf("ERROR\r\n"); - return; + if ((res = lwjson_stream_parse(&stream_parser, *c)) == lwjsonOK) { + printf("OK\r\n"); + } else if (res == lwjsonSTREAMDONE) { + printf("Done\r\n"); + } else { + printf("Error\r\n"); + break; } } printf("Parsing completed\r\n"); diff --git a/lwjson/src/include/lwjson/lwjson.h b/lwjson/src/include/lwjson/lwjson.h index 5faea42..c5c35bb 100644 --- a/lwjson/src/include/lwjson/lwjson.h +++ b/lwjson/src/include/lwjson/lwjson.h @@ -105,15 +105,12 @@ typedef struct lwjson_token { * \brief JSON result enumeration */ typedef enum { - lwjsonOK = 0x00, /*!< Function returns successfully */ - lwjsonERR, /*!< Generic error message */ - lwjsonERRJSON, /*!< Error JSON format */ - lwjsonERRMEM, /*!< Memory error */ - lwjsonERRPAR, /*!< Parameter error */ - - lwjsonSTREAMNONE, /*!< No new info to process - parsing in progress */ - lwjsonSTREAMINPROGRESS, /*!< Stream token parsing is in progress */ - lwjsonSTREAMDONE, /*!< Streaming parser is done */ + lwjsonOK = 0x00, /*!< Function returns successfully */ + lwjsonERR, /*!< Generic error message */ + lwjsonERRJSON, /*!< Error JSON format */ + lwjsonERRMEM, /*!< Memory error */ + lwjsonERRPAR, /*!< Parameter error */ + lwjsonSTREAMDONE, /*!< Streaming parser is done */ } lwjsonr_t; /** @@ -221,6 +218,7 @@ typedef struct lwjson_stream_parser { } lwjson_stream_parser_t; lwjsonr_t lwjson_stream_init(lwjson_stream_parser_t* jsp, lwjson_stream_parser_callback_fn evt_fn); +lwjsonr_t lwjson_stream_reset(lwjson_stream_parser_t* jsp); lwjsonr_t lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c); /** diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index a5f76af..35ab40a 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -141,11 +141,25 @@ lwjson_stream_init(lwjson_stream_parser_t* jsp, lwjson_stream_parser_callback_fn return lwjsonOK; } +/** + * \brief Reset LwJSON stream structure + * + * \param jsp: LwJSON stream parser + * \return \ref lwjsonOK on success, member of \ref lwjsonr_t otherwise + */ +lwjsonr_t +lwjson_stream_reset(lwjson_stream_parser_t* jsp) { + jsp->parse_state = LWJSON_STREAM_STATE_WAITINGFIRSTCHAR; + jsp->stack_pos = 0; + return lwjsonOK; +} + /** * \brief Parse JSON string in streaming mode * \param[in,out] jsp: Stream JSON structure * \param[in] c: Character to parse - * \return lwjsonr_t + * \return \ref lwjsonOK if parsing is in progress and no hard error detected + * \ref lwjsonSTREAMDONE when valid JSON was detected and stack level reached back `0` level */ lwjsonr_t lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { @@ -218,6 +232,11 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { } SEND_EVT(jsp, c == '}' ? LWJSON_STREAM_TYPE_OBJECT_END : LWJSON_STREAM_TYPE_ARRAY_END); + /* If that is the end of JSON */ + if (jsp->stack_pos == 0) { + return lwjsonSTREAMDONE; + } + /* Determine start of string - can be key or regular string (in array or after key) */ } else if (c == '"') { #if defined(LWJSON_DEV) From ae7b4b6551d8e05d9aeb572a1802226e206b4290 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Fri, 28 Oct 2022 07:38:04 +0200 Subject: [PATCH 71/77] Add check for primitive load --- lwjson/src/lwjson/lwjson_stream.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index 35ab40a..28d693c 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -364,7 +364,9 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { case LWJSON_STREAM_STATE_PARSING_PRIMITIVE: { /* Any character except space, comma, or end of array/object are valid */ if (!prv_is_space_char_ext(c) && c != ',' && c != ']' && c != '}') { - jsp->data.prim.buff[jsp->data.prim.buff_pos++] = c; + if (jsp->data.prim.buff_pos < sizeof(jsp->data.prim.buff) - 1) { + jsp->data.prim.buff[jsp->data.prim.buff_pos++] = c; + } } else { lwjson_stream_type_t t = prv_stack_get_top(jsp); From 527db650bd7e91aab43bbd57863dc645dc84fedc Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sat, 29 Oct 2022 00:12:20 +0200 Subject: [PATCH 72/77] Add static keyword for private functions --- lwjson/src/lwjson/lwjson_stream.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index 28d693c..ff9d4e3 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -82,7 +82,7 @@ static const char* type_strings[] = { * \param type: Stream type to be pushed on stack * \return `1` on success, `0` otherwise */ -uint8_t +static uint8_t prv_stack_push(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { if (jsp->stack_pos < LWJSON_ARRAYSIZE(jsp->stack)) { jsp->stack[jsp->stack_pos].type = type; @@ -99,7 +99,7 @@ prv_stack_push(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { * \param jsp: JSON stream parser instance * \return Member of \ref lwjson_stream_type_t enumeration */ -lwjson_stream_type_t +static lwjson_stream_type_t prv_stack_pop(lwjson_stream_parser_t* jsp) { if (jsp->stack_pos > 0) { lwjson_stream_type_t t = jsp->stack[--jsp->stack_pos].type; @@ -120,7 +120,7 @@ prv_stack_pop(lwjson_stream_parser_t* jsp) { * \param jsp: JSON stream parser instance * \return Member of \ref lwjson_stream_type_t enumeration */ -lwjson_stream_type_t +static lwjson_stream_type_t prv_stack_get_top(lwjson_stream_parser_t* jsp) { if (jsp->stack_pos > 0) { return jsp->stack[jsp->stack_pos - 1].type; From 1a0c0536e317cb507937cc5f00532f1ea1006078 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sun, 30 Oct 2022 18:24:02 +0100 Subject: [PATCH 73/77] Update docs --- docs/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.rst b/docs/index.rst index 91c286f..8f14ebf 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -79,6 +79,7 @@ Table of contents :caption: Other projects :hidden: + LwBTN - Button manager LwDTC - DateTimeCron LwESP - ESP-AT library LwEVT - Event manager From a232f8774cf05da52e9f9eac21ff858349a745f4 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Mon, 31 Oct 2022 10:24:55 +0100 Subject: [PATCH 74/77] Update vscode --- .vscode/launch.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 56ffea9..c76ad22 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,10 +1,8 @@ { - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { + /* GDB must in be in the PATH environment */ "name": "(Windows) Launch", "type": "cppdbg", "request": "launch", From d61c3577378745d9ca7a9e376b73ef406e82a51a Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Mon, 31 Oct 2022 11:03:11 +0100 Subject: [PATCH 75/77] Streaming parser now returns streaming error codes --- lwjson/src/include/lwjson/lwjson.h | 21 ++++++++++++++------- lwjson/src/lwjson/lwjson_stream.c | 8 ++++++-- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/lwjson/src/include/lwjson/lwjson.h b/lwjson/src/include/lwjson/lwjson.h index c5c35bb..dc7fd4c 100644 --- a/lwjson/src/include/lwjson/lwjson.h +++ b/lwjson/src/include/lwjson/lwjson.h @@ -105,13 +105,20 @@ typedef struct lwjson_token { * \brief JSON result enumeration */ typedef enum { - lwjsonOK = 0x00, /*!< Function returns successfully */ - lwjsonERR, /*!< Generic error message */ - lwjsonERRJSON, /*!< Error JSON format */ - lwjsonERRMEM, /*!< Memory error */ - lwjsonERRPAR, /*!< Parameter error */ - lwjsonSTREAMDONE, /*!< Streaming parser is done */ -} lwjsonr_t; + lwjsonOK = 0x00, /*!< Function returns successfully */ + lwjsonERR, /*!< Generic error message */ + lwjsonERRJSON, /*!< Error JSON format */ + lwjsonERRMEM, /*!< Memory error */ + lwjsonERRPAR, /*!< Parameter error */ + + lwjsonSTREAMWAITFIRSTCHAR, /*!< Streaming parser did not yet receive first valid character + indicating start of JSON sequence */ + lwjsonSTREAMDONE, /*!< Streaming parser is done, + closing character matched the stream opening one */ + lwjsonSTREAMINPROG, /*!< Stream parsing is still in progress */ +} + +lwjsonr_t; /** * \brief LwJSON instance diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index ff9d4e3..05a2f4e 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -64,6 +64,10 @@ static const char* type_strings[] = { #define LWJSON_DEBUG(jsp, ...) #endif /* defined(LWJSON_DEV) */ +/** + * \brief Sends an event to user for further processing + * + */ #define SEND_EVT(jsp, type) \ if ((jsp) != NULL && (jsp)->evt_fn != NULL) { \ (jsp)->evt_fn((jsp), (type)); \ @@ -165,7 +169,7 @@ lwjsonr_t lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { /* Get first character first */ if (jsp->parse_state == LWJSON_STREAM_STATE_WAITINGFIRSTCHAR && c != '{' && c != '[') { - return lwjsonOK; + return lwjsonSTREAMDONE; } start_over: @@ -428,5 +432,5 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { break; } jsp->prev_c = c; /* Save current c as previous for next round */ - return lwjsonOK; + return lwjsonSTREAMINPROG; } From d652d49b0c6949ffcdf499feb1eb8cdf8415720a Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Mon, 31 Oct 2022 11:03:26 +0100 Subject: [PATCH 76/77] Improve docs for stream --- dev/main.c | 12 ++++++++++-- docs/examples/stream.json | 13 +++++++++++++ docs/user-manual/stream.rst | 16 ++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 docs/examples/stream.json diff --git a/dev/main.c b/dev/main.c index 4301c8b..e293e6e 100644 --- a/dev/main.c +++ b/dev/main.c @@ -39,7 +39,7 @@ main() { /* Init JSON */ lwjson_init(&lwjson, tokens, LWJSON_ARRAYSIZE(tokens)); - f = CreateFile(TEXT("test\\json\\weather_onecall.json"), + f = CreateFile(TEXT("test\\json\\custom_stream.json"), GENERIC_READ, // open for reading 0, // do not share NULL, // no security @@ -70,7 +70,15 @@ main() { /* Now parse as a stream */ lwjson_stream_init(&stream_parser, jsp_stream_callback); for (const char* str = json_text; str != NULL && *str != '\0'; ++str) { - if (lwjson_stream_parse(&stream_parser, *str) != lwjsonOK) { + lwjsonr_t res = lwjson_stream_parse(&stream_parser, *str); + if (res == lwjsonSTREAMWAITFIRSTCHAR) { + printf("Waiting valid JSON start\r\n"); + } else if (res == lwjsonSTREAMINPROG) { + //printf("Stream in progress...\r\n"); + } else if (res == lwjsonSTREAMDONE) { + printf("Stream is completed\r\n"); + } else { + printf("Unknown error...\r\n"); break; } } diff --git a/docs/examples/stream.json b/docs/examples/stream.json new file mode 100644 index 0000000..fe4f850 --- /dev/null +++ b/docs/examples/stream.json @@ -0,0 +1,13 @@ +{ + "test": "abc", + "array": [ + "123", + "def", + "ghi" + ], + "array_in_array": [ + ["1", "2", "3"], + ["4", "5", "6"], + ["7", "8", "9"] + ] +} \ No newline at end of file diff --git a/docs/user-manual/stream.rst b/docs/user-manual/stream.rst index c08f421..9789370 100644 --- a/docs/user-manual/stream.rst +++ b/docs/user-manual/stream.rst @@ -50,5 +50,21 @@ An example of the stream parsing: :linenos: :caption: Parse JSON data as a stream object +Example +******* + +For the purpose of example, the following JSON input... + +.. literalinclude:: ../examples/stream.json + :language: json + :linenos: + :caption: JSON input for streaming + +\... will output the log as: + +.. literalinclude:: ../examples/stream_log.txt + :linenos: + :caption: JSON development log for the various events + .. toctree:: :maxdepth: 2 \ No newline at end of file From a2939a93814792a2b740adb8bfbc1821deb48fe2 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Mon, 12 Dec 2022 20:46:10 +0100 Subject: [PATCH 77/77] Add version 1.6 --- CHANGELOG.md | 4 +- dev/lwjson_opts.h | 2 +- library.json | 64 +++++++++---------- lwjson/src/include/lwjson/lwjson.h | 2 +- lwjson/src/include/lwjson/lwjson_opt.h | 2 +- .../src/include/lwjson/lwjson_opts_template.h | 2 +- lwjson/src/lwjson/lwjson.c | 2 +- lwjson/src/lwjson/lwjson_debug.c | 2 +- lwjson/src/lwjson/lwjson_stream.c | 2 +- 9 files changed, 41 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23dd1eb..ec0f1b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,12 +2,14 @@ ## Develop +## 1.6.0 + - Split CMakeLists.txt files between library and executable - Change license year to 2022 - Fix GCC warning for incompatible comparison types - Update code style with astyle - Add support for stream parsing - first version -- Add `.clang-format` draft +- Add `.clang-format` - Add `lwjsonSTREAMDONE` return code when streamer well parsed some JSON and reached end of string - Add option to reset stream state machine diff --git a/dev/lwjson_opts.h b/dev/lwjson_opts.h index a23c9fa..80fc50f 100644 --- a/dev/lwjson_opts.h +++ b/dev/lwjson_opts.h @@ -29,7 +29,7 @@ * This file is part of LwJSON - Lightweight JSON format parser. * * Author: Tilen MAJERLE - * Version: v1.5.0 + * Version: v1.6.0 */ #ifndef LWJSON_HDR_OPTS_H #define LWJSON_HDR_OPTS_H diff --git a/library.json b/library.json index 5bf7fdf..78145e7 100644 --- a/library.json +++ b/library.json @@ -1,35 +1,33 @@ { - "name": "LwJSON", - "version": "1.5.0", - "description": "Lightweight JSON parser for embedded systems with support for inline comments", - "keywords": "json, javascript, lightweight, parser, stm32, manager, library, comment, object, notation, object notation", - "repository": { - "type": "git", - "url": "https://github.com/MaJerle/lwjson.git" - }, - "authors": [ - { - "name": "Tilen Majerle", - "email": "tilen@majerle.eu", - "url": "https://majerle.eu" - } - ], - "license": "MIT", - "homepage": "https://github.com/MaJerle/lwjson", - "dependencies": { - - }, - "frameworks": "*", - "platforms": "*", - "export": { - "exclude": [ - ".github", - "dev", - "docs", - "**/.vs", - "**/Debug", - "build", - "**/build" - ] - } + "name": "LwJSON", + "version": "1.6.0", + "description": "Lightweight JSON parser for embedded systems with support for inline comments", + "keywords": "json, javascript, lightweight, parser, stm32, manager, library, comment, object, notation, object notation", + "repository": { + "type": "git", + "url": "https://github.com/MaJerle/lwjson.git" + }, + "authors": [ + { + "name": "Tilen Majerle", + "email": "tilen@majerle.eu", + "url": "https://majerle.eu" + } + ], + "license": "MIT", + "homepage": "https://github.com/MaJerle/lwjson", + "dependencies": {}, + "frameworks": "*", + "platforms": "*", + "export": { + "exclude": [ + ".github", + "dev", + "docs", + "**/.vs", + "**/Debug", + "build", + "**/build" + ] + } } \ No newline at end of file diff --git a/lwjson/src/include/lwjson/lwjson.h b/lwjson/src/include/lwjson/lwjson.h index dc7fd4c..1f7202f 100644 --- a/lwjson/src/include/lwjson/lwjson.h +++ b/lwjson/src/include/lwjson/lwjson.h @@ -29,7 +29,7 @@ * This file is part of LwJSON - Lightweight JSON format parser. * * Author: Tilen MAJERLE - * Version: v1.5.0 + * Version: v1.6.0 */ #ifndef LWJSON_HDR_H #define LWJSON_HDR_H diff --git a/lwjson/src/include/lwjson/lwjson_opt.h b/lwjson/src/include/lwjson/lwjson_opt.h index ebb8e6c..23c23ab 100644 --- a/lwjson/src/include/lwjson/lwjson_opt.h +++ b/lwjson/src/include/lwjson/lwjson_opt.h @@ -29,7 +29,7 @@ * This file is part of LwJSON - Lightweight JSON format parser. * * Author: Tilen MAJERLE - * Version: v1.5.0 + * Version: v1.6.0 */ #ifndef LWJSON_HDR_OPT_H #define LWJSON_HDR_OPT_H diff --git a/lwjson/src/include/lwjson/lwjson_opts_template.h b/lwjson/src/include/lwjson/lwjson_opts_template.h index 6db700c..50b181e 100644 --- a/lwjson/src/include/lwjson/lwjson_opts_template.h +++ b/lwjson/src/include/lwjson/lwjson_opts_template.h @@ -29,7 +29,7 @@ * This file is part of LwJSON - Lightweight JSON format parser. * * Author: Tilen MAJERLE - * Version: v1.5.0 + * Version: v1.6.0 */ #ifndef LWJSON_HDR_OPTS_H #define LWJSON_HDR_OPTS_H diff --git a/lwjson/src/lwjson/lwjson.c b/lwjson/src/lwjson/lwjson.c index ffde382..b8a1482 100644 --- a/lwjson/src/lwjson/lwjson.c +++ b/lwjson/src/lwjson/lwjson.c @@ -29,7 +29,7 @@ * This file is part of LwJSON - Lightweight JSON format parser. * * Author: Tilen MAJERLE - * Version: v1.5.0 + * Version: v1.6.0 */ #include #include "lwjson/lwjson.h" diff --git a/lwjson/src/lwjson/lwjson_debug.c b/lwjson/src/lwjson/lwjson_debug.c index 252bd71..1ac9ada 100644 --- a/lwjson/src/lwjson/lwjson_debug.c +++ b/lwjson/src/lwjson/lwjson_debug.c @@ -29,7 +29,7 @@ * This file is part of LwJSON - Lightweight JSON format parser. * * Author: Tilen MAJERLE - * Version: v1.5.0 + * Version: v1.6.0 */ #include #include diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index 05a2f4e..54f08c5 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -29,7 +29,7 @@ * This file is part of LwJSON - Lightweight JSON format parser. * * Author: Tilen MAJERLE - * Version: v1.5.0 + * Version: v1.6.0 */ #include #include "lwjson/lwjson.h"